You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于R_X86_64_PLT32地址计算及静态函数PLT标记的技术咨询

ELF重定位与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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 17:02:47