0

我正在尝试执行到地址 0x7C00 的绝对跳转,作为爱好 OS 中程序的一部分。我在 GAS 中使用英特尔语法并在 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

4

1 回答 1

2

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

首先是反汇编代码,看到 jmp 组装成 near 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为了方便起见,反汇编程序正在计算绝对地址,基于它在该指令末尾显示的地址。实际的相对位移是 little-endian fc 7b 00 00 = 0x00007bfc。由于我正在反汇编一个 .o 文件,因此链接器尚未填充实际位移。这可以通过使用来避免objdump -drwC -Mintel.

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

于 2020-05-19T03:24:45.133 回答