assembly 什么是callq指令?

ttp71kqs  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(337)

我有一些工具生成的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汇编程序https://sourceware.org/binutils/docs/as/index.html的文档,但找不到详细说明它支持的指令的部分。
我知道这是一个函数调用,但我想知道细节。我在哪里可以找到它们?

ycl3bljg

ycl3bljg1#

它只是call。如果您希望能够在Intel/AMD手册中查找指令,请使用Intel语法反汇编。(objdump -drwC -Mintel,GBD set disassembly-flavor intel,GCC -masm=intel
从技术上讲,q操作数大小后缀确实适用(它推送64位返回地址,并将RIP视为64位寄存器),但无法使用指令前缀覆盖它。例如,根据英特尔手册,calllcallw在64位模式下不可编码,所以有些AT&T的语法工具将其显示为callq而不是call,这很烦人。
不同的工具在32位和64位模式下是不同的。(Godbolt)

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

另一方面,尽管64位模式下 * 大多数 * 指令的默认操作数大小(不带雷克斯.W前缀)仍然是32。但是add $1, (%rdi)需要操作数大小后缀;即使pushw $1pushq $1在64位模式下都是可编码的(实际上也是可用的),但是push隐含地是pushq
64位模式下的GAS会将callw foo/foo:组装为66 e8 00 00,但我的Skylake CPU将其单步执行为6字节指令,在其后面消耗了2字节的00。并将RSP更改为8。因此,它将其解码为callq,带有rel32=0。忽略66操作数大小前缀。因此,即使没有操作数大小选项,GNU Binutils也认为有。(使用GAS 2.38测试)。因此,它在64位模式下使用后缀而不是32位模式仍然很奇怪,因为它认为两种模式下的情况是相同的。
Clang和llvm-objdump -d有相同的bug,在64位模式下组装/反汇编callw
AMD's manual表示64位模式不能使用32位操作数大小,但 * 没有 * 提到对使用16位操作数大小的任何限制。因此GAS和LLVM对于AMD CPU可能是正确的,并且66前缀的选择还是一样的,(您可以通过在静态可执行文件中单步执行callw foo/foo:(而不是0x401006)之后查看RIP是否= 0x1004来进行测试,其中.text部分从0x401000开始。)
NASM的ndisasm -b64假设66前缀在64位模式下将被忽略,将66E800000000反汇编为call qword 0x18c(它不理解ELF元数据,所以我只是用nops填充,并在a .o的反汇编中发现它,就好像它是一个平面二进制文件一样,因此有了不寻常的地址。)
来自英特尔的指令集参考手册(上面链接):
对于近调用绝对值,绝对偏移量是在通用寄存器或内存位置(r/m16,r/m32或r/m64)中间接指定得.操作数大小属性确定目的操作数得大小(16,32或64位).在64位模式下,近调用(及所有近分支)得操作数大小强制为64位.
for rel 32...与绝对偏移量一样,操作数大小属性确定目的操作数得大小(16,32或64位).在64位模式中,目的操作数始终为64位,因为近分支得操作数大小强制为64位.
在32位模式下,您可以对将EIP截断为16位的16位call rel16进行编码,也可以对使用绝对16位地址的call r/m16进行编码。但正如手册中所述,在64位模式下操作数大小是固定的。
这与push得情况不同,push在64位模式下默认为64位,但可以使用操作数大小前缀将其覆盖为16(但雷克斯.W=0时不能覆盖为32).因此,pushqpushw都可用,但只有callq可用.

deyfvvtc

deyfvvtc2#

callq指的是共享库/动态库中的可重定位调用。其思想是push 0,然后push要搜索的符号,然后调用一个函数,以便在第一次调用时搜索它。在程序的可重定位表中,它在第一次调用函数时将调用替换为函数的实际位置。随后的调用将引用在运行时创建的重定位表。

相关问题