在QEMU中诊断引导加载程序代码?
我试图创建一个最小的“引导程序代码”,该代码打印字符“ A”,然后停止。
我为此编写了以下C ++程序
#include#include #include int main(int argc, char** argv) { /* * If I run these code directly in Windows, it crashes, for I believe Windows * do not allow accessing the BIOS directly, that make sense * * But I can compile a binary, then I can use the debugger to view what machine code * does these correspond to, and build to boot program! */ /* __asm { mov ah, 0x0e mov al, 0x41 mov bx, 15 int 0x10 hlt lp: jmp lp } */ int disk_length = 80 * 18 * 512 * 2; char* disk = (char*)calloc(disk_length, sizeof(char)); const char program[] = { 0xb4, 0x0e, // mov ah, 0EH 0xb0, 0x41, // mov al, 41H 0x66, 0xbb, 0x0f, 0x00, // mov bx, 0FH 0xcd, 0x10, // int 10H 0xf4, // hlt 0xeb, 0xfe // lp: jmp lp }; const char boot_signature[] = {0x55, 0xAA}; const int program_length = _countof(program); const int boot_signature_length = _countof(boot_signature); // Be careful with file mode FILE* imgFile = fopen("disk.img", "wb"); memcpy(disk, program, program_length); memcpy(disk + 510, boot_signature, boot_signature_length); int written = 0; while (written < disk_length) { written += fwrite(disk + written, sizeof(char), disk_length - written, imgFile); } fclose(imgFile); return 0; }
首先,我在未注释内联程序集的情况下运行了代码。在调试器中,我派生了操作码,并确定操作码与源代码中的操作码匹配。接下来,我运行带有注释的内联程序集的代码,然后生成了一个img文件。我使用二进制编辑器查看内容,并确保其外观符合我的期望。最后,我运行了qemu-system-i386.exe -fda disk.img,我期望引导加载程序显示大写的“ A”,但是什么也没显示。
现在我有两个问题:
1.)我的代码有什么问题?2.)我如何诊断?
问题似乎是以下指令序列:
0x66, 0xbb, 0x0f, 0x00, // mov bx, 0FH 0xcd, 0x10, // int 10H
实模式下的操作数前缀0x66将指令解码为32位寄存器(在这种情况下),然后将使用4个字节对立即数进行编码。当然,这会将int 10h
用作数据的一部分mov
。因此,您的指令真正要做的是:
0x66, 0xbb, 0x0f, 0x00, 0xcd, 0x10, // mov ebx,0x10cd000f
然后是通常显示的hlt
和jmp
指令。在以16位实模式为目标(预期在8086/8088上运行)的代码中,您根本不需要使用此类前缀。您的代码应该简单地排除前缀,如下所示:
0xbb, 0x0f, 0x00, // mov bx, 0FH 0xcd, 0x10, // int 10H
您可以使用了解16位指令的反汇编disk.img
程序来反汇编文件。NDISASM是可以处理16位代码的很好的反汇编程序,它是NASM程序(Netwide汇编程序)的一部分。NASM可用于Windows。NDISASM将被安装并可以通过以下方式运行:
ndisasm -b16 disk.img
这将尝试解码disk.img
为16位(-b16
)二进制。您可能会发现字节翻译的指令是什么,什么地方出错了。
您也可以尝试使用QEMU中的远程调试功能,并使用GDB调试器逐步执行代码。我不在Windows上运行QEMU,所以不知道它是否具有远程调试支持。
您可以考虑使用可用于生成适合在引导加载程序中使用的16位8086/8088代码的汇编程序(例如NASM),而不是像现在那样编写引导加载程序。关于Stackoverflow的许多问题和答案表明如何使用NASM创建引导加载程序。