全局构造函数调用不在.init_array部分

时间:2011-06-14 12:19:26

标签: c++ gcc constructor

我正在尝试在嵌入式目标(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中没有将指针添加到此符号?

5 个答案:

答案 0 :(得分:26)

我知道问题已经差不多两年了,但我只是想弄清楚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,所以我需要自己调用静态初始值设定项:< / p>

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,例如:在Ubuntu 12.10 for ARM中。支持两个编译器是使用-nostdlib而不依赖于CodeSourcery CS3裸机支持的一个原因。

答案 1 :(得分:4)

Timmmm,

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

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

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

答案 2 :(得分:3)

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

答案 3 :(得分:2)

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

答案 4 :(得分:2)

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

 -Wl,--gc-sections

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