ELF - 了解 R_386_PC32 重定位

ELF - Understanding R_386_PC32 relocations

我正在尝试了解 ELF 中的重定位,但我在阅读这方面的文档时遇到了一些问题,该文档相当神秘。例如,重定位方程描述了 3 个参数,S、A 和 P。现在我知道 A 只是加数,它是一些用于帮助重定位计算的数字,S 是 "Value of the symbol whose index resides in the relocation entry"(和函数名一样吧?)但是P呢?手册将其描述为 "the place of the storage unit being relocated" 但这到底是什么意思?

我刚找到一个例子来说明这一点:假设我们有 2 个目标文件,obj1.oobj2.o.第一个引用一个名为 foo() 的函数,它位于 obj2.o.

objdump -d obj1.o 产量:

Disassembly of section .text:
00000000 <func>:
0:   55                      push   %ebp
1:   89 e5                   mov    %esp,%ebp
3:   83 ec 08                sub    [=11=]x8,%esp
6:   e8 fc ff ff ff          call 7 <func+0x7>
b:   c9                      leave  
c:   c3                      ret   

现在,readelf 显示这是一个 R_386_PC32 重定位,其方程为:S + A - P.

将两个文件合并生成一个完整的可执行文件后,重定位,重定位条目显然已修补:

objdump -d relocated
test:     file format elf32-i386
Disassembly of section .text:
080480d8 <func>:
80480d8:   55                      push   %ebp
80480d9:   89 e5                   mov    %esp,%ebp
80480db:   83 ec 08                sub    [=12=]x8,%esp
80480de:   e8 05 00 00 00          call   80480e8 <foo>
80480e3:   c9                      leave  
80480e4:   c3                      ret    
80480e5:   90                      nop
80480e6:   90                      nop
80480e7:   90                      nop
080480e8 <foo>:
80480e8:   55                      push   %ebp
80480e9:   89 e5                   mov    %esp,%ebp
80480eb:   5d                      pop    %ebp
80480ec:   c3                      ret

因此链接器似乎执行了以下计算:S + A – P: 0x80480e8 + 0xfffffffc – 0x80480df

我的问题是:

P是Program-Counter,所以这是PC相关的重定位。我没有检查 ELF32 究竟使用什么作为参考点。从未链接的call rel32中的fc ff ff ff = -4来看,应该是4字节位移的开始。在机器代码中,相对跳转像 call rel32 指令的 end 作为基础(即下一条指令的开始),这样就可以解释 4 字节的偏移量。

这是加数的一个用例。

另一种是对静态数据进行相对于 PC 的寻址,以生成与位置无关的代码。您的 PC 引用可能就在附近,但甚至不在您使用它的指令中,或者您想要索引一个全局数组。

所以你可能有类似

call get_eip_into_ebx
mov $table - this_instruction + 40(%ebx), %ecx

或者对于 真实 示例,look at what gcc and clang do 对于 -m32 -PIE 加载全局。 (但是全局偏移 table 符号名称得到特殊处理,所以我不打算重现编译器 asm 输出。)

S = 0x80480e8为符号起始地址(foo()的入口地址) P = 0x80480df 为需要修改(重定位)值的地址

所以S-P就是这两个地址之间的距离(以字节为单位)

然而,调用rel32指令计算从下一条指令开始的距离(rel32),并且与P有4个字节的偏移量,所以-4。