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

Warning: indirect call without `*'

有了这个简单的组装:

.text
    .globl main
str:
    .asciz "%i\n"
add:
    push %rbp
    mov %rsp, %rbp
    movl %esi, %eax
    addl %edi, %eax
    pop %rbp
    ret

main:
    mov , %esi
    mov , %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 calls the address stored in rax (which is what OP wants to do), while call *(%rax) calls the address stored in memory pointed to by rax

星号表示调用是间接调用。意思是调用存储在%rax

里面的函数

AT&T 语法在间接调用中始终需要 *,例如 call *%rax。这会设置 RIP = RAX。
call *(%rax, %rdi) 将从内存中加载,如 RIP = mem_load(RDI+RAX).
call *foo(%rip) 将使用 RIP 相对寻址模式从内存中地址 foo 加载新的 RIP。

当它缺少 * 但仍然是一个明确的间接调用(因为寄存器操作数或 (%reg) 寻址模式)时,汇编器将推断并警告您。


AT&T 语法设计避免了 call foo(直接)和 call *foo(具有绝对直接寻址模式的间接内存)之间的歧义

通常在 64 位模式下你会使用 call *foo(%rip),但如果 call foo 可能意味着内存间接,那么歧义仍然存在。当然语法是在 AMD64 存在之前设计的。

在 Intel 语法中,使用绝对寻址从地址 foo 处的内存将指针加载到 EIP/RIP 的内存间接调用是:

  • GAS 英特尔语法:call [foo]call qword ptr foocall ds:foo

  • MASM:call [foo] 可能 ignore the brackets 所以只有 qword ptrds: 有效。

  • NASM: call [foo]call qword [foo]

正如您所见,MASM 风格的 Intel 语法(GAS .intel_syntax noprefix 使用的)使用 ds:qword ptr 来指示某物是内存操作数,允许call foo 正常 E8 rel32 直接调用。

当然,正如我所说,在 64 位模式下,您通常希望在 GAS 中使用 call [RIP + foo] 或在 NASM 中使用 call [rel foo]。 (在实际的 MASM 中,我认为 RIP-relative 是默认打开的。)