汇编 - x86 调用指令和内存地址?
Assembly - x86 call instruction and memory address?
我一直在阅读一些汇编代码,我开始看到调用指令实际上是程序计数器相关的。
然而,每当我使用 visual studio 或 windbg 进行调试时,它总是说调用 0xFFFFFF ...对我来说这意味着它说我要跳转到那个地址。
谁是对的? Visual Studio 是否隐藏了指令编码的复杂性,只是说哦,这就是程序的意思,调试器知道它是一个 pc 相关的指令,并且因为它知道 pc,所以它只是去计算你?
非常困惑。
如果您要反汇编 .o
尚未链接的目标文件,调用地址将只是一个占位符,由链接器填写。
您可以使用 objdump -drwc -Mintel
来 显示 来自 .o
的重定位类型 + 符号名称( -r
选项是关键。或者 -R
用于已链接的共享库。)
显示跳转目标的实际地址对用户更有用,而不是将其反汇编为jcc eip-1234H
之类的。目标文件有一个默认的加载地址,所以反汇编程序在每条指令中都有一个 eip
的值,这通常出现在反汇编输出中。
例如在我写的一些 asm 代码中(我使用符号名称进入目标文件,因此循环分支目标实际上对反汇编程序可见):
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
请注意,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>
...
请注意 call
指令如何使它们的相对位移 = 0。因此,在链接器插入实际相对值之前,它们会在 call
之后用指令的目标进行编码称呼。 (即 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
请注意,相对跳转到同一文件中符号的完全相同的指令编码,即使该文件没有基地址,所以反汇编程序只是将其视为零。
参见英特尔的指令编码参考手册。 https://whosebug.com/tags/x86/info 处的链接。即使在 64 位模式下,call
也仅支持 32 位符号扩展相对偏移量。支持 64 位地址作为绝对地址。 (在32位模式下,支持16位相对地址,加上一个操作数大小的前缀,我猜节省了一个指令字节。)
我一直在阅读一些汇编代码,我开始看到调用指令实际上是程序计数器相关的。
然而,每当我使用 visual studio 或 windbg 进行调试时,它总是说调用 0xFFFFFF ...对我来说这意味着它说我要跳转到那个地址。
谁是对的? Visual Studio 是否隐藏了指令编码的复杂性,只是说哦,这就是程序的意思,调试器知道它是一个 pc 相关的指令,并且因为它知道 pc,所以它只是去计算你?
非常困惑。
如果您要反汇编 .o
尚未链接的目标文件,调用地址将只是一个占位符,由链接器填写。
您可以使用 objdump -drwc -Mintel
来 显示 来自 .o
的重定位类型 + 符号名称( -r
选项是关键。或者 -R
用于已链接的共享库。)
显示跳转目标的实际地址对用户更有用,而不是将其反汇编为jcc eip-1234H
之类的。目标文件有一个默认的加载地址,所以反汇编程序在每条指令中都有一个 eip
的值,这通常出现在反汇编输出中。
例如在我写的一些 asm 代码中(我使用符号名称进入目标文件,因此循环分支目标实际上对反汇编程序可见):
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
请注意,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>
...
请注意 call
指令如何使它们的相对位移 = 0。因此,在链接器插入实际相对值之前,它们会在 call
之后用指令的目标进行编码称呼。 (即 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
请注意,相对跳转到同一文件中符号的完全相同的指令编码,即使该文件没有基地址,所以反汇编程序只是将其视为零。
参见英特尔的指令编码参考手册。 https://whosebug.com/tags/x86/info 处的链接。即使在 64 位模式下,call
也仅支持 32 位符号扩展相对偏移量。支持 64 位地址作为绝对地址。 (在32位模式下,支持16位相对地址,加上一个操作数大小的前缀,我猜节省了一个指令字节。)