用寄存器vs值调用JMP的不同行为

问题描述 投票:0回答:1

我正在尝试在业余操作系统中执行绝对跳转到地址0x7C00的过程。我在GAS中使用intel语法,并在QEMU中进行测试。我尝试了两种方法:

jmp 0x00007c00

mov eax, 0x00007C00
jmp eax

第二种方法似乎按我的预期工作,并跳到0x7C00,但是第一种方法导致QEMU崩溃,表明它是“试图在0x40007c00的RAM或ROM外部执行代码”。有谁知道为什么它会跳转到另一个地址,并且高字节被设置为0x4000?

编辑:

拆卸时,我分别收到以下信息:

  3c:   e9 fc 7b 00 00          jmp    7c3d <int32_end+0x7ad4>

  3c:   b8 00 7c 00 00          mov    $0x7c00,%eax
  41:   ff e0                   jmp    *%eax

所以它们的编译方式不同,尽管我对第二个代码到底在做什么有些困惑,看起来像是跳转到0x7c3d

assembly x86 gas intel-syntax
1个回答
0
投票

答案原来是评论中的一系列见解:

[首先是分解代码,以查看jmp组装成接近jmp rel32。事实证明,所有x86直接近跳转都是相对的。 felixcloutier.com/x86/jmp显示此jmp使用的E9操作码的编码。为了对正确的rel32偏移进行编码以达到给定的绝对目标地址,汇编器+链接器需要知道跳转指令将从其运行的地址。

源代码中的

jmp 0x00007c00为它提供了0x00007c00的绝对跳转目标,但是汇编器将以相对跳转的形式到达它。与jmp .+0x7c00或直接指定rel32位移不同。如果指令本身在文件中写入两次,然后将其汇编+链接到ELF可执行文件(例如gcc -static -nostdlib foo.s && objdump -drwC -Mintel a.out)中,则可以轻松地看到这一点。在这里,两个jmp指令具有不同的编码(不同的rel32)和相同的绝对目标。另外,观察最后的小精灵时,您可以看到汇编程序以相对跳转(0x7C00的跳转)到达0x3ff06723,因为代码地址以0xC0100000开头。

我遇到的一个问题是,当我的代码应该以0xC0100000开始时,地址和跳转似乎太小了。我未能意识到.o文件中的地址是相对于文件开头的,因此为什么指令驻留在0x3c处,这是其在文件中的偏移量。如果我反汇编链接后生成的最后一个elf文件,则所有地址都添加了0xC0100000

[还值得注意的是,为了方便起见,反汇编程序正在根据该指令末尾显示的地址来计算绝对地址7c3d。实际相对位移为小尾数fc 7b 00 00 = 0x00007bfc。由于我在拆卸.o文件时,链接器尚未填写实际的位置。可以通过使用objdump -drwC -Mintel.

来避免

为了使执行跳转的代码与位置无关,最好的方法是坚持使用第二种方法中使用的mov-immediate + jmp eax

© www.soinside.com 2019 - 2024. All rights reserved.