设为首页 加入收藏

TOP

11.4.1 glibc全局构造与析构(2)
2013-10-07 00:44:33 来源: 作者: 【 】 浏览:58
Tags:11.4.1 glibc 全局 构造

11.4.1  glibc全局构造与析构(2)

我们暂且不管这里的神秘函数__tcf_1,它将在本节的最后部分讲到。GLOBAL__I_Hw作为特殊的函数当然也享受特殊待遇,一旦一个目标文件里有这样的函数,编译器会在这个编译单元产生的目标文件(.o)的".ctors"段里放置一个指针,这个指针指向的便是GLOBAL__I_Hw。

那么把每个目标文件的复杂全局/静态对象构造的函数地址放在一个特殊的段里面有什么好处呢?当然不为别的,为的是能够让链接器把这些特殊的段收集起来,收集齐所有的全局构造函数后就可以在初始化的时候进行构造了。

在编译器为每个编译单元生成一份特殊函数之后,链接器在连接这些目标文件时,会将同名的段合并在一起,这样,每个目标文件的.ctors段将会被合并为一个.ctors段,其中的内容是各个目标文件的.ctors段的内存拼接而成。由于每个目标文件的.ctors段都只存储了一个指针(指向该目标文件的全局构造函数),因此拼接起来的.ctors段就成为了一个函数指针数组,每一个元素都指向一个目标文件的全局构造函数。这个指针数组不正是我们想要的全局构造函数的地址列表吗?如果能得到这个数组的地址,岂不是构造的问题就此解决了?

没错,得到这个数组的地址其实也不难,我们可以效仿前面".init"和".finit"拼凑的办法,对".ctor"段也进行拼凑。还记得在链接的时候,各个用户产生的目标文件的前后分别还要链接上一个crtbegin.o和crtend.o吧?这两个glibc自身的目标文件同样具有.ctors段,在链接的时候,这两个文件的.ctors段的内容也会被合并到最终的可执行文件中。那么这两个文件的.ctors段里有什么呢?

crtbegin.o:作为所有.ctors段的开头部分,crtbegin.o的.ctor段里面存储的是一个4字节的 1(0xFFFFFFFF),由链接器负责将这个数字改成全局构造函数的数量。然后这个段还将起始地址定义成符号__CTOR_LIST__,这样实际上__CTOR_LIST__所代表的就是所有.ctor段最终合并后的起始地址了。

crtend.o:这个文件里面的.ctors内容就更简单了,它的内容就是一个0,然后定义了一个符号__CTOR_END__,指向.ctor段的末尾。

在前面的章节中已经介绍过了,链接器在链接用户的目标文件的时候,crtbegin.o总是处在用户目标文件的前面,而crtend.o则总是处在用户目标文件的后面。例如链接两个用户的目标文件a.o和b.o时,实际链接的目标文件将是(按顺序)ld crti.o crtbegin.o a.o b.o crtend.o crtn.o。在这里我们忽略crti.o和crtn.o,因为这两个目标文件和全局构造无关。在合并crtbegin.o、用户目标文件和crtend.o时,链接器按顺序拼接这些文件的.ctors段,因此最终形成.ctors段的过程将如图11-10所示。

 
(点击查看大图)图11-10  .ctor段的形成

在了解了可执行文件的.ctors段的结构之后,再回过头来看__do_global_ctor_aux的代码就很容易了。__do_global_ctor_aux从__CTOR_LIST__的下一个位置开始,按顺序执行函数指针,直到遇上NULL(__CTOR_END__)。如此每个目标文件的全局构造函数都能被调用。

【小实验】

在main前调用函数:

glibc的全局构造函数是放置在.ctors段里的,因此如果我们手动在.ctors段里添加一些函数指针,就可以让这些函数在全局构造的时候(main之前)调用:

#include <stdio.h>
void my_init(void)
{
printf("Hello ");
}

typedef void (*ctor_t)(void);
//在.ctors段里添加一个函数指针
ctor_t __attribute__((section (".ctors"))) my_init_p = &my_init;

int main()
{
printf("World!\n");
return 0;
}

如果运行此程序,结果将打印出:Hello World!

当然,事实上,gcc里有更加直接的办法来达到相同的目的,那就是使用__attribute__((constructor))

示例如下:

#include <stdio.h>
void my_init(void) __attribute__ ((constructor));
void my_init(void)
{
printf("Hello ");
}
int main()
{
printf("World!\n");
return 0;
}
【责任编辑:云霞 TEL:(010)68476606】

回书目   上一节   下一节

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇11.4.1 glibc全局构造与析构(3) 下一篇11.4.1 glibc全局构造与析构(1)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: