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
我收到错误:
foo.s:17: Warning: indirect call without `*'
为什么?应该%rax包含函数的地址(如注释中所示),这不是c,其中有带有 的指针*,而是包含地址的寄存器。那么这里出了什么问题呢?
改变
call %rax
到
call *%rax
这与 不一样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 是默认启用的。)