使用寄存器与值调用 JMP 的不同行为
Different behavior calling JMP with register vs value
我正在尝试执行到地址 0x7C00 的绝对跳转,作为业余爱好过程的一部分 OS。我在 GAS 中使用 intel 语法并在 QEMU 中进行测试。我尝试了两种方法:
jmp 0x00007c00
和
mov eax, 0x00007C00
jmp eax
第二种方法似乎按我的预期工作并跳转到 0x7C00,但第一种方法导致 QEMU 崩溃并指出它是 "trying to execute code outside RAM or ROM at 0x40007c00"。有谁知道为什么它跳转到不同的地址并且高位字节被设置为 0x4000?
编辑:
我在拆解的时候分别收到了以下信息:
3c: e9 fc 7b 00 00 jmp 7c3d <int32_end+0x7ad4>
和
3c: b8 00 7c 00 00 mov [=13=]x7c00,%eax
41: ff e0 jmp *%eax
所以他们的编译方式不同,尽管我仍然对第二个到底在做什么感到困惑,它看起来像是跳转到 0x7c3d
答案竟然是评论中的一系列见解:
首先是反汇编代码,看到 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-endianfc 7b 00 00 = 0x00007bfc
。由于我正在反汇编一个 .o 文件,链接器还没有填充真正的位移。这可以通过使用 objdump -drwC -Mintel.
来避免
为了使执行跳转的代码与位置无关,最好的方法是坚持使用第二种方法中的 mov-immediate
+ jmp eax
。
我正在尝试执行到地址 0x7C00 的绝对跳转,作为业余爱好过程的一部分 OS。我在 GAS 中使用 intel 语法并在 QEMU 中进行测试。我尝试了两种方法:
jmp 0x00007c00
和
mov eax, 0x00007C00
jmp eax
第二种方法似乎按我的预期工作并跳转到 0x7C00,但第一种方法导致 QEMU 崩溃并指出它是 "trying to execute code outside RAM or ROM at 0x40007c00"。有谁知道为什么它跳转到不同的地址并且高位字节被设置为 0x4000?
编辑:
我在拆解的时候分别收到了以下信息:
3c: e9 fc 7b 00 00 jmp 7c3d <int32_end+0x7ad4>
和
3c: b8 00 7c 00 00 mov [=13=]x7c00,%eax
41: ff e0 jmp *%eax
所以他们的编译方式不同,尽管我仍然对第二个到底在做什么感到困惑,它看起来像是跳转到 0x7c3d
答案竟然是评论中的一系列见解:
首先是反汇编代码,看到 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-endianfc 7b 00 00 = 0x00007bfc
。由于我正在反汇编一个 .o 文件,链接器还没有填充真正的位移。这可以通过使用 objdump -drwC -Mintel.
为了使执行跳转的代码与位置无关,最好的方法是坚持使用第二种方法中的 mov-immediate
+ jmp eax
。