我知道这个问题被问到已经快两年了,但我自己必须弄清楚用 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
and __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 裸机支持。