第十一章 运行库<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

静态glibc

 

1     入口函数和程序初始化

入口函数:在main函数之前和之后运行的函数称为入口函数或入口点(entry point)。
一个典型的程序运行如下:
(1)       进程创建,控制权交给入口点,入口点为运行库中的入口函数
(2)       入口函数对运行库和程序运行环境进行初始化,包括堆,I/O、线程、全局变量构造等
(3)       初始化后,调用main函数
(4)       Main函数完毕,调用入口函数,做清理工作,包括全局变量析构、堆销毁、关闭I/O,然后系统调用结束进程。

 

Glibc入口函数

Glibc的入口为_start,该入口为ld链接器默认的链接脚本指令的。i386的源码如下:
libc\sysdeps\i386\elf\Start.S
_start:

xorl %ebp,       %ebp
popl   %esi
movl  $esp                   %ecx

 

pushl          %esp                                     //sp
pushl          %edx                                     //rtld_fini
pushl          $__libc_csu_fini                 //fini
pushl          $__libc_csu_init                //init
pushl          %ecx                                     //ubp_av
pushl          %esi                                      //argc
pushl          main                                     //main
call              __libc_start_main
上面右边注释几位__libc_start_main的参数。__libc_start_main的工作如下:
__pthread_initialize_minimal();
_cax_atexit(rtld_fini, NULL, NULL);

__libc_init_first(argc, argv, __environ);
__cxa_atexit(fini, NULL, NULL);

(*init)(argc, argv, __environ);
加粗部分为注册函数,main函数结束时调用。在__libc_start_main末尾有:
result = main(argc, argv, __environ);
exit ( result );
exit函数中,依次调用注册过的函数,然后调用_exit系统调用。因为程序要么按main函数返回,要么exit()结束,都要进入exit函数,能保证注册函数顺利结束。

 

MSVC CRT入口函数

 

运行库与I/O

用户打开一个文件,获得该文件的文件描述符(file description)或者句柄。文件句柄总是内核的文件对象相关联的。fd012分别代表标准输入、标准输出和错误输出。那么fd是什么呢?
每个进程都有一个私有的“打开文件表”,该表由操作系统内核管理,打开文件表是一个指针数组,而fd则是数组的下表。
I/O初始化的职责是:需要在用户空间中建立stdiostdoutstderr及其他对应的FILE结构,使得程序进入main之后可以直接使用printfscanf等函数。

 

2     C/C++运行库

C语言运行库

C语言标准库

Glibc的启动文件

Glibc除了C标准库之外,还有几个辅助程序运行时的运行库。分别是crt1.ocrti.ocrtn.o
Crt1.o中包含_start,由它负责调用__libc_start_main初始化libc并且调用main函数。由于C++ELF文件改进,出现了在main函数之前\后执行的全局\静态对象的构造和析构,在目标文件中引入了.init.finit。链接器在静态链接时会把输入目标文件总的.init.finit段收集起来,并且与crti.ocrtn.o中的指令进行合并,形成一个.init.finit。其中crti.ocrtn.o中的内容是整个初始化段的前部分和后部分。经过连接后,最终的形成_init()_finit两个函数,由__libc_csu_init__libc_csu_fini调用。
GCC平台相关的文件

crtbeginT.ocrtend.o实现C++全局对象的构造和析构。.init.finit段提供了一个在main之前和之后运行代码的机制,真正全局构造和析构则由crtbeginT.ocrtend.o完成。
libgcc.aGCC提供的一个仿真计算库,软件仿真各种硬件不提供的运算。
libgcc_eh.a支持C++的异常处理。

 

3     运行库与多线程

4     C++全局构造与析构

_startà__libc_start_mainàinit\__libc_csu_inità_init

这里的_init正是crti中的_init()函数,经过反汇编,可以发现函数进入了
_startà__libc_start_mainàinit\__libc_csu_inità_inità__do_global_crt_aux

__do_global_crt_aux中有对__CTOR_LIST__[i]的循环调用。
GCC在编译某个文件时,会遍历该文件中的所有全局对象,生成一个特殊的函数,对全局对象进行构造和析构:(以helloworld为例)
Static void GLOBAL__I_Hw(void)
{
           Hw::Hw();                  //构造
Atexit(__tcf_1);        //析构
}
GCC会在该文件的目标文件中生成.ctors,存放指向改函数的指针。
链接器在链接的时候,所有的.ctors段被合并成一个.ctors段,因为合并后的.ctors成了一个指针数组,存放全局构造函数的地址列表__CTOR_LIST__
在链接的时候,crtbegin.ocrtend.o也包含.ctors段,crtbegin.o作为其实,存放的内容为-1,会被链接器修改为全局构造函数的数量,并且将起始地址定义为__CTOR_LIST__crtend.o段的内容为全0,并定义符号__CTOR_END__指令段尾。
析构刚好相反,命名跟构造函数对应。
5     fread实现