x86-64 AT&T语法汇编中*符号与RIP的用法相关疑问
AT&T语法x86-64汇编*符号与RIP使用问题解答
关于*符号的核心定义
首先明确:并非永远指代对应地址中存储的数据,它是AT&T汇编中专用于控制转移类指令的标识符,仅用来区分直接寻址和间接寻址,普通内存读写指令(如mov、add等)不需要加,本身就会读取地址对应的数据。
问题1:jmp*、call*指令的含义与常见用法
不带的jmp/call属于直接控制转移,汇编器会直接将操作数编码为相对当前指令的偏移量,执行时直接跳转到目标位置;带的jmp*/call*属于间接控制转移,操作数本身存储的是跳转目标的地址,执行时需要先读取操作数对应位置的内容作为目标地址再跳转。
常见用法示例:
- 寄存器间接寻址:
call *%rax
直接将rax寄存器中存储的值作为调用目标地址,对应C语言中普通函数指针调用的常见编译结果,比如void (*func)() = test; func();就会生成类似指令。 - 内存间接寻址:
jmp *(%rbx)
先读取rbx寄存器指向的内存地址中的8字节值(x86-64下地址长度为8字节),再将该值作为跳转目标地址,常用于函数指针数组的访问,比如call *func_arr(, %rdi, 8)就是取func_arr数组第rdi个元素存储的函数地址进行调用。 - 绝对内存间接寻址:
jmp *0x402000
直接读取固定内存地址0x402000处存储的8字节值作为跳转目标,仅在内核、裸机开发等地址固定的场景使用。
问题2:RIP相关寻址规则与操作方法
关于call label(%rip)的寻址逻辑
你感知到的「调用label对应的地址」是最终效果,但底层计算逻辑确实是RIP + 偏移量。
AT&T汇编中offset(%基址寄存器)的通用规则是计算「基址寄存器值 + offset」作为有效地址,label(%rip)里的label并不是符号的绝对地址,而是汇编器/链接器自动计算的「label地址与当前指令下一条指令地址的差值」(执行当前call指令时,RIP本身就指向当前指令的下一条指令地址),二者相加的结果刚好是label的绝对地址,所以最终效果是跳转到label处。
RIP的使用规则
x86-64架构中RIP的使用只有两类固定场景,没有例外:
- RIP相对内存寻址:就是上述
offset(%rip)的形式,所有用RIP作为基址的内存访问都遵循「RIP值 + offset」的计算规则 - 显式读取RIP值:x86-64没有直接读写RIP的指令,通用读取方式是
lea 0(%rip), %rax,lea指令仅计算有效地址不访问内存,所以执行后rax中存储的就是当前RIP的值。
实现rip+5操作的方法
分两种需求场景:
- 仅需要获取rip+5的值存到寄存器:直接写
lea 5(%rip), %rax即可,执行后rax中存储的就是当前RIP+5的结果 - 需要跳转到rip+5的位置:两种写法都可以实现
# 写法1:直接相对跳转,汇编器自动计算偏移 jmp .+5 # 写法2:先计算地址再间接跳转 lea 5(%rip), %rax jmp *%rax
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

