10

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

为什么我会收到此“未定义的引用”错误?我错过了什么?我认为我缺少一些链接器标志,但我一生都无法弄清楚是什么。

4

3 回答 3

6

老问题,但我遇到了类似的问题,解决方案正如 Marco van de Voort 指出的那样,如果你要使用__libc_init_array你应该省略-nostartfiles链接器选项,以包含正常的 libc 初始化函数。重复答案。

其次,我建议--specs=nano.specs在与 gcc-arm 链接时包含该标志(我相信 yargarto 是一个分支,甚至只是 gcc-arm 的预编译),因为它减少了 libc 等代码的消耗。

于 2013-08-09T18:35:02.907 回答
5

我不是专家,但是:

可能 _init(正常的运行时入口点)引用了执行 ctor 和 dtor 表的代码。

您使用-nostartfiles 以避免标准启动,并且可能整个启动代码都被--gc-sections 消除了。显式调用再次添加引用。

如果省略 --gc-sections 不能解决它,它也可能是您的(嵌入式)链接器脚本中缺少的 keep() 语句,该语句始终保留入口代码,或者您自己的启动代码 (startup_*) 应该参考它

于 2012-12-06T16:58:15.527 回答
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] ();
}

另请参阅:了解 __libc_init_array

如果实现了自定义启动代码,则需要通过链接到“ init.o ”或通过实现类似于上面代码片段的内容来执行此初始化。

如果至少使用newlib-nano规范为arm-none-eabi ARMv7e 目标构建,那么_init方法会从crti.ocrtn.o链接进来,这为_init_fini提供了一些空存根。对 arm-none-eabi 的所有其他 stdlib 对象进行了一些搜索,但没有发现其他将部分附加到.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

如果有人想将此特定 ARM 目标的__libc_init_array与链接器选项 nostartfiles 结合使用则可以提供自己的_init存根方法,让链接器通过,只要没有其他初始化代码发送到节.init,除了crti.ocrtn.o 之外。存根可能如下所示:

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

特殊函数_init_fini是控制构造函数和析构函数的一些历史遗留问题。但是,它们已经过时,并且它们的使用可能会导致不可预测的结果。任何现代库都不应该再使用这些,而是​​使用 GCC 函数属性构造函数析构函数,它们将方法添加到.preinit_array.init_array.fini_array部分内的这些表中。

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

于 2021-05-20T23:19:46.310 回答