mil*_*bos 3 x86 assembly x86-64 gnu-assembler att
有了这个简单的组装:
.text
.globl main
str:
.asciz "%i\n"
add:
push %rbp
mov %rsp, %rbp
movl %esi, %eax
addl %edi, %eax
pop %rbp
ret
main:
mov $1, %esi
mov $2, %edi
lea add(%rip), %rax
call %rax #what is wrong here? the rax should contain first instruction of add
mov %eax, %esi
xor %eax, %eax
lea str(%rip), %rdi
call printf
xor %eax, %eax
ret
Run Code Online (Sandbox Code Playgroud)
我收到错误:
foo.s:17: Warning: indirect call without `*'
Run Code Online (Sandbox Code Playgroud)
为什么?应该%rax
包含函数的地址(如注释中所示),这不是c
,其中有带有 的指针*
,而是包含地址的寄存器。那么这里出了什么问题呢?
改变
call %rax
Run Code Online (Sandbox Code Playgroud)
到
call *%rax
Run Code Online (Sandbox Code Playgroud)
这与 不一样call *(%rax)
。来自@interjay 下面的评论:
call *%rax
调用存储在的地址rax
(这就是OP想要做的),而call *(%rax)
调用存储在指向的内存中的地址rax
星号表示该调用是间接调用。这意味着调用函数存储在里面%rax
AT&T 语法始终需要*
间接调用,例如call *%rax
. 这设置 RIP = RAX。
call *(%rax, %rdi)
将从内存加载,如 RIP = mem_load(RDI+RAX)。将使用 RIP 相对寻址模式
call *foo(%rip)
从内存中的地址加载新的 RIP 。foo
当它缺少*
但仍然明确是间接调用时(由于寄存器操作数或寻址(%reg)
模式),汇编器将推断这一点并警告您。
AT&T 语法设计避免了call foo
(直接)和call *foo
(具有绝对直接寻址模式的内存间接)之间的歧义。
通常在 64 位模式下您会使用,但如果可能意味着内存间接,call *foo(%rip)
那么歧义仍然存在。call foo
当然,语法是在 AMD64 出现之前设计的。
在 Intel 语法中,将指针从foo
具有绝对寻址的内存地址加载到 EIP/RIP 的内存间接调用将是:
GAS 英特尔语法:call [foo]
、call qword ptr foo
、 或call ds:foo
MASM:call [foo]
可能会忽略括号,因此只能qword ptr
工作ds:
。
NASM:call [foo]
或call qword [foo]
正如您所看到的,MASM 风格的 Intel 语法(由 GAS 使用.intel_syntax noprefix
)使用ds:
或qword ptr
来指示某物是内存操作数,从而允许直接call foo
进行正常E8 rel32
调用。
当然,正如我所说,在 64 位模式下,您通常希望call [RIP + foo]
在 GAS 或call [rel foo]
NASM 中使用。(在实际的 MASM 中,我认为 RIP-relative 是默认启用的。)