关于R_X86_64_PLT32地址计算及静态函数PLT标记的技术咨询
先看你写的这段C++代码:
#include "a.h" int Other() { return 1; } int SomeFunction() { Other(); Other(); Other(); return 0; }
还有你用readelf -r a.o得到的重定位表:
Relocation section '.rela.text' at offset 0x240 contains 3 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000018 000900000004 R_X86_64_PLT32 0000000000000000 _Z5Otherv - 4 00000000001d 000900000004 R_X86_64_PLT32 0000000000000000 _Z5Otherv - 4 000000000022 000900000004 R_X86_64_PLT32 0000000000000000 _Z5Otherv - 4 Relocation section '.rela.eh_frame' at offset 0x288 contains 2 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0 000000000040 000200000002 R_X86_64_PC32 0000000000000000 .text + f
接下来逐个解决你的问题:
1. 此场景下L、A、P的取值分别是什么?
先明确R_X86_64_PLT32公式L + A - P里每个参数的定义,结合你的目标文件场景:
- P:重定位条目所在的内存地址(也就是
Other()调用指令的位置)。在你的a.o里,就是重定位表中每个条目的Offset值,比如第一个调用点是0x18,第二个是0x1d,第三个是0x22。这是目标文件中的偏移,链接成可执行文件后会变成实际的虚拟地址。 - A:重定位条目中的
Addend,也就是你看到的-4。这个值是编译器预先计算的修正值——因为x86_64的call指令是相对寻址,指令本身长度为4字节,减4是为了修正偏移,保证跳转地址计算准确。 - L:符号
Other()的最终虚拟地址。在目标文件阶段,这个值是0(还没经过链接,符号的实际地址尚未确定);当链接成可执行文件后,L会被替换成Other()函数在内存中的实际加载地址。
这个公式的作用是计算call指令需要的相对偏移:最终指令的相对地址 = 符号地址 + 修正值 - 当前指令地址,确保CPU能正确跳转到目标函数。
2. PLT用于动态对象,为何静态函数Other()会被标记为PLT32?
这和你编译代码的选项直接相关——你大概率用了**位置无关代码(PIC)**的编译选项(比如-fPIC)。
在PIC模式下,编译器生成的代码需要能被加载到任意内存地址,不能直接使用绝对地址调用函数。哪怕是当前编译单元内的静态函数,为了保证代码的位置无关性,编译器也会生成通过PLT间接调用的指令(对应R_X86_64_PLT32重定位)。这样不管程序被加载到哪个地址,都能通过PLT/GOT表找到函数的实际地址。
如果不想让静态函数走PLT,可以尝试去掉-fPIC选项编译,此时编译器会生成直接调用的指令,重定位类型也会变成R_X86_64_PC32之类的非PLT类型。
3. 如何查找ELF文件的PLT表及其中的Other()?
要注意:目标文件(比如你的a.o)还没有PLT表——PLT是链接阶段生成的,只有最终的可执行文件或共享库才有PLT表。具体操作步骤如下:
步骤1:链接生成可执行文件
假设你的目标文件是a.o,用gcc链接成可执行文件a.out:
gcc a.o -o a.out
步骤2:查看PLT段的位置
用readelf -S a.out可以看到所有段信息,找到.plt段(可能还有.plt.got),示例输出如下:
Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align ... [13] .plt PROGBITS 0000000000401020 00001020 0000000000000030 0000000000000010 AX 0 0 16 ...
步骤3:反汇编PLT表,找到Other()的条目
用objdump -d --plt a.out可以反汇编PLT表的内容,你会看到_Z5Otherv(Other()的C++ mangled名称)对应的PLT条目:
0000000000401040 <_Z5Otherv@plt>: 401040: ff 25 e2 2f 00 00 jmp *0x2fe2(%rip) # 404028 <_GLOBAL_OFFSET_TABLE_+0x20> 401046: 68 00 00 00 00 pushq $0x0 40104b: e9 e0 ff ff ff jmp 401030 <.plt>
步骤4:验证重定位信息(可选)
用readelf -r a.out可以看到链接后的重定位项,此时_Z5Otherv的重定位会指向PLT相关的地址,不再是目标文件里的0。
另外,如果是静态链接的可执行文件,PLT表可能不会生成(因为所有函数地址都是确定的),此时直接用objdump -d a.out就能找到Other()的直接调用指令。
内容的提问来源于stack exchange,提问作者Jaebum

