Я знаю, что прошло почти два года с тех пор, как был задан этот вопрос, но мне просто нужно было разобраться в механике инициализации C ++ без метала с помощью GCC, поэтому я решил поделиться здесь деталями. Оказывается, в Интернете много устаревшей или запутанной информации. Например, часто упоминаемая оболочка 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
в своей сборке, я не могу использовать __libc_init_array
CodeSourcery для выполнения .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, например в Ubuntu 12.10 для ARM. Поддержка обоих компиляторов является одной из причин использования -nostdlib
и не полагается на поддержку чистого кода CodeSourcery CS3.