2.5.3 重定位
重定位从本质上来说就是地址修正。由于目标文件在链接之前不能获取自己所使用符号的虚拟地址信息,因此导致依赖于这些符号的数据定义或者指令信息缺失。汇编器在生成目标文件的时候就记录下所有需要重定位的信息。链接器获取这些重定位信息,并按照重定位信息的含义修改已经生成的代码,使得最终的代码正确、完整。
之所以称重定位是链接器最关键的操作,主要是因为地址空间分配和符号解析都是为重定位做准备的。程序在运行时,段的信息、符号的信息都显得“微不足道”,因为CPU只关心文件内的代码和数据。即便如此,也不能忽略地址空间分配和符号解析的重要性。既然重定位是对已有二进制信息的修改,因此作为链接器需要清楚几件事情:
1)在哪里修改二进制信息?
2)用什么信息进行修改?
3)按照怎样的方式修改?
这三个问题反映在重定位中对应的三个参数:重定位地址、重定位符号和重定位类型。
重定位地址在重定位表中没有直接记录,因为在重定位目标文件内,段地址还没确定下来,它只记录了重定位位置所在段内的偏移,在地址空间分配结束后,我们使用如下公式计算出重定位地址:
重定位地址 = 重定位位置所在段基址 + 重定位位置的段内偏移
重定位符号记录着被指令或者数据使用的符号信息,比如call指令的标号、mov指令使用的变量符号等。在符号解析结束后,重定位符号的地址就已经确定了。
重定位类型决定修改二进制信息的方式,即绝对地址重定位和相对地址重定位。
在确定了重定位符号地址和重定位地址后,根据重定位的类型,链接器便可以正确修改重定位地址处的符号地址信息。
至此,链接器的主要工作流程描述完毕。作为编译系统的最后一个功能模块,链接器与操作系统的关系是最密切的,比如它需要考虑页面地址对齐、指令系统结构以及加载器工作的特点等。