为什么我在__libc_init_array中有_init的未定义引用?

时间:2012-12-05 23:56:30

标签: gcc linker yagarto

我正在尝试使用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

为什么我收到此“未定义参考”错误?我错过了什么?我假设有一些我缺少的链接器标志,但我不能为我的生活弄清楚是什么。

3 个答案:

答案 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_arrayinit_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.ocrtn.o,它们只为 _init_fini 提供了一些空存根。是否在所有其他 stdlib 对象中搜索 arm-none-eabi,但没有发现其他对象会将部分附加到 .init 中,这无论如何都会过时。下面是对 crti.ocrtn.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.ocrtn .o。存根可能如下所示:

extern "C" void _init(void) {;}

特殊函数 _init_fini 是一些历史遗留物,用于控制构造函数和析构函数。但是,它们已经过时,使用它们会导致不可预测的结果。现代库不应该再使用这些,而是​​使用 GCC 函数属性 constructordestructor 来向 .preinit_array< 中的那些表添加方法/em>、.init_array.fini_array 部分。

如果知道有一些初始化代码被发送到 .init(即使这在今天已经过时),那么应该提供一个 _init(void) 函数,这将通过调用 .init 部分的起始地址来运行此初始化代码。