对于X86平台下的ELF文件的重定位入口所修正的指令寻址方式只有两种:绝对近址32寻址和相对近址32寻址。
这两种指令修正方式每个被修正的位置的长度都为32位,即4个字节,而且都是近址寻址,不用考虑Intel的段间远址寻址。r_info成员的低8位表示重定位入口的类型。
X86基本重定位类型
宏定义 值 重定位修正方法
R_386_32 1 绝对寻址修正 S + A
R_386_PC32 2 相对寻址修正 S + A - P
A = 保存在指令中被修正位置的值
P = 被修正的位置(相对于段开始的偏移量或者虚拟地址),该值可以通过r_offset计算得到。
S = 符号的实际地址,即由r_info的高24位指定的符号的实际地址。
因为a.o中shared和swap需要地址重定位,以此为例子说明重定位地址修正方法。用objdump -r a.o可以得到a.o中需要重定位的符号如图4.2.7所示:
***图4.2.7(同4.2.4相同)***
从图中可知,shared的重定位类型为R_386_32,因此为绝对寻址修正方式。那么S的值怎么得到,可以从a.o,b.o链接成的可执行文件的符号表中得到,可执行文件的符号表如图4.2.8:
***图4.2.8***
从图中看出shared的实际地址为0x08049158,即是S的值。从a.o反汇编指令中可知A的值,如图4.2.9所示:
***图4.2.9***
从图4.2.7中得知shared相对段开始的偏移为0x15,所以A的值是0x0。那么shared重定位后的地址为S + A = 0x08049158 + 0x0 = 0x08049158。怎样验证该数值的正确性?可以通过可执行文件的反汇编代码验证,如图4.2.10所示:
***图4.2.10***
注意:图中的地址为小端格式,即数值的高位存放在低地址,低位在高地址。
而swap的重定位方式为相对寻址修正,swap的S值从图4.2.8中可知为0x080480c0,A的值为0xfffffffc,为-4的补码。P的值为符号swap的偏移量加上该段的起始地址,swap的偏移量为0x21,该段的起始地址符号main的地址,即为0x08048094。因此最终swap的地址为0x080480c0 - 0x04 - (0x08048094 + 0x21)= 0x07。从图4.2.10可以看到call指令被修正后的地址确实为0x07。而call指令的调用地址是修正后的地址加上下一条指令的地址,0x07 + 0x080480b9 = 0x080480c0,该地址即为swap函数的地址。
***图4.2.11***