编译原理-链接程序设计

书中设计的是静态链接器。

链接器的工作内容是把多个可重定位文件正确的合并为可执行文件,不是简单的物理合并。

合并内容如下:

  • 合并同类的段
  • 为段分配合适的地址空间
  • 分析目标文件符号定义的完整性
  • 对符号的地址进行解析
  • 重定位

image-20220428075300992

地址空间分配

汇编器生成的目标文件,不知道数据段和代码段的虚拟地址, 因此他们的段地址都设置为0。

链接器需要为他们分配端的基址。

链接器按照目标文件的输入顺序扫描文件信息,从每个文件的段表中提取出各个文件的代码段和数据段信息。

链接器将所有文件的同类型端合并,按照代码段、数据段、.bss端的顺序一次决定每个段的起始地址,此时需要考虑段间对其产生的偏移,以及特殊的地址计算方式。

如: a.o,b.o两个目标文件

a.o:数据段大小 0x08字节

​ 代码段大小0x04字节

b.o:数据段大小0x04字节

​ 代码段大小0x21字节

链接器处理后,可执行文件ab:数据段大小 0x0c字节

​ 代码段大小0x6d字节(其中对齐b.o代码段消耗2字节

image-20220428081122612

符号解析

如果说地址空间分配是为段指定地址的话,符号解析就是为段内的符号指定地址。

一个汇编程序内的符号可以分为两类

  1. 内部符号:自身定义的符号
  2. 外部符号:来自其他文件定义的符号,本地文件只是使用该符号。

内部符号在其端内的偏移地址是确定的,当段的起始地址指定完毕后,内部符号的地址按照如下方式计算

符号地址=符号所在的段基地址+符号所在段内偏移

外部符号的地址在本地文件中是无法确定的,但是外部符号也是定义在一个外部文件中,在这个外部文件中,外部符号就是这个外部文件的内部符号了。它的基地址的计算方式也满足上面的式子。使用该外部符号的本地文件需要的也是这个地址。

在重定位文件内,符号表记录了符号的所有信息。

此时对于本地文件中的内部符号,和外部文件的外部符号有不同的标记方式:

  • 内部符号直接记录符号的段内偏移
  • 外部符号直接标识为“未定义

那么这个“未定义”在什么时候被修改为正确的地址呢?

当链接器扫描到该外部符号所在的外部文件时,此时这个“未定义”会被替换为正确的符号地址。

最终保证所有目标文件内的符号信息,无论是内部还是外部符号的地址都是正确的。

链接器在扫描重定位目标文件的时候,会动态的维护两个目标集合。

  • 一个是记录所有文件定义的全局符号集合Export
  • 一个是记录所有文件使用未定义符号的集合Import,该集合内的所有符号都来源于其他文件。

所有目标文件扫描完毕后,所有目标文件符号表内的所有符号都会获得完整、正确的符号地址信息。

重定位

重定位是链接器中最关键的操作。

从本质上来说就是地址修正。

因为目标文件在链接之前无法知道自己所用符号的虚拟地址信息,因此导致依赖这些符号的数据定义或者指令信息缺失。

链接器需要清除的事情:

  1. 在哪里修改二进制?(重定位地址)
  2. 用什么信息修改?(重定位符号)
  3. 按照怎样的方式修改?(重定位类型)

重定位地址

重定位地址在重定位表中没有记录,因为在重定位目标文件内,段地址还没确定下来,它只记录了重定位位置所在的段内偏移。在地址空间分配结束后,可以通过以下公式计算得出:

重定位地址 = 重定位位置所在段基址 + 重定位位置的段内偏移

重定位符号

重定位符号记录着被指令或者数据使用的符号信息。在符号解析完成后,重定位符号地址就已经确定了

重定位类型

重定位类型决定修改二进制信息的方式

  • 绝对地址重定位
  • 相对地址重定位

相关内容

0%