【问题标题】:Why do I have an undefined reference to _init in __libc_init_array?为什么我在 __libc_init_array 中有未定义的 _init 引用?
【发布时间】:2012-12-05 23:56:30
【问题描述】:

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

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

【问题讨论】:

    标签: gcc linker yagarto


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      我不是专家,但是:

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

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

      如果省略 --gc-sections 不能解决问题,也可能是您的(嵌入式)链接描述文件中缺少 keep() 语句,该语句始终保留入口代码,或者您自己的启动代码 (startup_*) 应该引用它

      【讨论】:

      • 所以,如果我将 void _init(void){} 添加到我的启动代码中,它编译得很好。我想知道这个函数应该做什么?
      • _init 通常是二进制文件中的点,如果二进制文件已加载,操作系统会跳转到该点。或多或少的入口点的默认标签。如何解决此问题取决于您的嵌入式加载程序系统的设置方式。对我来说,听起来这个工具链并不完美。
      • 我没有“嵌入式加载器系统”,我自己编写了启动代码(或者,更具体地说,我从互联网上复制+粘贴了它)......这是一个 Cortex-M4, ResetHandler 是第一个(也是唯一一个)执行的东西。
      • 谁在哪里。启动代码和库系统似乎不匹配。
      • 在我的情况下,我必须使用-nodefaultlibs 为 STM32 链接制作 C++ 代码,而不会出现“缺少 _init”错误。
      【解决方案3】:

      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' 或通过实现类似于上面的代码 sn-p 的东西来执行此初始化。

      如果至少为具有 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
      

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

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

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

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

      【讨论】:

        猜你喜欢
        • 2015-10-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-23
        • 2012-02-05
        • 2014-04-06
        • 2014-03-31
        相关资源
        最近更新 更多