assembly - 使用寄存器与值调用 JMP 的不同行为
问题描述
我正在尝试执行到地址 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
解决方案
答案原来是评论中的一系列见解:
首先是反汇编代码,看到 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
推荐阅读
- javascript - Find the first same number in the same index on different for loops
- css - 如何更改 Bulma 的默认样式?
- java - 从数组列表中取出 xPos 和 YPos 值
- c++ - 在 C++ 上多次使用 udp 端口/在 C++ 服务器上多次使用端口
- python - 当条件不满足时,dataframe.all() 返回 True
- linux - 脚本执行方式的区别
- javascript - 使用 html2canvas 动作只工作一次,我需要代码工作多次
- apache - WAMP SERVER 为 HTTPS 使用 443 以外的其他端口
- javascript - 过滤功能在 Todo 应用程序中不起作用
- reactjs - Firestore - 合并来自两个不同集合的文档 - React Typescript