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

为什么`%rip + displacement`寻址可以访问程序数据段?

x86-64汇编中ary(%rip)寻址模式的原理解释

你遇到的困惑核心是对x86-64新增的RIP相对寻址机制不熟悉,和内存布局认知没有直接关系。

两种取符号地址写法的本质区别

  • leaq ary, %rax 属于绝对地址寻址:链接阶段会直接把ary的完整64位虚拟地址硬编码到指令编码中,指令长度更长,运行时直接读取硬编码值就能拿到地址,不依赖寄存器计算。
  • leaq ary(%rip), %rbx 属于RIP相对寻址:这是x86-64架构下全局符号访问的默认寻址方式,地址计算公式为 目标地址 = RIP当前值 + 链接器预填充的偏移量,这里的RIP取值固定为当前执行指令的下一条指令的地址。

RIP相对寻址能访问数据段的核心逻辑

程序编译链接完成后,同一个可执行文件内各个段的相对偏移是固定的,和程序加载到内存的基址无关:

  1. 链接阶段,链接器会提前计算好ary符号所在位置,与当前leaq指令下一条指令位置的差值,把这个32位有符号差值作为位移填到指令的寻址字段,不需要存储完整8字节绝对地址,生成的指令体积更小。
  2. 程序运行时,RIP(指令指针寄存器)始终指向待执行的下一条指令地址,无论操作系统把整个程序映射到虚拟内存的哪个起始地址,代码段和数据段的相对距离都不会变化,用RIP值加上预计算的固定偏移,就能精准定位到数据段中ary数组的首地址,最终执行效果和绝对地址加载完全一致。

RIP相对寻址是位置无关代码(PIC)的标准实现方式,程序加载时不需要对这类指令做地址重定位,加载和运行效率远高于绝对寻址,这也是你用gcc -S生成的汇编代码中,绝大多数全局符号访问都采用符号(%rip)写法的原因。

对你的测试代码的验证说明

你编写的测试代码中,两条leaq指令执行完成后,%rax和%rbx存储的值完全相等,都是ary数组的首地址:

.section .data
ary:
    .int 1, 2, 3, 4, 5, 6

.section .text
.globl  _start

_start:
    leaq ary, %rax       # 绝对地址加载ary首地址到rax
    leaq ary(%rip), %rbx # RIP相对寻址加载ary首地址到rbx,结果与rax完全一致

    movq $60, %rax
    syscall

你可以通过gdb对编译后的程序做调试,在_start入口设置断点单步执行,执行完两条加载指令后打印两个寄存器的值,就能验证结果一致性。

补充说明:你之前理解的ary(, %rdi, 4)属于绝对地址下的比例变址寻址,当%rdi取值为0时确实等价于直接加载ary的绝对地址,这是32位x86时代延续下来的寻址写法,在64位模式下如果寻址表达式中不出现%rip,默认就会按绝对地址处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 08:54:21