QEMU从汇编加载C代码时闪烁重启问题求助
QEMU重启且GDB无法连接,跳转到内核地址触发问题的排查与解决
首先解决GDB连接问题,没有调试环境根本没法定位问题:
- 启动QEMU时必须加上调试参数,让它开放调试端口并暂停等待连接:
qemu-system-i386 -s -S -fda your_os_image.img-s等价于-gdb tcp::1234,开放1234端口供GDB连接;-S让QEMU启动后暂停,直到GDB发送继续指令。 - 打开另一个终端启动GDB,连接到QEMU:
这样就能正常调试,后续可以在gdb your_kernel.elf (gdb) target remote localhost:1234 (gdb) continuejmp KERNELLOC前加断点,查看0x1000地址的内容是否正确。
接下来是核心问题:你的引导扇区磁盘加载逻辑有致命错误
你用int 0x13加载2个扇区,但既没指定加载目标地址,也没设置磁盘参数,更没检查加载是否成功,导致内核根本没被加载到0x1000,跳过去执行的是随机垃圾代码,直接触发崩溃重启。修改引导扇区的磁盘加载部分:
[org 0x7c00] KERNELLOC equ 0x1000 CODESEG equ gdt_code - gdt_start DATASEG equ gdt_data - gdt_start ; Load Sectors - 修正后的磁盘加载逻辑 mov ah, 0x02 ; BIOS读磁盘功能号 mov al, 0x2 ; 读取2个扇区 mov ch, 0x00 ; 柱面号0 mov dh, 0x00 ; 磁头号0 mov cl, 0x02 ; 起始扇区号2(引导扇区是第1个扇区) mov bx, KERNELLOC ; 加载到内存地址0x1000 int 0x13 ; 调用BIOS中断 jc disk_error ; 如果CF标志位被置位,说明加载失败 ; 原有代码继续 cli lgdt [gdt_descriptor] mov eax, cr0 or eax, 1 mov cr0, eax jmp CODESEG:start_protected_mode ; 新增磁盘错误处理 disk_error: ; 这里需要你实现一个16位模式下的字符串打印函数print_string_16 mov bx, DISK_ERROR_MSG call print_string_16 jmp $ ; 死循环,避免继续执行 DISK_ERROR_MSG db "Disk read failed!", 0 ; 以下是原有GDT和保护模式代码,保持不变 gdt_start: gdt_null: dd 0x0 dd 0x0 gdt_code: dw 0xffff dw 0x0 db 0x0 db 0b10011010 db 0b11001111 db 0x0 gdt_data: dw 0xffff dw 0x0 db 0x0 db 0b10010010 db 0b11001111 db 0x0 gdt_end: gdt_descriptor: dw gdt_end - gdt_start dd gdt_start [bits 32] start_protected_mode: mov ax, DATASEG mov ss, ax mov es, ax mov fs, ax mov gs, ax mov ebp, 0x90000 mov esp, ebp jmp KERNELLOC times 510-($-$$) db 0 dw 0xaa55
另外需要确认内核的链接地址是否正确:
- 你的C内核必须通过链接脚本指定加载地址为0x1000,比如创建
linker.ld:ENTRY(kernel_main) SECTIONS { . = 0x1000; .text : { *(.text) } .data : { *(.data) } .bss : { *(.bss) } } - 编译链接内核时使用:
gcc -m32 -ffreestanding -c kernel.c -o kernel.o ld -m elf_i386 -T linker.ld kernel.o -o kernel.bin - 最后合并引导扇区和内核生成镜像:
cat boot.bin kernel.bin > os.img
做完这些修改后,再用QEMU启动,应该能正常加载内核,不会出现重启问题。如果还有异常,用GDB在jmp KERNELLOC处断点,查看0x1000地址的内容是否是内核的机器码,进一步排查。
内容的提问来源于stack exchange,提问作者SKZI
相关产品推荐
相关产品推荐

