You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ARM Cortex-M4裸机C项目链接C++库时未定义符号问题

ARM Cortex-M4裸机项目链接C++库的系统调用未定义引用问题分析

问题背景

为ARM Cortex-M4交叉编译外部C++库/可重链接目标文件时,使用参数:

-fno-unwind-tables
-ffunction-sections
-fdata-sections
-fmessage-length=0
-std=c++11
-fno-rtti
-fno-exceptions
-fno-threadsafe-statics
-Wnon-virtual-dtor
-fno-unwind-tables
-ffreestanding
-fno-non-call-exceptions
-fno-use-cxa-atexit
-fno-common
-r

编译C++包装文件时追加-C参数,随后用以下参数链接库与包装文件:

-static -r -nostartfiles -nolibc -nodefaultlibs -nostdlib

将该产物链接到独立裸机C项目时,出现一系列未定义引用错误:

../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc.a(lib_a-abort.o): in function `abort':
abort.c:(.text.abort+0xa): undefined reference to `_exit'

../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc.a(lib_a-signalr.o): in function `_kill_r':
signalr.c:(.text._kill_r+0x12): undefined reference to `_kill'

../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc.a(lib_a-signalr.o): in function `_getpid_r':
signalr.c:(.text._getpid_r+0x0): undefined reference to `_getpid'

../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc.a(lib_a-writer.o): in function `_write_r':
writer.c:(.text._write_r+0x14): undefined reference to `_write'

../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc.a(lib_a-closer.o): in function `_close_r':
closer.c:(.text._close_r+0xc): undefined reference to `_close'

../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc.a(lib_a-fstatr.o): in function `_fstat_r':
fstatr.c:(.text._fstat_r+0x12): undefined reference to `_fstat'

../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc.a(lib_a-isattyr.o): in function `_isatty_r':
isattyr.c:(.text._isatty_r+0xc): undefined reference to `_isatty'

../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc.a(lib_a-lseekr.o): in function `_lseek_r':
lseekr.c:(.text._lseek_r+0x14): undefined reference to `_lseek'

../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc.a(lib_a-readr.o): in function `_read_r':
readr.c:(.text._read_r+0x14): undefined reference to `_read'

添加--specs=nosys.specs或-lnosys可解决问题,但需明确根源,避免盲目依赖。

问题根源

这些未定义符号是newlib(ARM交叉编译工具链的libc)可重入标准库函数依赖的底层系统调用桩。

  • 当最终链接裸机项目时,链接器会解析所有依赖:如果C++库/包装代码中隐式调用了newlib的可重入函数(如abort、可重入版IO函数),链接器会从libc.a中提取对应的目标文件(如lib_a-abort.o、lib_a-signalr.o)。
  • 这些newlib内部函数依赖_exit、_kill等底层系统调用——裸机环境没有操作系统提供这些实现,因此触发未定义引用错误。

为什么单独查看C++产物看不到这些符号

你用-r参数生成的是可重链接目标文件(relocatable object),此时链接器仅完成部分符号解析,并未将libc.a中的目标文件合并进来。只有在最终链接裸机项目时,链接器才会根据依赖关系从libc.a中提取所需的.o文件,此时这些依赖的系统调用符号才会暴露出来。因此单独查看C++产物时,无法看到这些来自newlib的符号。

非依赖nosys的解决思路

1. 排查隐式的标准库依赖

使用nm -u <你的C++可重链接文件>查看未定义符号,定位是否有代码触发了newlib函数的引用(如abort、printf等)。比如:

  • C++代码中是否有assert断言失败逻辑,会隐式调用abort;
  • 是否不小心使用了标准IO、信号相关的功能;
  • 内存分配失败时,某些编译器会调用abort。

2. 实现最小化系统调用桩

不需要依赖nosys,自己实现这些缺失的底层符号,适配裸机环境:

// syscall_stubs.c
void _exit(int status) {
    while (1); // 裸机无操作系统,死循环即可
}

int _kill(int pid, int sig) {
    return -1; // 不支持信号机制,返回错误
}

int _getpid(void) {
    return 1; // 裸机环境固定进程ID为1
}

ssize_t _write(int fd, const void *buf, size_t count) {
    // 如果需要串口输出,可在这里实现UART发送逻辑
    // 不需要的话直接返回count表示成功
    return count;
}

int _close(int fd) {
    return 0; // 空实现,返回成功
}

int _fstat(int fd, void *buf) {
    return 0; // 空实现
}

int _isatty(int fd) {
    return 1; // 默认所有fd都是终端(适配裸机场景)
}

off_t _lseek(int fd, off_t offset, int whence) {
    return offset; // 空实现,返回偏移量
}

ssize_t _read(int fd, void *buf, size_t count) {
    return 0; // 无输入设备,返回0
}

将该文件编译为目标文件后,加入到项目链接列表中,即可替代nosys的作用。

3. 优化C++编译参数

追加-fno-builtin参数,禁止编译器生成内置标准库函数调用,减少不必要的依赖;同时确保C++代码完全脱离操作系统相关的标准库功能。

内容的提问来源于stack exchange,提问作者gstukelj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 05:24:54