用寄存器vs值调用JMP的不同行为 [英] Different behavior calling JMP with register vs value

查看:182
本文介绍了用寄存器vs值调用JMP的不同行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to perform an absolute jump to the address 0x7C00 as part of a procedure in a hobby OS. I'm using intel syntax in GAS and testing in QEMU. I tried two methods:

jmp 0x00007c00

mov eax, 0x00007C00
jmp eax

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

The second method seems to work as I intended and jumps to 0x7C00, but the first method causes QEMU to crash stating that it's "trying to execute code outside RAM or ROM at 0x40007c00". Does anyone know why it's jumping to a different address and the upper bytes are being set to 0x4000?

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

When dissassembling, I've received the following respectively:

  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

So they are compiling differently, although I'm still a bit confused on what exactly the second one is doing which looks like a jump to 0x7c3d

推荐答案

答案竟然是评论中涉及的一系列见解:

The answer turned out to be a series of insights gone over in the comments:

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

First was dissassembling the code to see that the jmp assembled into a near jmp rel32. As it turns out, all x86 direct near jumps are relative. felixcloutier.com/x86/jmp shows the encoding for the E9 opcode this jmp is using. To encode the right rel32 offset to reach a given absolute target address, the assembler + linker need to know the address where the jump instruction will be running from.

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

jmp 0x00007c00 in the source gives it the absolute jump target of 0x00007c00, but the assembler will reach it with a relative jump. It's not the same as jmp .+0x7c00 or specifying the rel32 displacement directly. This can be seen easily if the instruction is written twice in a file by itself and its assembled+linked into an ELF executable (e.g. gcc -static -nostdlib foo.s && objdump -drwC -Mintel a.out). Here the two jmp instructions have different encodings (different rel32) and the same absolute target. Additionally, when observing the final elf you can see the assembler reaching 0x7C00 with a relative jump (of 0x3ff06723 since the code addresses begin at 0xC0100000).

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

One issue I had was that the addresses and jumps seem far too small when my code is supposed to begin at 0xC0100000. I failed to realize that addresses in .o files are relative to the start of the file, hence why the instruction resides at 0x3c which is its offset in the file. If I dissassemble the final elf file made after linking, all of the addresses have 0xC0100000 added to them.

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

Also worth noting is that the disassembler is calculating the absolute address 7c3d for convenience, based on the address it's showing for the end of that instruction. The actual relative displacement is the little-endian fc 7b 00 00 = 0x00007bfc. Since I was disassembling a .o file, the linker hadn't filled in the real displacement yet. This can be avoided by using objdump -drwC -Mintel.

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

In order to make the code doing the jump to be position-independent, the best approach is sticking with the mov-immediate + jmp eax used in the second method.

这篇关于用寄存器vs值调用JMP的不同行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆