自制汇编操作系统在Qemu中启动失败,无法显示Hello World
问题分析与修复方案
核心问题拆解
你的代码出现boot failed且无输出,主要源于三个关键错误:引导扇区格式无效、内核加载/链接地址不匹配、缺少磁盘操作错误处理。以下是具体修复步骤:
1. 修复引导扇区编译格式
引导扇区是实模式下直接运行的512字节二进制代码,不能用ELF格式编译(-f elf32)——ELF目标文件包含额外头信息,会导致times 510-($-$$)的填充计算错误,最终生成的文件末尾没有正确的0xAA55引导标志,QEMU无法识别为可引导软盘。
修正后的bootloader.asm
section .text org 0x7c00 ; 告诉nasm代码将被加载到0x7c00地址执行 _start: cli xor ax, ax mov ds, ax mov es, ax mov ss, ax mov sp, 0x9000 ; 读取磁盘:将第2个扇区加载到0x8000 mov bx, 0x8000 mov dh, 0 ; 磁头0 mov dl, 0 ; 驱动器0(软盘) mov ch, 0 ; 柱面0 mov cl, 2 ; 扇区2 mov ah, 2 ; 读扇区功能 mov al, 1 ; 读取1个扇区 int 0x13 ; 检查磁盘读取错误(CF置位表示失败) jc disk_error jmp 0x8000 disk_error: ; 输出错误提示 mov ah, 0x0E mov al, 'E' int 0x10 hlt times 510-($-$$) db 0 dw 0xAA55
关键修改:
- 添加
org 0x7c00,让nasm正确计算指令地址 - 明确指定读取1个扇区(
mov al,1) - 增加磁盘读取错误检查和处理
2. 匹配内核加载与链接地址
bootloader要把内核加载到0x8000,所以必须告诉nasm内核代码将在0x8000地址执行,否则跳转后指令地址不匹配,执行垃圾代码。
修正后的kernel.asm
section .text org 0x8000 ; 指定代码加载到0x8000执行 _start_kernel: mov ah, 0x0E mov al, 'H' int 0x10 mov al, 'e' int 0x10 mov al, 'l' int 0x10 mov al, 'l' int 0x10 mov al, 'o' int 0x10 loop: hlt jmp loop ; 防止hlt后被中断唤醒继续执行 times 512-($-$$) db 0
关键修改:
- 添加
org 0x8000 - 增加死循环,避免
hlt后CPU继续执行无效代码
3. 修复软盘镜像生成流程
原来的链接脚本把bootloader和kernel合并为ELF格式文件,破坏了引导扇区结构。正确做法是分别编译两者为二进制文件,再合并成软盘镜像(第一个扇区是bootloader,第二个是kernel)。
修正后的Makefile
boot = bootloader.asm kernel = kernel.asm boot_bin = bootloader.bin kernel_bin = kernel.bin os = os.bin asm_compiler = nasm compile: $(asm_compiler) -f bin $(boot) -o $(boot_bin) $(asm_compiler) -f bin $(kernel) -o $(kernel_bin) cat $(boot_bin) $(kernel_bin) > $(os) ; 合并两个扇区为软盘镜像 run: qemu-system-i386 -fda $(os) clean: rm $(boot_bin) $(kernel_bin) $(os)
关键修改:
- 编译bootloader和kernel为纯二进制格式
- 通过
cat合并两个512字节文件,生成合法的软盘镜像
错误根源总结
- 引导扇区无效:ELF格式编译导致
0xAA55标志位置错误,QEMU无法识别引导盘 - 地址不匹配:内核链接地址与bootloader加载地址不一致,跳转后执行错误代码
- 无错误处理:磁盘读取失败时直接跳转至无效地址,无法定位问题
内容的提问来源于stack exchange,提问作者Federico Occhiochiuso
相关产品推荐
相关产品推荐

