在c++入口函数中,会先完成全局变量的构造,也就是在mian()函数之前完成。在Linux环境下fork一个新进程后然后exec执行,这个时候,修改内核栈中的返回地址为可执行文件的入口的地址,就是_start地址,然后出栈,返回到用户态下,当然这个时候就跳到_start处执行mian函数之前的初始化工作,初始化后再跳转到main处执行,我们知道进程映像有许多段组成——代码段,数据段,字符表,字符串表等等,其中还有.init和.finit段,这两段最后会拼接成_init()和_finit()函数用作mian函数之前和之后执行。_start会传经来一个init函数指针,这个指针指向初始化工作,但是init实际指向了_libc_csu_init函数(位于glibc源代码的csu\ELF-init.c中),在这个函数中又调用了_init()函数,这个函数才会调用.init段,用户在Init段中的函数会在这里执行。我们如果反汇编此函数会知道该函数有调用了do_global_ctors_aux函数,这个函数是在gcc提供的目标文件中,这个函数才是最终的构造函数调用的地方,主要代码:
for(i= nptrs ; i>=1;
i--){ __CTOR_LIST__[i] ();}
很明显,__CTOR_LIST__[i] 是每个构造函数的入口地址了。往下深挖下去,这个数组是如何构建的,编译器会将每个目标文件的构造函数地址放在一个.ctors这个特殊的段中,链接器链接目标文件的时候会把.ctors段在一起,也就是所有的全局构造函数了。下面也做了个实验。
#include <stdio.h>
void my_init(void)
{
printf("hello ");
}
typedef void (*ctor_t) (void);
ctor_t __attribute__((section (".ctors"))) my_init_p = &my_init;
int main()
{
printf("world\n");
return 0;
}运行结果
