什么是 callq 指令?

What is callq instruction?

我有一些由工具生成的 x86_64 架构的 gnu 汇编代码,其中有这些说明:

movq %rsp, %rbp  
leaq str(%rip), %rdi
callq puts
movl [=10=], %eax

我找不到关于 "callq" 指令的实际文档。

我看过 http://support.amd.com/TechDocs/24594.pdf 也就是 "AMD64 Architecture Programmer’s Manual Volume 3: General-Purpose and System Instructions" 但他们只描述了 CALL near 和 far 指令。

我查看了 gnu 汇编程序的文档 https://sourceware.org/binutils/docs/as/index.html,但找不到详细说明它支持的指令的部分。

我知道这是对函数的调用,但我想知道详情。我在哪里可以找到它们?

只是call。如果您希望能够在 Intel/AMD 手册中查找说明,请使用 Intel 语法反汇编。

q 操作数大小后缀在技术上确实适用(它推送一个 64 位 return 地址并将 RIP 视为 64 位寄存器),但无法用指令前缀。即 calllcallw 在 64 位模式下不可编码,所以一些 AT&T 语法工具将其显示为 callq 而不是 call 只是令人讨厌。这当然也适用于 retq

不同的工具在 32 位和 64 位模式下是不同的。 (Godbolt)

  • gcc -S:总是 call/ret。不错
  • clang -S:callq/retqcalll/retl。至少它一直很烦人。
  • objdump -d: callq/retq(显式 64 位)和 call/ret(隐式 32 位) .不一致且有点愚蠢,因为 64 位无法选择操作数大小,但 32 位可以。 (不是 有用的 选择,但是:callw 将 EIP 截断为 16 位。)

    尽管另一方面,大多数指令在64位模式下的默认操作数大小(没有REX.W前缀)仍然是32。但是add , (%rdi) 需要操作数大小后缀;如果没有任何暗示,汇编程序不会为您选择 32 位。 OTOH,push 是隐含的 pushq,即使 pushw pushq 在 64 位模式下都是可编码的(and usable in practice)。


来自英特尔的指令集参考手册(上面链接):

For a near call absolute, an absolute offset is specified indirectly in a general-purpose register or a memory location (r/m16, r/m32, or r/m64). The operand-size attribute determines the size of the target operand (16, 32 or 64 bits). When in 64-bit mode, the operand size for near call (and all near branches) is forced to 64-bits.

for rel32 ... As with absolute offsets, the operand-size attribute determines the size of the target operand (16, 32, or 64 bits). In 64-bit mode the target operand will always be 64-bits because the operand size is forced to 64-bits for near branches.

在 32 位模式下,您可以编码将 EIP 截断为 16 位的 16 位 call rel16,或使用绝对 16 位地址的 call r/m16。但是正如手册所说,操作数大小在 64 位模式下是固定的。

callq 指的是共享 libraries/dynamic 库中的 relocatable 调用。这个想法是推 0,然后推符号进行搜索,然后调用一个函数,以便在第一次调用时搜索它。在程序的relocatable table中,在第一次调用函数时,替换掉对函数实际位置的调用。后续调用引用在 运行 时间创建的重定位 table。