我试图在 x86 汇编中做一个相对跳转,但是我无法让它工作。似乎出于某种原因,我的跳跃不断被重写为绝对跳跃或其他东西。
我正在尝试做的一个简单的示例程序是这样的:
.global main
main:
jmp 0x4
ret
由于 jmp 指令的长度为 4 个字节,并且相对跳转与跳转地址的偏移量 + 1,因此这应该是一个奇特的空操作。但是,编译运行这段代码会导致段错误。
真正让我困惑的是,将它编译到目标级别然后反汇编目标文件表明汇编程序似乎正确地进行了相对跳转,但在文件编译后链接器将其更改为另一种类型的跳跃。
例如,如果上面的代码在名为 asmtest.s 的文件中:
$gcc -c asmtest.s
$objdump -D asmtest.o
... Some info from objdump
00000000 <main>:
0: e9 00 00 00 00 jmp 5 <main+0x5>
5: c3 ret
这看起来汇编器正确地进行了相对跳转,尽管 jmp 指令填充了 0 是可疑的。
然后我使用 gcc 链接它然后反汇编它并得到这个:
$gcc -o asmtest asmtest.o
$objdump -d asmtest
...Extra info and other disassembled functions
08048394 <main>:
8048394: e9 6b 7c fb f7 jmp 4 <_init-0x8048274>
8048399: c3 ret
在我看来,这看起来像是链接器重写了 jmp 语句,或者用 5 替换了另一个地址。
所以我的问题归结为,我做错了什么?
我是否错误地指定了偏移量?我误解了相对跳跃的工作原理吗? gcc 是否试图确保我不会在我的代码中做危险的事情?
最佳答案
实际上,汇编程序认为您正在尝试进行绝对 跳转。但是,jmp
操作码在金属级别是相对的。因此,汇编程序无法知道在 0xe9 字节之后要写什么,因为汇编程序不知道您的代码将在哪个地址结束。
汇编器不知道,但链接器知道。因此,汇编器在 asmtest.o
header 的某处写入了对链接器的请求,内容如下:“当您知道代码将加载到哪个地址时,调整 0xe9 之后的那些字节这样它们就适合从该点(使用相对寻址)跳转到绝对地址“4”。链接器就是这样做的。它看到 0xe9 在地址 0x08048394,下一个操作码在 0x08048399,它计算出:从 0x08048399 到 0x00000004,必须减去 0x08048395,这相当于(在 32 位机器上)加上 0xf7fb7c6b。因此,生成的二进制文件中的“6b 7c fb f7”序列。
您可以像这样“手动”编码相对跳转:
.global main
main:
.byte 0xe9
.long 0x4
ret
因此,汇编器不会注意到您的 0xe9 实际上是一个 jmp
,它也不会试图比您聪明。在二进制文件中,您将获得所需的“e9 04 00 00 00”序列,并且没有链接器交互。
请注意,上面的代码可能会崩溃,因为相对偏移量是从偏移量紧接之后的地址开始计算的(即下一个操作码的地址,这里是ret
) .这将在 ret
之后跳入无人区 4 个字节,并且可能会出现段错误或其他奇怪的事情。
关于c - GCC/X86,相对跳转问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2638775/