链接与加载


任何一个链接器和加载器的基本工作都非常简单: 将更抽象的名字与更底层的名字绑定起来,好让程序员使用更抽象的名字编写代码。

  • 也就是说,它可以将程序员写的一个诸如
    getline的名字绑定到“iosys模块内可执行代码的612字节处”或者可以采用诸如“这个模块的静态数据开始的第450个字节处”这样更抽象的数字地址然后将其绑定到数字地址上。
  • 链接器对每一个程序的部分地址进行绑定并分配相对地址,加载器完成最后的重定位步骤并赋予的实际地址。

linker: 它扮演的是一个“聚焦”功能的角色,即将相关代码或数据收集到一个段中。

重定位: 为程序不同部分分配加载地址,调整程序中的数据和代码以反映所分配地址

  • 在很多系统中,重定位不止进行一次。对于链接器的一种普遍情景是由多个子程序来构建一个程序,并生成一个链接好的起始地址为0的输出程序,各个子程序通过重定位在大程序中确定位置。当这个程序被加载时,系统会选择一个加载地址,而链接好的程序会作为整体被重定位到加载地址。

符号解析: 当通过多个子程序来构建一个程序时,子程序间的相互引用是通过符号进行的。

  • 主程序可能会调用一个名为sqrt的计算平方根例程,并且数学库中定义了sqrt例程。链接器通过标明分配给sqrt的地址在库中来解析这个符号,并通过修改目标代码使得call指令引用该地址。

尽管有相当一部分功能在链接器和加载器之间重叠,定义一个仅完成程序加载的程序为加载器,一个仅完成符号解析的程序为链接器是合理的。他们任何一个都可以进行重定位,而且曾经也出现过集三种功能为一体的链接加载器。

链接器和加载器共有的一个重要特性就是他们都会修改目标代码,他们也许是唯一比调试程序在这方面应用更为广泛的程序。这是一个独特而强大的特性,而且细节非常依赖于机器的规格,如果做错的话就会引发令人困惑的bug。

就象编译或汇编一样,链接基本上也是一个两遍的过程。链接器将一系列的目标文件、库、及可能的命令文件作为它的输入,然后将输出的目标文件作为产品结果,此外也可能有诸如加载映射信息或调试器符号文件的副产品。如图所示。


  • 每个输入文件都包含一系列的段(segments),即会被连续存放在输出文件中的代码或数据块。每一个输入文件至少还包含一个符号表(symbol table)。有一些符号会作为导出符号,他们在当前文件中定义并在其他文件中使用,通常都是可以在其它地方被调用的当前文内例程的名字。其它符号会作为导入符号,在当前文件中使用但不在当前文件中定义,
    通常都是在该文件中调用但不存在于该文件中的例程的名字。
  • 当链接器运行时,会首先对输入文件进行扫描,得到各个段的大小,并收集对所有符号的定义和引用。它会创建一个列出输入文件中定义的所有段的段表,和包含所有导出、导入符号的符号表。
  • 利用第一遍扫描得到的数据,链接器可以为符号分配数字地址,决定各个段在输出地址空间中的大小和位置,并确定每一部分在输出文件中的布局。
  • 第二遍扫描会利用第一遍扫描中收集的信息来控制实际的链接过程。它会读取并重定位目标代码,为符号引用替换数字地址,调整代码和数据的内存地址以反映重定位的段地址,并将重定位后的代码写入到输出文件中。通常还会再向输出文件中写入文件头部信息,重定位的段和符号表信息。如果程序使用了动态链接,那么符号表中还要包含运行时链接器解析动态符号时所需的信息。在很多情况下,链接器自己将会在输出文件中生成少量代码或数据,例如用来调用覆盖中或动态链接库中的例程的“胶水代码”,或在程序启动时需要被调用的指向各初始化例程的函数指针数组。
  • 在链接器运行时,链接器会识别出解析未定义符号所需的共享库,但是链接器会在输出文件中标明用来解析这些符号的库名称,而不是在链接时将他们链入程序,这样可以在程序被加载时进行共享库绑定。

链接器和加载器的核心动作是重定位和代码修改。

  • 作为链接过程的一部分,链接器会修改目标代码以反映实际分配的地址。例如,考虑如下这段将变量a中的内容通过寄存器eax移动到变量b的x86代码片段。
    mov a,%eax
    mov %eax,b
    如果a定义在同一文件的位置0x1234,而b是从其它地方导入的,那么生成的代码将会是:
    A1 34 12 00 00 mov a,%eax
    A3 00 00 00 00 mov %eax,b
    每条指令包含了一个字节的操作码和其后4个字节的地址。第一个指令有对地址1234的引用(由于x86使用从右向左的字节序,因此这里是逆序),而第二个指令由于b的位置是未知的因此引用位置为0。
    现在想象链接器将这段代码进行链接,a所属段被重定位到了0x10000,b最终位于地址0x9A12。则链接器会将代码修改为:
    A1 34 12 01 00 mov a,%eax
    A3 12 9A 00 00 mov %eax,b
    也就是说,链接器将第一条指令中的地址加上0x10000,现在它所标识的a的重定位地址就是0x11234,并且也补上了b的地址。虽然这些调整影响的是指令,但是目标文件中数据部分任何相关的指针也必须修改。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值