Different behavior calling JMP with register vs value

你。 提交于 2020-06-01 05:54:29

问题


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

and

mov eax, 0x00007C00
jmp eax

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?

EDIT:

When dissassembling, I've received the following respectively:

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

and

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

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


回答1:


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

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 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).

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.

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.

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.



来源:https://stackoverflow.com/questions/61880905/different-behavior-calling-jmp-with-register-vs-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!