以下一行代码是什么意思:
...
401147: ff 24 c5 80 26 40 00 jmpq *0x402680(,%rax,8)
...
在内存地址前面的星号代表什么意思?此外,当内存访问方法缺少第一个寄存器值时,这是什么意思?
通常是类似于(“%register”,%rax,8),但在这种情况下,它没有第一个寄存器。
有什么提示吗?
以下一行代码是什么意思:
...
401147: ff 24 c5 80 26 40 00 jmpq *0x402680(,%rax,8)
...
在内存地址前面的星号代表什么意思?此外,当内存访问方法缺少第一个寄存器值时,这是什么意思?
通常是类似于(“%register”,%rax,8),但在这种情况下,它没有第一个寄存器。
有什么提示吗?
q
表示四元组等)%
为前缀,立即值以 $
为前缀DISP(BASE, INDEX, SCALE)
的形式(DISP + BASE + INDEX * SCALE)*
表示(与直接操作数相对应)。jmpq
跳转到存储在 %rax * 8 + 0x402680
中的绝对地址,其长度为四元组。
jmp foo
) 和 RIP = 从某个符号地址装载(jmp *foo
)。请记住,movl $1, foo
是一个对绝对地址 foo
进行存储的操作。jmp %rax
或 jmp 24(%rax)
或者其他任何不是裸符号名称的东西,GAS会推断并警告关于没有 *
的间接跳转。)。jmp *foo(%rip)
将全局变量加载到RIP中,而不是像 jmp *foo
这样使用32位绝对地址。但这种可能性存在,并且在AT&T语法设计之前,在x86-64之前,这是正常的做事方式。)实际上这是计算表跳转,其中0x402680是表的地址,rax是8字节(qword)指针的索引。
将东西转换为英特尔语法总是可以使事情更清晰:
FF24C5 80264000 JMP QWORD PTR [RAX*8+402680]
jmpq
是一个无条件跳转到指定地址的指令。'q' 表示处理的是 quad words (64 位长)。
*0x402680(,%rax,8)
: 这是在x-86汇编中表示地址的一种方式。您说得没错,通常在第一个逗号前面有一个寄存器,但如果未指定寄存器,则仍遵循相同的规则。D(reg1, reg2, scalingFactor)
,其中 D 代表位移(displacement)。位移基本上就是一个整数。reg1
是第一个或基础寄存器。 reg2
是第二个寄存器,scalingFactor
是 2、4、8 中的一个(也许还有 1,但我不确定)。现在,您可以按照以下方式通过简单相加来获得地址:位移 + (reg1
中的值)+ scalingFactor
*(reg2
中的值)。jmp foo
将设置RIP为foo。jmp *foo
将设置RIP为从该内存地址加载的结果。(当然,在64位代码中,您通常使用RIP相对寻址来访问静态存储,因此jmp *foo(%rip)
会解引用全局变量foo,假设它保存了一个函数指针,并且这是一个尾调用。 - Peter Cordes这是一个跳转到存储在内存中的地址的指令。该地址存储在内存地址rax*8+0x402680
处,其中rax
是当前rax
值(当此指令执行时)。
jmpq *0x402680(,%rax,8)
RIP <- M[0x402680 + (8 * RAX)]
其中M
是系统内存。
因此,我们可以编写通用格式jmpq *c(r1, r2, k)
,其中c
是立即常量,r1
和r2
是通用寄存器,k
可以是1(默认)、2、4或8:
RIP <- M[c + r1 + (k * r2)]
最简单的例子
为了让事情更清晰:
.data
# Store he address of the label in the data section.
symbol: .int label
.text
# Jumps to label.
jmp *symbol
label:
如果没有*
,它会跳转到.data
部分中symbol
的地址并且崩溃。
我觉得这个语法有点不一致,因为对于大多数指令:
mov symbol, %eax
mov label, %eax
已经移动了地址为symbol
的数据,$symbol
用于该地址。在这一点上,Intel语法更加一致,因为它总是使用[]
进行解引用。
*
当然是C中解引用操作符*ptr
的助记符。
jmp foo
会导致CPU将foo
处的字节作为代码加载(通过设置RIP)。而mov foo,%eax
则将foo处的字节加载到寄存器中。我不确定使用jmp $foo
进行直接相对跳转的语法设计是否好,因为通常$
修饰符仅适用于(绝对)立即数,而不是相对编码。.long foo
发出符号的地址。我们真的不希望jmp foo
成为内存间接RIP = load(foo)
的语法;那是一个可怕的失败模式。 - Peter Cordesjmp
指令与其他指令的参数处理方式不同;它仅支持内存间接跳转的寻址模式,因此没有必要在没有 *
的情况下消除立即数和其他内容之间的歧义。 - Peter Cordes
*
实际上意味着间接引用,就像C语言的解引用运算符一样。考虑jmp foo
(RIP=foo)和jmp *foo
(RIP=foo所指向的内存内容)。当然,通常使用RIP相对寻址来访问代码指针变量foo,例如jmp *foo(%rip)
,但是foo
与*foo
很好地展示了为什么AT&T语法需要用*
来消除歧义。 - Peter Cordes