前言:继续程序的链接
之前我们说过了「符号解析」,「符号解析」的本质就是:建立定义与引用之间的联系
而「重定位」的本质就是:合并所有程序,程序中引用符号的地址修改为定义符号的绝对地址
重定位的全部流程
- 合并相同的节(数据节和代码节)
-
对「定义的符号」进行重定位
确定「定衣的符号」在虚拟空间的绝对地址,完成这一步以后,每个全局或局部变量都可确定地址
-
对「引用符号」进行重定位
用上述「定义符号」的绝对地址修改 .text 和 .data 节中「引用符号」的地址
链接器是如何完成重定位的
一切要从汇编阶段说起:
当程序编译完成后,汇编能够生成二进制可重定位文件。生成的可重定位文件包含两个节(IA-32):
- .rel.text 函数
- .rel.data 数据
每一个节都是一个表,每一个表包含多个表项,每个表项的结构如下:
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
}ELF32_Rel;
- r_offset 指出当前需要重定位的位置相对于所在节起始位置的偏移量。若重定位的是变量的位置,则所在节是 .data 节,若重定位的是函数的位置,则所在节是 .text 节
- r_info 指出当前的符号在符号表中的索引以及重定位类型。前 24 位是符号索引(r_sym),后 8 位是重定位类型(r_type,在后面会指出)
当链接器符号解析(静态)结束以后,E 集合中就有了所有目标文件。只需把需要重地位的符号修改成定义符号的地址就好了
在 IA-32 架构中有两种重定位的方式,对应着 r_type
R_386_PC32
R_386_32
自己这一块有一些疑惑,也不细说了。只知道有 PC 的那个就是根据 PC 重定位,没有 PC 的那个方法,就是虚拟空间的绝对地址。
最后
再完整的说一下符号静态链接的全过程
- 对每一个输入文件来说,首先判断是不是库文件。
-
如果不是库文件,就是目标文件 f。就能把目标文件放入 E 中,根据 f 中未解析符号和定义符号判断后分别放入 U、D 中
比如说,f 中有一个未解析符号 k,如果 D 中存在对它的定义,那么就可以建立联系。如果没有就放入 U 中。
- 如果是库文件,会试图把所有 U 中的符号与库文件中的符号匹配,匹配上了就从 U 放入 D 中。并把匹配上的模块放入 E 中。一直重复直到 U D 不再变化。库文件剩下的内容直接就不管了。
- 如果往 D 中放入了一个已经存在的符号或者扫描完所有文件后 U 还是非空,则链接器会停止并报错。否则将 E 中内容经过重定位后合并,生成可执行文件
如果我真的去实现一个编译器的时候。我再完全弄懂里面的道道