我正在尝试在业余操作系统中执行绝对跳转到地址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
答案原来是评论中的一系列见解:
[首先是分解代码,以查看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
。