【问题标题】:Global constructor call not in .init_array section全局构造函数调用不在 .init_array 部分
【发布时间】:2011-06-14 12:19:26
【问题描述】:

我正在尝试在嵌入式目标 (ARM Cortex-M3) 上添加全局构造函数支持。 假设我有以下代码:

class foobar
{
    int i;

public:
    foobar()
    {
        i = 100;
    }

    void inc()
    {
        i++;
    }
};

foobar foo;

int main()
{
    foo.inc();
    for (;;);
}

我是这样编译的:

arm-none-eabi-g++ -O0 -gdwarf-2 -mcpu=cortex-m3 -mthumb -c foo.cpp -o foo.o

当我使用 objdump 查看 .init_array 部分时,它显示 .init_section 的大小为零。

我确实得到了一个名为 _Z41__static_initialization_and_destruction_0ii 的符号。 当我反汇编目标文件时,我看到全局构造是在 static_initialization_and_destruction 符号中完成的。

为什么 .init_section 中没有给这个符号添加一个指针?

【问题讨论】:

  • 您认为 gcc 不够聪明,无法 1) 看到值是 100,或者 2) 看到值只是增加但从未使用过?
  • @Bo Persson:这就是我添加 -O0 选项的原因,所以 gcc 不会优化它。

标签: c++ gcc constructor


【解决方案1】:

我知道这个问题被问到已经快两年了,但我只需要自己弄清楚使用 GCC 进行裸机 C++ 初始化的机制,所以我想我会在这里分享详细信息。事实证明,网络上有很多过时或令人困惑的信息。例如,经常提到的 collect2 包装器似乎没有用于 ARM ELF 目标,因为它的任意部分支持启用了下面描述的方法。

首先,当我使用 Sourcery CodeBench Lite 2012.09-63 使用给定命令行编译上面的代码时,我确实看到了正确的 .init_array 部分大小为 4:

$ arm-none-eabi-objdump -h foo.o

foo.o:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
...
 13 .init_array   00000004  00000000  00000000  0000010c  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, DATA
...

当我查看部分内容时,它只包含 0:

$ arm-none-eabi-objdump -j .init_array -s foo.o
Contents of section .init_array:
 0000 00000000                             ....

但是,还有一个重定位部分将其正确设置为_GLOBAL__sub_I_foo

$ arm-none-eabi-objdump -x foo.o
...
RELOCATION RECORDS FOR [.init_array]:
OFFSET   TYPE              VALUE
00000000 R_ARM_TARGET1     _GLOBAL__sub_I_foo

一般来说,.init_array 指向您的所有_GLOBAL__sub_I_XXX 初始化程序存根,每个存根都调用自己的_Z41__static_initialization_and_destruction_0ii 副本(是的,它是多重定义的),它使用适当的参数调用构造函数。

因为我在构建中使用了-nostdlib,所以我不能使用CodeSourcery 的__libc_init_array 为我执行.init_array,所以我需要自己调用静态初始化器:

extern "C"
{
    extern void (**__init_array_start)();
    extern void (**__init_array_end)();

    inline void static_init()
    {
        for (void (**p)() = __init_array_start; p < __init_array_end; ++p)
            (*p)();
    }
}

__init_array_start__init_array_end 由链接描述文件定义:

. = ALIGN(4);
.init_array :
{
__init_array_start = .;
KEEP (*(.init_array*))
__init_array_end = .;
}

这种方法似乎适用于 CodeSourcery 交叉编译器和本机 ARM GCC,例如在 ARM 的 Ubuntu 12.10 中。支持这两种编译器是使用 -nostdlib 而不依赖 CodeSourcery CS3 裸机支持的原因之一。

【讨论】:

  • 我不知道自 2013 年 2 月以来情况是否发生了变化,但我发现我必须将 __init_array_start__init_array_end 视为函数指针,而不是指向函数指针的指针: typedef void (InitFunc)(无效); extern InitFunc __init_array_start; extern InitFunc __init_array_end; InitFunc pFunc = &__init_array_start; for ( ; pFunc
  • @Timma,您的代码中有几个错误。更改为typedef void (*InitFunc)(void); extern InitFunc __init_array_start; extern InitFunc __init_array_end; InitFunc *pFunc = &amp;__init_array_start; for ( ; pFunc &lt; &amp;__init_array_end; ++pFunc ) { (*pFunc)(); } 时编译。
  • 问题understanding the __libc_init_array 提供了一个更清晰的代码,它使用了一组函数指针。它有效。
  • @lorcap 看起来我的星号被解释为降价。正如你所看到的,我的一些文字是斜体的。这恰好是我输入星号的地方。抱歉,不知道我怎么没看到。也许评论格式有一个 SO 更新?
【解决方案2】:

提姆,

我刚刚在 nRF51822 上遇到了同样的问题,并通过在 Stock Nordic .ld 文件中的几行周围添加 KEEP() 来解决它:

KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))

同时,我对 fini_array 区域也做了同样的事情。解决了我的问题,链接器仍然可以删除其他未使用的部分...

【讨论】:

    【解决方案3】:

    由于 gcc 的 -c 参数,您只生成了一个目标文件。要创建 .init 部分,我相信您需要将该 .o 链接到实际的可执行文件或共享库中。尝试删除 -c 参数并将输出文件重命名为“foo”,然后使用反汇编程序检查生成的可执行文件。

    【讨论】:

    • 当我这样做时,我仍然遇到同样的问题。
    • 好的,所以需要注意的是,我对 ARM 基本上一无所知,我想说这意味着您在实现对 ARM 的全局 ctor 和 dtors 的支持方面还有另一个步骤要执行。您获得 Z41... 符号的事实令人鼓舞,因为它向我表明 .o 文件是正确的。但很明显,链接时没有发生任何事情。在 x86 GCC 上,一个名为 collect2 的链接时间进程处理将所有各种静态构造函数合并到 .init 部分,然后再移交给 ld (或类似的东西)。我认为链接器支持是您的下一步。
    【解决方案4】:

    如果你仔细看,_Z41__static_initialization_and_destruction_0ii 会在全局构造函数中被调用。哪个反过来将链接到.init_array 部分(来自 CodeSourcery 的arm-none-eabi-)或其他一些函数(__main(),如果您使用的是 Linux g++)。 () 这应该在启动时或main() 处调用。 另请参阅this 链接。

    【讨论】:

      【解决方案5】:

      我有一个类似的问题,我的构造函数没有被调用(nRF51822 Cortex-M0 with GCC)。问题原来是由于这个链接器标志:

       -Wl,--gc-sections
      

      别问我为什么!我以为它只是删除了死代码。

      【讨论】:

      • 很多很多天真的“死代码”消除算法会删除 unreferenced 符号/不能对 可达输出 有贡献的部分...即使它们是中断处理程序、静态全局数据结构等所必需的。这可能不是 gcc 的工作方式,但它是一个常见的问题。在 gcc 和 clang 下尝试链接时优化 -flto -O4 看看会发生什么。
      • 它会删除死代码。如果有一个没有人引用的全局对象,它的构造函数是否调用了死代码?几乎没有人会调用那个对象......
      猜你喜欢
      • 2018-08-11
      • 2013-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多