静态链接
- 本文需要用到的代码
#include<stdio.h>
int global_init_val = 84; //.data
int global_uninit_val;
__attribute__((section("FOO"))) int global = 42;//FOO
int main(){
int init_a = 20;
int uninit_b;
static int static_b = 40;//.data
static int static_uninit_c;
return 0;
}
- 对于链接器来说,整个链接过程,就是将几个输入目标文件加工合并成一个输出文件,由目标文件
.o
到可执行文件.out
,合并方式一般由两种- 按序叠加,其实就是把
a.o
的所有段放好了,然后接下来放b.o
的所有段,但是这种方法有缺点- 首先就是每个文件的段都是分开的,导致最后的可执行文件的段可能有许多个,非常的浪费空间
- 内存的基本分配单位是页,所以每个段都会至少占据一个页,那么就会产生非常多的页内碎片
- 相似段合并 : 把几个输入目标文件的一样的段放一起,比如把a.o和b.o的.text放一起,然后放.data,现在一般都是这种方式
- 按序叠加,其实就是把
两步链接
- 空间和地址分配
- 分配虚拟空间
- 更新符号地址
- 符号解析和重定位
空间和地址分配
- 分配虚拟空间
- 将所有目标文件的符号表以及相关信息整合成一个全局符号表,通过给每个符号加上一个偏移量就可以得出符号的虚拟地址,比如main函数原本相对.text的偏移是X(这个y通过查看该目标文件的符号表就可以知道
readelf -s hello2.o
),而分配虚拟空间后,.text的虚拟空间是Y,所以main的最终的虚拟地址就是X+Y
- 注意 : linux中,虚拟空间默认从0x08048000开始分配
符号解析和重定位
-
在ELF文件格式中,存在一个重定位表,该表记录的信息是:该目标文件所有存在外部引用的地方,而重定位表往往由多个,比如
.ref.text
,.ref.rodata
,使用objdump -r hello2.o
可以查看文件的重定位表
-
符号解析就是根据重定位表,找到哪里需要重定位,重定位的符号是什么,然后再去全局符号表找到该符号的虚拟地址
-
指令修正方式,<<程序员的自我修养>>这本书p109有详细的例子说明