assembly - Qemu 和原始二进制文件

标签 assembly x86 x86-16 osdev watcom

我正在编译和运行二进制文件(引导扇区,第 1 阶段,第 2 阶段)以进行练习。引导扇区是asm,第一阶段是asm,运行良好。第二阶段在 0x1000 处加载,我有一些 asm 跳转到我的 C 代码的开头。我的跳跃和调用似乎偏离(短)了两个字节。

我已经尝试过 Bochs 和 Qemu 中的代码(单步执行)。所有代码看起来都不错。我什至在 IDA 中拆过它,每一个看起来都很好。我认为这可能是我缺乏代码对齐知识。

第二阶段从0x1000开始:

0x1000: cli    
0x1001: xor    eax,eax
0x1003: mov    eax,0x1f1a
0x1008: mov    esp,eax
0x100a: sti    
0x100b: jmp    0x1010

第一次跳转到达0x1010(这是反汇编的C代码):

0x1010: push   0x16b4
0x1015: call   0x14ca   <---
0x101a: add    esp,0x4
0x101d: jmp    0x101d

上面对 0x14CA 的调用实际上到达了 0x000014c9,短了两个字节。

如上面的代码所示,我希望跳转或调用能够到达操作数地址,但它总是会缺少两个字节。

最佳答案

这是一个疯狂的猜测,实际上可能是错误的。它基于以下事实:在 32 位代码中,您编码的相关 JMP 和 CALL 指令为 5 个字节,在 16 位代码中,它们为 3 个字节。 5 字节 - 3 字节 = 2 字节。鉴于相对 JMP 和 CALL 目标是基于距下一条指令开始的距离,它可能会提示可能出现的问题。

如果我使用此代码:

bits 32
org 0x1000

    cli
    xor    eax,eax
    mov    eax,0x1f1a
    mov    esp,eax
    sti
    jmp    0x1010
    push   0x16b4
    call   0x14ca
    add    esp,0x4
    jmp    0x101d

并将其组装为:

nasm -f bin stage2.asm -o stage2.bin

并使用以下命令查看 32 位解码:

ndisasm -b32 -o 0x1000 stage2.bin

我得到:

00001000  FA                cli
00001001  31C0              xor eax,eax
00001003  B81A1F0000        mov eax,0x1f1a
00001008  89C4              mov esp,eax
0000100A  FB                sti
0000100B  E900000000        jmp dword 0x1010
00001010  68B4160000        push dword 0x16b4
00001015  E8B0040000        call dword 0x14ca
0000101A  83C404            add esp,byte +0x4
0000101D  E9FBFFFFFF        jmp dword 0x101d

这看起来是正确的。但是,如果我使用以下命令将相同的代码解码为 16 位:

ndisasm -b16 -o 0x1000 stage2.bin

我得到:

00001000  FA                cli
00001001  31C0              xor ax,ax
00001003  B81A1F            mov ax,0x1f1a
00001006  0000              add [bx+si],al
00001008  89C4              mov sp,ax
0000100A  FB                sti
0000100B  E90000            jmp word 0x100e
0000100E  0000              add [bx+si],al
00001010  68B416            push word 0x16b4
00001013  0000              add [bx+si],al
00001015  E8B004            call word 0x14c8
00001018  0000              add [bx+si],al
0000101A  83C404            add sp,byte +0x4
0000101D  E9FBFF            jmp word 0x101b
00001020  FF                db 0xff
00001021  FF                db 0xff

指令解码不正确,但是 JMP 和 CALL 存在并且转到错误的内存位置。这看起来非常像您所看到的观察结果。

在没有看到您的代码的情况下,我希望当您在 0x1000 处开始执行第 2 阶段时,您已进入 32 位保护模式。如果你还没有,那么我怀疑这就是你问题的根源。我相信 32 位编码指令正在 16 位实模式下执行。

<小时/>

更新

根据评论,OP 建议他们进入 32 位保护模式,作为进入虚幻模式过程的一部分。他们相信虚幻模式仍会将指令解码为 32 位代码,因此出现了问题。

您可以通过进入 32 位保护模式进入虚幻模式并返回到 16 位实模式。虚幻模式仍然是 16 位实模式,只不过隐藏描述符缓存中的限制设置为 0xffffffff(4GiB 限制)。返回到 16 位实模式后,您将能够使用 32 位寻址直接对超过 64KiB 的段中的内存进行寻址,但代码仍然在 16 位实模式下运行。

如果您正在为 16 位虚幻模式编写代码,您的编译器和汇编器仍然需要生成 16 位代码。如果您打算编写/生成 32 位代码,则不能选择虚幻模式,您需要进入 32 位保护模式才能执行 32 位代码。

关于assembly - Qemu 和原始二进制文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56763791/

相关文章:

windows - 汇编程序文件作为使用 WDK 工具构建驱动程序的输入

c - 为什么不同 gcc 版本的简单 C 程序的汇编代码不同?

assembly - "int 21h"在汇编中是什么意思?

assembly - INT13H(非扩展)是否能够访问每个柱面超过 16 个磁头的驱动器?

assembly - 8086 中的堆栈段和堆栈指针

assembly - x86 asm 中 NOT 指令的简单示例

assembly - x86 汇编语言中的函数调用

x86 - 上证所指令 : Byte+Short

assembly - cmp 时设置 CF

c++ - GCC 5.4.0 的昂贵跳跃