我正在尝试使用Yagarto和Eclipse为ARM微控制器平台构建一个简单的项目。在我的启动代码中,我有这个(我认为这是相当标准和无趣的):
void Reset_Handler(void)
{
/* Initialize data and bss */
__Init_Data();
/* Call CTORS of static objects */
__libc_init_array();
/* Call the application's entry point.*/
main();
while(1) { ; }
}
除非我注释掉对__libc_init_array()
的调用,否则我会从链接器中收到以下错误:
arm-none-eabi-g++ -nostartfiles -mthumb -mcpu=cortex-m4 -TC:/Users/mark/workspace/stm32_cpp_test/STM32F40x_1024k_192k_flash.ld -gc-sections -Wl,-Map=test_rom.map,--cref,--no-warn-mismatch -o stm32_cpp_test "system\\syscalls.o" "system\\startup_stm32f4xx.o" "system\\mini_cpp.o" "system\\cmsis\\system_stm32f4xx.o" main.o
d:/utils/yagarto/bin/../lib/gcc/arm-none-eabi/4.7.2/../../../../arm-none-eabi/lib/thumb/v7m\libg.a(lib_a-init.o): In function `__libc_init_array':
C:\msys\1.0\home\yagarto\newlib-build\arm-none-eabi\thumb\v7m\newlib\libc\misc/../../../../../../../newlib-1.20.0/newlib/libc/misc/init.c:37: undefined reference to `_init'
collect2.exe: error: ld returned 1 exit status
为什么我收到此“未定义参考”错误?我错过了什么?我假设有一些我缺少的链接器标志,但我不能为我的生活弄清楚是什么。
答案 0 :(得分:5)
我不是专家,但是:
可能_init(正常运行时入口点)引用执行ctor和dtor表的代码。
您使用-nostartfiles以避免标准启动,并且可能整个启动代码被--gc-sections消除。显式调用再次添加引用。
如果省略--gc-sections没有解决它,它也可能是(嵌入式)链接描述文件中缺少keep()语句,它始终保留条目代码,或者您自己的启动代码 (startup_ *)应该引用它
答案 1 :(得分:5)
老问题,但我遇到了类似的问题,解决方案就像Marco van de Voort所说,如果你要使用__libc_init_array
,你应该省略-nostartfiles
链接器选项,包括正常的libc init函数。答案重复。
其次我建议在与gcc-arm链接时包含--specs=nano.specs
标志(我相信yargarto是一个分支,甚至只是gcc-arm的预编译),因为它减少了libc等代码消耗。
答案 2 :(得分:0)
stdlib 中的 __libc_init_array 函数负责调用所有注册到 preinit_array 和 init_array 的初始化程序或 C++ 构造函数。在 preinit 和 init 之间,它调用一个 extern _init 函数。代码看起来很简单:
#include <sys/types.h>
/* These magic symbols are provided by the linker. */
extern void (*__preinit_array_start []) (void) __attribute__((weak));
extern void (*__preinit_array_end []) (void) __attribute__((weak));
extern void (*__init_array_start []) (void) __attribute__((weak));
extern void (*__init_array_end []) (void) __attribute__((weak));
extern void _init (void);
void __libc_init_array (void)
{
size_t count;
size_t i;
count = __preinit_array_end - __preinit_array_start;
for (i = 0; i < count; i++)
__preinit_array_start[i] ();
_init ();
count = __init_array_end - __init_array_start;
for (i = 0; i < count; i++)
__init_array_start[i] ();
}
另见:understanding the __libc_init_array。
如果实现了自定义启动代码,则需要通过链接到“init.o”或通过实现类似于上述代码段的内容来执行此初始化。
如果至少为具有 newlib-nano 规范的 arm-none-eabi ARMv7e 目标构建,则 _init 方法从crti.o 和 crtn.o,它们只为 _init 和 _fini 提供了一些空存根。是否在所有其他 stdlib 对象中搜索 arm-none-eabi,但没有发现其他对象会将部分附加到 .init 中,这无论如何都会过时。下面是对 crti.o 和 crtn.o 的一些反汇编:
$ ./bin/arm-none-eabi-objdump.exe -j .init -D ./lib/gcc/arm-none-eabi/10.2.1/thumb/v7/nofp/crt?.o
./lib/gcc/arm-none-eabi/10.2.1/thumb/v7/nofp/crti.o: file format elf32-littlearm
Disassembly of section .init:
00000000 <_init>:
0: b5f8 push {r3, r4, r5, r6, r7, lr}
2: bf00 nop
./lib/gcc/arm-none-eabi/10.2.1/thumb/v7/nofp/crtn.o: file format elf32-littlearm
Disassembly of section .init:
00000000 <.init>:
0: bcf8 pop {r3, r4, r5, r6, r7}
2: bc08 pop {r3}
4: 469e mov lr, r3
6: 4770 bx lr
如果有人想要将 __libc_init_array 与链接器选项 nostartfiles 结合用于此特定 ARM 目标,则可以提供自己的 _init > 存根方法,让链接器通过,只要没有其他初始化代码被发送到 .init 部分,除了这个来自 crti.o 和 crtn .o。存根可能如下所示:
extern "C" void _init(void) {;}
特殊函数 _init 和 _fini 是一些历史遗留物,用于控制构造函数和析构函数。但是,它们已经过时,使用它们会导致不可预测的结果。现代库不应该再使用这些,而是使用 GCC 函数属性 constructor 和 destructor 来向 .preinit_array< 中的那些表添加方法/em>、.init_array 和 .fini_array 部分。
如果知道有一些初始化代码被发送到 .init(即使这在今天已经过时),那么应该提供一个 _init(void) 函数,这将通过调用 .init 部分的起始地址来运行此初始化代码。