目前的链接器都采用2步链接法,链接过程可采用脚本来控制
1,空间与地址分配,
2,符号解析与重定位
一空间与地址分配
有2种方式,按序叠加如图1,相似段合并如图2。目前一般采用第二种方法。
事实上,我们在这里谈到的空间分配只关注于虚拟地址空间分配。
图1
图2
在Linux下,ELF可执行文件默认从地址0x08048000开始分配。
$ld a.o b.o -e nomain -o ab
-e nomain表示将nomain函数作为程序入口,ld链接器默认的程序入口为_start(并非程序一定从main函数入口,也可以没有main函数)
二 符号解析与重定位
$objdump -d a.o
使用objdump -d参数可以看到代码段反汇编的结果。
在未进行空间分配之前,目标文件代码段中的起始地址以0x00000000开始,等到空间分配完成以后,各个函数才会确定自己在虚拟地址空间的位置。
$readelf -s a.o
查看 a.o 的符号表。
三,common块机制
概念:当不同的目标文件需要的common 块空间大小不一致时,以最大的那个块为准。
编译器将未初始化的全局变量定义作为弱符号处理。
当编译器将一个编译单元编译成目标文件的时候,如果该编译单元包括了弱符号(未初始化的全局变量)那么该弱符号最终所占空间的大小是未知的,
因为有可能其他编译单元中该符号所占的空间比本编译单元该符号所占的空间要大。所以编译器此时无法为该弱符号在BSS段分配空间,因为所需空间大小未知。
但在链接过程中可以确定弱符号的大小,所以它可以在最终输出文件的BSS段为其分配空间。
四,重复代码消除,
产生的可能:模板\ 外部内联函数\虚函数表
针对模板的方法是:将每个模板的实例代码都单独地存放在一个段中。
其他也是用同样的方法来消除。
五\
.init ,若一个函数放在.init段,在 main函数执行前系统就会执行它。
. fini,若一个函数放在. fini段,在 main函数执行后该函数就会被执行。
C++中构造与析构函数就是由此而实现。
六\BFD库
目标:通过一种统一的接口来处理不同的目标文件格式。
现在GCC\ 链接器LD \调度器GDB及binutils 的其他工具都通过BFD库来处理目标文件
ubuntu下包含BFD开发库的软件包为binutils-dev