【问题标题】:What is callq instruction?什么是callq指令?
【发布时间】:2018-03-26 22:48:44
【问题描述】:

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

movq %rsp, %rbp  
leaq str(%rip), %rdi
callq puts
movl $0, %eax

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

我查看了http://support.amd.com/TechDocs/24594.pdf,这是“AMD64 架构程序员手册第 3 卷:通用和系统指令”,但它们只描述了 CALL 近和远指令。

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

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

【问题讨论】:

  • @prl:我没有找到关于callq 的任何现有问题。关于分支和其他堆栈指令的默认操作数大小,实际上有一些不明显的东西要说。这是一个新手问题,但实际上,如果我不再对所有糟糕的“我的代码不起作用,我什么都不知道”的问题感到脾气暴躁。

标签: assembly x86 x86-64 att


【解决方案1】:

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

q 操作数大小后缀在技术上确实适用(它推送 64 位返回地址并将 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 $1, (%rdi) 需要操作数大小后缀;如果没有任何暗示,汇编程序不会为您选择 32 位。 OTOH,push 隐含为 pushq,尽管 pushw $1pushq $1 在 64 位模式下都是可编码的 (and usable in practice)。


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

对于近调用绝对值,绝对偏移量在通用寄存器或内存位置(r/m16、r/m32 或 r/m64)中间接指定。 操作数大小属性确定目标操作数的大小(16、32 或 64 位)。 在 64 位模式下,近调用(以及所有近分支)的操作数大小被强制为 64 位

for rel32 ... 与绝对偏移量一样,operand-size 属性确定目标操作数的大小(16、32 或 64 位)。 在 64 位模式下,目标操作数将始终为 64 位,因为近分支的操作​​数大小被强制为 64 位。

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 您描述的是 Unix 系统传统上用于动态链接的 PLT (macieira.org/blog/2012/01/…)。这是可选的:gcc -fno-plt 进行早期绑定,内联间接callq *puts@GOT(%rip)callq 助记符并不意味着使用 PLT,甚至用于同一文件中的函数之间的调用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-20
    • 2020-12-10
    • 2012-12-02
    • 2010-09-10
    相关资源
    最近更新 更多