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

ARM64 v8架构下获取overwriteFunc地址为何返回PLT表地址而非真实地址

问题解答

现象本质

你获取到的0x423A90是**过程链接表(PLT, Procedure Linkage Table)**中overwriteFunc对应的表项地址,并非函数本身的实现地址,这是类Unix系统动态链接机制的标准设计,和ARM64架构本身的指令特性也有关,不属于异常问题。

具体原因拆解

1. PLT存在的核心作用

当overwriteFunc定义在外部动态共享库中时,编译期编译器无法确定其运行时的真实内存地址,因此引入PLT作为中间跳转层:

  • 所有对外部函数的调用、地址获取都会先指向对应PLT项,保证编译阶段生成的代码是位置无关的,不需要修改共享库本身的代码就能加载到任意内存地址运行。
  • 配合GOT(全局偏移表)实现延迟绑定:只有函数第一次被调用时才会触发动态链接器解析函数真实地址,并回填到GOT对应表项中,后续调用直接读取GOT里缓存的地址跳转即可,大幅降低程序启动时的重定位开销。

2. 对应PLT汇编逻辑解析

0x0000000000423a90 <+0>:    adrp    x16, 0x513000    ; 计算GOT表所在页的基地址
0x0000000000423a94 <+4>:    ldr     x17, [x16,#1640] ; 从GOT对应表项加载函数地址
0x0000000000423a98 <+8>:    add     x16, x16, #0x668 ; 计算重定位参数的地址,供第一次解析用
0x0000000000423a9c <+12>:   br      x17              ; 跳转到加载的地址

第一次调用时GOT表项中存储的是PLT公共解析 stub 的地址,br x17会跳转到解析逻辑完成地址解析和回填;后续调用时GOT表项已经存了overwriteFunc的真实地址,直接跳转即可。

3. 为什么需要两次跳转而非一次

这个设计是动态链接需求和ARM64指令集特性共同决定的:

  • 首先是延迟绑定的需求:必须有中间层来判断是否已经完成地址解析,第一次调用需要跳转到解析逻辑,后续调用跳转到真实函数,两次跳转是实现这个分支逻辑的必要结构。
  • 其次是ARM64指令集的限制:ARM64采用定长4字节指令,单条指令无法直接加载64位的立即数地址,也无法完成跨大地址范围的直接跳转,必须通过多次读取内存、寄存器中转才能完成跳转,因此PLT的实现天然需要多条指令完成地址加载+跳转的逻辑。
    如果不需要延迟绑定,可以在编译时添加-fno-plt参数关闭PLT机制,编译器会直接通过GOT表调用外部函数,此时函数地址访问只需要一次跳转即可。

内容的提问来源于stack exchange,提问作者Qiyang Li

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 06:18:00