在 QEMU 中诊断引导加载程序代码?
Diagnosing boot loader code in QEMU?
正在诊断 QEMU 中的引导加载程序代码?
我正在尝试创建一个打印字符 'A' 然后停止的最小 'boot loader code'。
我为此写了下面的C++程序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
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
实模式下的 operand prefix 0x66 将指令解码为 32 位寄存器(在本例中),然后将使用 4 个字节对立即值进行编码。这当然会使用 int 10h
作为 mov
数据的一部分。所以你的指令真正做的是:
0x66, 0xbb, 0x0f, 0x00, 0xcd, 0x10, // mov ebx,0x10cd000f
然后是通常出现的 hlt
和 jmp
指令。在以 16 位实模式为目标的代码中(预计在 8086/8088 上 运行ning),您根本不想使用此类前缀。您的代码应该简单地排除前缀并且看起来像:
0xbb, 0x0f, 0x00, // mov bx, 0FH
0xcd, 0x10, // int 10H
您可以使用理解 16 位指令的反汇编程序来反汇编 disk.img
文件。 NDISASM 是 NASM 程序(Netwide Assembler)的一部分,它是一个可以处理 16 位代码的好反汇编程序。 NASM 可用于 Windows。 NDISASM 将被安装并且可以 运行 这样:
ndisasm -b16 disk.img
这将尝试将 disk.img
解码为 16 位 (-b16
) 二进制文件。您可能会发现从您的字节中翻译的指令是什么以及哪里出了问题。
您还可以尝试使用 QEMU 中的远程调试功能,并使用 GDB 调试器单步调试您的代码。我在 Windows 上没有 运行 QEMU 所以我不知道它是否内置了远程调试支持。
您可以考虑使用可用于生成 16 位 8086/适合在引导加载程序中使用的 8088 代码。 Whosebug 上有许多问题和答案,说明如何使用 NASM 创建引导加载程序。
正在诊断 QEMU 中的引导加载程序代码?
我正在尝试创建一个打印字符 'A' 然后停止的最小 'boot loader code'。
我为此写了下面的C++程序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
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
实模式下的 operand prefix 0x66 将指令解码为 32 位寄存器(在本例中),然后将使用 4 个字节对立即值进行编码。这当然会使用 int 10h
作为 mov
数据的一部分。所以你的指令真正做的是:
0x66, 0xbb, 0x0f, 0x00, 0xcd, 0x10, // mov ebx,0x10cd000f
然后是通常出现的 hlt
和 jmp
指令。在以 16 位实模式为目标的代码中(预计在 8086/8088 上 运行ning),您根本不想使用此类前缀。您的代码应该简单地排除前缀并且看起来像:
0xbb, 0x0f, 0x00, // mov bx, 0FH
0xcd, 0x10, // int 10H
您可以使用理解 16 位指令的反汇编程序来反汇编 disk.img
文件。 NDISASM 是 NASM 程序(Netwide Assembler)的一部分,它是一个可以处理 16 位代码的好反汇编程序。 NASM 可用于 Windows。 NDISASM 将被安装并且可以 运行 这样:
ndisasm -b16 disk.img
这将尝试将 disk.img
解码为 16 位 (-b16
) 二进制文件。您可能会发现从您的字节中翻译的指令是什么以及哪里出了问题。
您还可以尝试使用 QEMU 中的远程调试功能,并使用 GDB 调试器单步调试您的代码。我在 Windows 上没有 运行 QEMU 所以我不知道它是否内置了远程调试支持。
您可以考虑使用可用于生成 16 位 8086/适合在引导加载程序中使用的 8088 代码。 Whosebug 上有许多问题和答案,说明如何使用 NASM 创建引导加载程序。