警告:不带“*”的间接调用

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,其中有带有 的指针*,而是包含地址的寄存器。那么这里出了什么问题呢?

Waq*_*med 6

改变

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

  • 这是不正确的。`call *%rax` 调用存储在 `rax` 中的地址(这就是 OP 想要做的),而 `call *(%rax)` 调用存储在 `rax` 指向的内存中的地址。 (4认同)

Pet*_*des 5

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 是默认启用的。)