我正在为 ARM 微控制器编写固件。我已经构建了没有系统调用的交叉编译器,实际上我的 RTOS (ChibiOS) 提供了一个简单的系统调用实现。
我的所有代码都是用 C 编写的,除了某些部分我只使用 C++ 与 Eigen 库(用于线性代数的 C++ 模板库,它只是标头)链接。
如果我使用 -O2 -DNDEBUG 编译源示例代码(据我所知,使用 NDEBUG 代码不需要断言()),一切都会编译正常并且固件可以正常工作。
如果我使用 -O0 编译源示例代码,我将得到以下内容:
Linking build/ch.elf
/home/noether/workspace/tool-chains/arm-none-eabi-4.6.2/lib/gcc/arm-none-eabi/4.6.2 /../../../../arm-none-eabi/lib/thumb/cortex-m4/libc.a(lib_a-abort.o): In function `abort':
/home/noether/workspace/tool-chains/summon-arm-toolchain/build/arm-none-eabi/thumb /cortex-m4/newlib/libc/stdlib/../../../../../../../gcc-4.6.2/newlib/libc/stdlib /abort.c:63: undefined reference to `_exit'
/home/noether/workspace/tool-chains/arm-none-eabi-4.6.2/lib/gcc/arm-none-eabi/4.6.2/../../../../arm-none-eabi/lib/thumb/cortex-m4/libc.a(lib_a-signalr.o): In function `_kill_r':
/home/noether/workspace/tool-chains/summon-arm-toolchain/build/arm-none-eabi/thumb/cortex-m4/newlib/libc/reent/../../../../../../../gcc-4.6.2/newlib/libc/reent/signalr.c:61: undefined reference to `_kill'
/home/noether/workspace/tool-chains/arm-none-eabi-4.6.2/lib/gcc/arm-none-eabi/4.6.2/../../../../arm-none-eabi/lib/thumb/cortex-m4/libc.a(lib_a-signalr.o): In function `_getpid_r':
/home/noether/workspace/tool-chains/summon-arm-toolchain/build/arm-none-eabi/thumb/cortex-m4/newlib/libc/reent/../../../../../../../gcc-4.6.2/newlib/libc/reent/signalr.c:96: undefined reference to `_getpid'
collect2: ld returned 1 exit status
make: *** [build/ch.elf] Error 1
如果我输入 -DNDEBUG 也没关系,我有相同的输出。 我也在使用这些标志,-fno-exceptions 和 fno-rtti。 如果我不使用/链接 Eigen 库(唯一的 C++ 东西),即使使用 -O0,g++ 也能很好地编译源代码。
其实我实现了一个简单的 _kill _getpid 和 _exit 函数,代码编译通过了,但是代码从 13KB 变成了 130KB,并且崩溃了(可能是我没有写好这些函数)。
如果我使用 -O0,我想要的是从我的代码中删除这些东西(中止等),因为它是用 -O2 完成的(我猜)。
非常感谢。如果您需要更多信息,请告诉我。
这些参考文献几乎肯定是由于使用了
assert()
;失败时将调用 abort()
,进而尝试发出信号(在此实现中使用 kill()
系统调用)来中止进程。显然,如果您无法进行系统调用,这是不可能的。
使用
-DNDEBUG
进行构建(就像在优化版本中所做的那样)将解决此问题;它会导致 assert()
宏不生成任何代码,因此不会有对 abort()
的引用。优化级别本身应该不会产生任何影响。
或者,如果您想保留断言,您可以实现自己的
assert()
宏,不需要任何系统调用。
您可以简单地为那些缺少的系统调用实现存根并将它们链接到您的代码。 这将使链接器保持安静,但您可能会让存根做一些明智或有用的事情。 exit() 可能会禁用中断并无限循环或强制重置。 _kill() 可能会挂接到您的 RTOS 中以终止线程,或者简单地调用 _exit(),并且 _getpid() 可能会返回一些虚拟值或 RTOS 线程 ID。