考虑以下代码:
#include <iostream>
#include <stdio.h>
void preinit_void();
void init_void();
__attribute__((section(".init_array")))
void (*p_init)(void) = &init_void;
void constructor_void() __attribute__((constructor));
void constructor_void() {
printf(__FUNCTION__);
}
__attribute__((section(".preinit_array")))
void (*p_preinit)(void) = &preinit_void;
void preinit_void() {
printf(__FUNCTION__);
}
void init_void() {
printf(__FUNCTION__);
}
int main() {
std::cout << __FUNCTION__ << '\n';
}
运行代码时,输出为
preinit_voidinit_voidconstructor_voidmain
如果我要将代码更改为:
#include <iostream>
#include <stdio.h>
void preinit_void();
void init_void();
void constructor_void() __attribute__((constructor));
void constructor_void() {
printf(__FUNCTION__);
}
__attribute__((section(".init_array")))
void (*p_init)(void) = &init_void;
__attribute__((section(".preinit_array")))
void (*p_preinit)(void) = &preinit_void;
void preinit_void() {
printf(__FUNCTION__);
}
void init_void() {
printf(__FUNCTION__);
}
int main() {
std::cout << __FUNCTION__ << '\n';
}
输出更改为:
preinit_voidconstructor_voidinit_voidmain
我对于首先初始化哪个部分感到困惑。输出是仅由于编译器的解析而发生变化(它首先找到.init_array
部分,而不是constructor
),还是有适当的初始化顺序?
答案 0 :(得分:2)
在当前(GNU)系统上,对ELF构造函数的引用被放置在.init_array
节中,就像您手动添加的条目一样。这就是更改源代码顺序时执行顺序更改的原因。 GCC和binutils具有语言扩展,可以根据优先级使用单独的节,并且链接编辑器将按优先级排序后,将每个应用程序/共享对象的所有内容序列化为最后一个.init_array
节(基于源代码和链接顺序) )。
基于对象之间的依赖关系(通过DT_NEEDED
表示)执行拓扑排序后,将执行不同共享对象的构造函数数组,以便在对象依赖之后初始化对象。
这超出了C ++初始化所需的范围,但是在(GNU)ABI相关的各种文档中或多或少地指定了它,并且它不会改变。由于引入了新功能,某些方面可能会发生变化(例如,多个DF_1_INITFIRST
对象,尽管这有点令人困惑),但希望它们只会影响实际使用这些功能的过程。