程序的静态链接(4):重定位

概述

在完成地址分配和符号解析之后,程序中每个定义的符号都有了唯一的运行时内存地址,并且所有的符号引用都可以与某个确定的符号定义关联起来,然后链接器就可以进入重定位阶段了。在这个阶段中,链接器会读取所有可重定位目标文件中的重定位信息,然后调整代码段和数据段中对每个符号的引用,使它们指向正确的运行时内存地址。

重定位表

重定位信息描述了如何对对应的节区中的符号引用进行修改,ELF文件使用重定位表来存储重定位信息。对于可重定位文件中每个需要重定位的节区都有一个对应的重定位表(通常使用**.rel**作为前缀)。表中包含了需要进行重定位的项,典型的重定位表包括.rel.text.rel.data,其中:

  • .rel.text:描述代码段的重定位信息,用于对.text代码段进行重定位;
  • .rel.data:描述数据段的重定位信息,用于对.data数据段进行重定位。

可以使用objdump来查看main.o的重定位表:
在这里插入图片描述
上图中,RELOCATION RECORDS FOR [.text]表示这个重定位表是代码段的重定位表,后面的信息是需要进行重定位的符号,其中偏移表示代码段中需要被调整的位置。

重定位项

当编译器生成一个可重定位目标文件时,它并不知道代码和数据最终会加载到内存中的什么位置,也不清楚这个模块引用的任何外部定义的符号的位置。因此,当编译器遇到对最终位置未知的符号的引用,就会为其生成一个重定位项,用于指示链接器在链接生成可执行文件时如何修改这个引用。ELF重定位表中存储的重定位项,其结构定义如下:

typedef struct {
        Elf64_Addr r_offset;
        uint64_t   r_info;
        int64_t    r_addend;
} Elf64_Rela;

重定位项最主要的作用是记录了需要重定位的符号在文件中的位置以及如何对符号进行重定位。重定位项结构中的各个字段意义如下:

  • r_offset:用于指明重定位项的地址,对于可重定位文件,字段的值为重定位项相对于所在节的偏移地址;对于可执行文件或共享目标文件,字段的值是链接器为重定位项分配的虚拟地址;
  • r_info:低32位表明了符号重定位的类型,高32位用于确定符号在符号表中的索引;
  • r_addend:附加的符号常数,一些重定位类型要使用这个常数被对被修改引用的值进行偏移调整。

重定位类型

对于可重定位文件中,基本的重定位类型有R_X86_64_32和R_X86_64_PC32两种,其描述如下:

  • R_X86_64_32:重定位一个使用32位绝对地址的引用。通过绝对寻址,处理器直接使用在指令中编码的32位值进行寻址;
  • R_X86_64_PC32:重定位一个使用32位PC相对地址的引用。当处理器在执行一条使用PC相对寻址的指令时,通过将指令中编码的32位值加上PC的当前运行时值,最终得到引用地址,PC值通常是下一条指令在内存中的地址。

重定位符号引用

在前面示例源码中,main函数引用了两个全局符号arraysum。编译器为每个引用都生成了一个重定位项,每项的描述信息可以在重定位表中进行查看。这里将main.o目标文件进行反汇编,可以看到编译器已经指定需要为下图中两个位置的对arraysum的引用进行重定位:
在这里插入图片描述
对于重定位类型R_X86_64_32和R_X86_64_PC32,其重定位修正方法如下:

  • R_X86_64_32:属于绝对寻址方式修正,修正方式为 S + A
  • R_X86_64_PC32: 属于相对寻址方式修正,修正方式为S + A - P

其中,S=符号的实际地址,A=附加的修正常量,P=重定位位置的地址。对比可以看出,绝对寻址方式修正和相对寻址方式修正的区别就是绝对寻址修正后的地址是该符号的实际地址,相对寻址修正后的地址为符号距离被修正位置的地址差。

重定位绝对引用

对于main函数中对全局变量array的引用重定位类型为R_X86_64_32,使用绝对寻址方式修正,计算公式为S + A,其中:

  • S是符号array的实际地址,值为0x601000;
  • A是附加常量,值为0。

对这个重定位位置修正后值为:0x601000 + 0 = 0x601000,则指令修正后的结果为:
在这里插入图片描述

重定位PC相对引用

对于main函数对外部函数sum的引用重定位类型为R_X86_64_PC32,使用相对寻址方式修正,计算公式为S + A - P,其中:

  • S是符号sum的实际地址,值为0x400107;
  • A是附加常量,值为-4;
  • P是重定位位置的地址,值为0x4000fb。

对这个重定位位置修正后值为:0x400107 + (-4) - 0x4000fb = 0x8,则指令修正后的结果为:
在这里插入图片描述

相关参考

  • 《程序员的自我修养——链接、装载与库》
  • 《深入理解计算机系统》
posted @ 2020-07-26 20:00  Aspiresky  阅读(335)  评论(0编辑  收藏  举报