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

Ing*_*onk 9 c++ gcc constructor

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

class foobar
{
    int i;

public:
    foobar()
    {
        i = 100;
    }

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

foobar foo;

int main()
{
    foo.inc();
    for (;;);
}
Run Code Online (Sandbox Code Playgroud)

我这样编译:

arm-none-eabi-g++ -O0 -gdwarf-2 -mcpu=cortex-m3 -mthumb -c foo.cpp -o foo.o
Run Code Online (Sandbox Code Playgroud)

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

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

为什么.init_section中没有将指针添加到此符号?

Tre*_*son 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
...
Run Code Online (Sandbox Code Playgroud)

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

$ arm-none-eabi-objdump -j .init_array -s foo.o
Contents of section .init_array:
 0000 00000000                             ....
Run Code Online (Sandbox Code Playgroud)

但是,还有一个重定位部分将其正确设置为_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
Run Code Online (Sandbox Code Playgroud)

通常,.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)();
    }
}
Run Code Online (Sandbox Code Playgroud)

__init_array_start__init_array_end通过链接脚本定义:

. = ALIGN(4);
.init_array :
{
__init_array_start = .;
KEEP (*(.init_array*))
__init_array_end = .;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我不知道自2013年2月以来情况是否发生了变化,但我发现我必须将`__init_array_start`和`__init_array_end`视为函数指针,而不是指向函数指针的指针:typedef void(*InitFunc)(void); extern InitFunc __init_array_start; extern InitFunc __init_array_end; InitFunc*pFunc =&__ init_array_start; for(; pFunc <&__ init_array_end; ++ pFunc){(*pFunc)(); } (2认同)

acm*_*acm 3

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