一个关于重定位的例子 1、重定位项有两种结构类型, typedef struct { Elf32_Addr r_offset; Elf32_Word r_info; } Elf32_Rel; typedef struct { Elf32_Addr r_offset; Elf32_Word r_info; Elf32_Sword r_addend; } Elf32_Rela; MIPS是使用了第一种方式,所以,addend是存放在了将要被修改的位置,即需要进行重定位的地方。 2、执行如下命令, $ mipsel-linux-readelf -r sysconf.o Relocation section '.rel.rodata' at offset 0xb74 contains 132 entries: Offset Info Type Sym.Value Sym. Name 00000000 0000020c R_MIPS_GPREL32 00000000 .text 00000004 0000020c R_MIPS_GPREL32 00000000 .text 00000008 0000020c R_MIPS_GPREL32 00000000 .text 0000000c 0000020c R_MIPS_GPREL32 00000000 .text 00000010 0000020c R_MIPS_GPREL32 00000000 .text 可以看到,除了.text段中有需要进行重定位的地方以外,.rodata中也有一些,并且这些offset是对应于.rodata段。 $ mipsel-linux-objdump -Dr sysconf.o Disassembly of section .rodata: 00000000 <.rodata>: 0: ffffc064 0xffffc064 0: R_MIPS_GPREL32 .text 4: ffffc06c 0xffffc06c 4: R_MIPS_GPREL32 .text 8: ffffc074 0xffffc074 8: R_MIPS_GPREL32 .text c: ffffc07c 0xffffc07c c: R_MIPS_GPREL32 .text 10: ffffc084 0xffffc084 10: R_MIPS_GPREL32 .text 这里将.rodata中需要重定位的地方和对应的重定位项结合在一起打印出来。可以看出,对于.rodata中的第一个字,其保存的addend值为0xffffc064。 3、MIPS ABI文档如下描述R_MIPS_GPREL_32: Name Value Field Symbol Calculation R_MIPS_GPREL_32 12 T-word32 local A + S + GP0 - GP A Represents the addend used to compute the value of the relocatable field. S Represents the value of the symbol whose index resides in the relocation entry, unless the the symbol is STB_LOCAL and is of type STT_SECTION in which case S represents the original sh_addr minus the final sh_addr. GP Represents the final gp value to be used for the relocatable, executable, or shared object file being produced. GP0 Represents the gp value used to create the relocatable object. $ mipsel-linux-objdump -Dr sysconf.o Disassembly of section .reginfo: 00000000 <.reginfo>: 0: b200001e 0xb200001e ... 14: 00004000 sll t0,zero,0x0 .reginfo段中记录了ri_gp_value,可以看到,GP0的值为0x4000。 4、当链接成可执行程序时,依照同样的方法,我们可以找到 00400094 <.reginfo>: 400094: b20000f4 0xb20000f4 ... 4000a8: 10008a70 b 3e2a6c <__start-0x1d6a4> 所以,gp的值为0x10008a70, 00425c70 <__sysconf>: 425c70: 3c1c0fbe lui gp,0xfbe 所以,S的值为0x425c70, 根据公式“A + S + GP0 - GP”,可以计算出.rodata中需要重定位的地方的最终内容。比如,对于.rodata中的第一个字, 0xffffc064 + 0x425c70 + 0x4000 - 0x10008a70 = 0xf041d264 5、附注 如果查看一下binutils/bfd/elf32-mips.c,就可以找到R_MIPS_GPREL32相应的HOWTO数据结构,以及处理函数。 6、其它 这里,我个人有一个地方没有搞明白,MIPS ABI中介绍说S是初始的sh_addr减去最终的sh_addr,但是,看起来,似乎应该反过来才正确。 邢明杰