【问题标题】:Warning: indirect call without `*'警告:没有“*”的间接调用
【发布时间】:2020-10-29 23:52:53
【问题描述】:

有这个简单的组装:

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

【问题讨论】:

  • AT&T 语法包含很多荒谬的规则,其中之一就是这里需要*。只需添加*,或切换到理智的英特尔语法。
  • 根据英特尔文档,call 指令接受地址。无论是否有星号,我都在rax 中提供
  • 英特尔文档没有描述 AT&T 语法。
  • @interjay 在 nasm 中看起来如何?
  • 只需call rax。

标签: assembly x86 x86-64 gnu-assembler att


【解决方案1】:

AT&T 语法在间接调用中始终需要*,例如call *%rax。这将设置 RIP = RAX。
call *(%rax, %rdi) 将从内存中加载,如 RIP = mem_load(RDI+RAX)。
call *foo(%rip) 将从内存中的地址 foo 加载一个新的 RIP,使用 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 foo 或 call ds:foo

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

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

如您所见,MASM 风格的 Intel 语法(由 GAS 使用 .intel_syntax noprefix)使用 ds: 或 qword ptr 来指示某物是内存操作数,从而允许 call foo 成为普通的 @ 987654346@直接拨打。

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

【讨论】:

    【解决方案2】:

    改变

    call %rax
    

    到

    call *%rax
    

    这与call *(%rax) 不同。来自@interjay 下面的评论:

    call *%rax调用存储在rax中的地址(这是OP想要做的),而call *(%rax)调用存储在rax指向的内存中的地址

    星号表示调用是间接调用。表示存储在%rax中的调用函数

    【讨论】:

    • 和内存地址一样吗?如果函数第一条指令不在rax 中,但可以说在0xdeadbeef 中?我可以call *$deadbeef吗?
    • 这是不正确的。 call *%rax调用存储在rax中的地址(这是OP想要做的),而call *(%rax)调用存储在rax指向的内存中的地址。
    • 我实际上尝试过使用 *%rax,但随后添加了样式括号。我不知道这有什么不同。谢谢!我会修复答案
    • 这个答案可能是错误的。 x86-64 上的 * 与直接/间接无关。它曾经是 32 位 x86,但对于 64 位来说不是。
    猜你喜欢
    • 1970-01-01
    • 2011-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-27
    • 1970-01-01
    • 1970-01-01
    • 2020-10-03
    相关资源
    最近更新 更多