初始化节函数是先调用还是构造函数?

时间:2018-06-23 10:06:08

标签: c++ gcc initializer

考虑以下代码:

#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),还是有适当的初始化顺序?

1 个答案:

答案 0 :(得分:2)

在当前(GNU)系统上,对ELF构造函数的引用被放置在.init_array节中,就像您手动添加的条目一样。这就是更改源代码顺序时执行顺序更改的原因。 GCC和binutils具有语言扩展,可以根据优先级使用单独的节,并且链接编辑器将按优先级排序后,将每个应用程序/共享对象的所有内容序列化为最后一个.init_array节(基于源代码和链接顺序) )。

基于对象之间的依赖关系(通过DT_NEEDED表示)执行拓扑排序后,将执行不同共享对象的构造函数数组,以便在对象依赖之后初始化对象。

这超出了C ++初始化所需的范围,但是在(GNU)ABI相关的各种文档中或多或少地指定了它,并且它不会改变。由于引入了新功能,某些方面可能会发生变化(例如,多个DF_1_INITFIRST对象,尽管这有点令人困惑),但希望它们只会影响实际使用这些功能的过程。