汇编 - x86 调用指令和内存地址?

hal*_*ton 6 x86 assembly linker masm nasm

我一直在阅读一些汇编代码,并且开始看到调用指令实际上是与程序计数器相关的。

但是,每当我使用 Visual Studio 或 windbg 进行调试时,它总是说 call 0xFFFFFF ... 这对我来说意味着它说我要跳转到那个地址。

谁是对的?Visual Studio 是否隐藏了指令编码的复杂性,只是说哦,这就是程序的意思,也就是说调试器知道它是一个与 pc 相关的指令,并且因为它知道 pc,它只是去为你做数学运算?

高度混乱。

Pet*_*des 7

如果您正在反汇编.o尚未链接的目标文件,则调用地址将只是一个由链接器填写的占位符。

您可以使用objdump -drwc -Mintel,以显示从搬迁类型+符号名.o (该-r选项是关键,或者-R一个已经连接的共享库。)


对于用户来说,显示跳转目标的实际地址更有用,而不是将其反汇编jcc eip-1234H为什么。目标文件有一个默认的加载地址,所以反汇编器eip在每条指令处都有一个值,这通常出现在反汇编输出中。

例如,在我编写的一些汇编代码中(我使用符号名称将其放入目标文件中,因此循环分支目标实际上对反汇编程序可见):

objdump -M intel  -d rs-asmbench:
...
00000000004020a0 <.loop>:
  4020a0:       0f b6 c2                movzx  eax,dl
  4020a3:       0f b6 de                movzx  ebx,dh
   ...
  402166:       49 83 c3 10             add    r11,0x10
  40216a:       0f 85 30 ff ff ff       jne    4020a0 <.loop>

0000000000402170 <.last8>:
  402170:       0f b6 c2                movzx  eax,dl
Run Code Online (Sandbox Code Playgroud)

请注意,jne指令的编码是字节的有符号小端 32 位位移-0xD0。(跳转e/rip后将它们的位移加到 的值上。跳转指令本身有 6 个字节长,因此位移必须是-0xD0,而不仅仅是-0xCA.) 0x100 - 0xD0 = 0x30,这是 2 的补码位移的最低有效字节的值。

在您的问题中,您谈论的是调用地址是0xFFFF...,除非那只是一个占位符,否则这毫无意义,或者您认为0xFF位移中的非字节是操作码的一部分。

在链接之前,对外部符号的引用如下所示:

objdump -M intel -d main.o
  ...
  a5:   31 f6                   xor    esi,esi
  a7:   e8 00 00 00 00          call   ac <main+0xac>
  ac:   4c 63 e0                movsxd r12,eax
  af:   ba 00 00 00 00          mov    edx,0x0
  b4:   48 89 de                mov    rsi,rbx
  b7:   44 89 f7                mov    edi,r14d
  ba:   e8 00 00 00 00          call   bf <main+0xbf>
  bf:   83 f8 ff                cmp    eax,0xffffffff
  c2:   75 cc                   jne    90 <main+0x90>
  ...
Run Code Online (Sandbox Code Playgroud)

请注意call指令的相对位移 = 0。因此,在链接器插入实际相对值之前,它们会call在调用后立即使用指令的目标对 a 进行编码。(即RIP = RIP+0)。在call bf后面紧跟着的是在开始的指令0xbf从节开始。另一个call具有不同的目标地址,因为它位于文件中的不同位置。(gcc 放入main了它自己的部分:).text.startup

因此,如果您想了解实际调用的内容,请查看链接的可执行文件,或获取查看目标文件符号的反汇编程序,以插入调用目标的符号名称,而不是将它们显示为具有零位移的调用。

在链接之前已经解决了到本地符号的相对跳转:

objdump -Mintel  -d asm-pinsrw.o:
0000000000000040 <.loop>:
  40:   0f b6 c2                movzx  eax,dl
  43:   0f b6 de                movzx  ebx,dh
  ...
 106:   49 83 c3 10             add    r11,0x10
 10a:   0f 85 30 ff ff ff       jne    40 <.loop>
0000000000000110 <.last8>:
 110:   0f b6 c2                movzx  eax,dl
Run Code Online (Sandbox Code Playgroud)

请注意,即使文件没有基地址,相对跳转到同一文件中的符号的指令编码完全相同,因此反汇编程序只是将其视为零。

有关指令编码,请参阅 Intel 的参考手册。https://stackoverflow.com/tags/x86/info 上的链接。即使在 64 位模式下,也call仅支持 32 位符号扩展的相对偏移量。支持 64 位地址作为绝对地址。(在 32 位模式下,支持 16 位相对地址,带有操作数大小前缀,我猜节省了一个指令字节。)