自定义Bootloader可加载汇编内核却无法加载C内核问题排查
自制Bootloader切换C语言内核加载失败问题
我正在开发一款带有自制Bootloader的自定义操作系统,之前使用汇编内核(kernel.asm)时一切正常,但替换为C语言内核(kernel.c)后,Bootloader无法加载内核并报错。
环境详情
- Bootloader:自制(使用NASM编写)
- 模式切换:Bootloader从16位实模式启动,调用内核前切换至32位保护模式
- 内核:kernel.c通过
i686-elf-gcc编译,链接器为i686-elf-ld - 工具链:M2芯片macOS上的交叉编译器
- 磁盘镜像:MBR引导扇区
- 测试工具:
qemu-system-i386 -hda os-image.bin
Bootloader(boot.asm)说明
- 将kernel.bin加载至0x10000地址
- 切换至保护模式
- 跳转至0x10000作为内核入口点
保护模式跳转代码
mov eax, 0x10000 jmp eax ; Jump to kernel
C内核问题
替换为以下kernel.c代码:
void kernel_main() { char *video_memory = (char *)0xB8000; *video_memory = 'X'; // 屏幕打印'X' while (1); }
同时定义了_start作为入口点:
__attribute__((section(".text"))) __attribute__((noreturn)) void _start() { kernel_main(); while (1); // 防止返回 }
链接脚本(linker.ld)
OUTPUT_FORMAT(elf32-i386) ENTRY(_start) SECTIONS { . = 0x1000; /* 内核加载地址0x1000 */ .text ALIGN(4) : { *(.text) } .rodata ALIGN(4) : { *(.rodata) } .data ALIGN(4) : { *(.data) } .bss ALIGN(4) : { *(.bss) } }
构建命令
nasm -f bin boot.asm -o boot.bin i686-elf-gcc -ffreestanding -m32 -c kernel.c -o kernel.o i686-elf-ld -m elf_i386 -T linker.ld -o kernel.bin --oformat binary kernel.o cat boot.bin kernel.bin > os-image.bin qemu-system-x86_64 -fda os-image.bin
测试时出现加载失败错误,Qemu启动后显示"Booting from Hard Disk..."后报错,无法正常加载内核。
完整boot.asm代码
[bits 16] ; 16位实模式 [org 0x7c00] ; Bootloader加载地址0x7C00 start: mov al, dl call print_hex_byte ; 打印启动驱动器号 cli ; 禁用中断 xor ax, ax mov ds, ax mov es, ax mov ss, ax mov sp, 0x7C00 ; 设置栈 ; 打印"LOADING KERNEL"信息 mov si, loading_msg call print_string disk_read: mov ah, 0x00 ; 读取前重置磁盘 mov dl, 0x00 ; 驱动器0 int 0x13 mov ah, 0x02 ; BIOS读扇区功能 mov al, 1 ; 读取1个扇区(512字节) mov ch, 0 ; 柱面0 mov dh, 0 ; 磁头0 mov cl, 2 ; 扇区2 mov dl, 0x00 ; 驱动器0(硬盘) mov ax, 0x1000 mov es, ax mov bx, 0x0000 mov cx, 3 ; 重试次数 read_retry: int 0x13 jnc read_ok ; 成功则跳转 mov si, int_cmd call print_string jnc read_ok ; 成功则跳转 loop read_retry ; 重试3次 jmp disk_error ; 全部失败则显示错误 ; 16字节磁盘地址包(DAP) dap: db 0x10 ; 包大小(16字节) db 0 ; 固定为0 dw 1 ; 读取扇区数 dw 0x0000 ; 缓冲区偏移 dw 0x1000 ; 缓冲区段地址 dq 1 ; LBA扇区号(扇区2) read_ok: mov si, kernel_loaded_msg call print_string mov esi, 0x10000 mov ecx, 16 print_loop: mov al, [esi] call print_hex_byte inc esi loop print_loop ; 加载全局描述符表(GDT) cli ; 禁用中断 lgdt [gdt_descriptor] ; 加载GDT ; 设置CR0寄存器的保护模式位 mov eax, cr0 or eax, 0x1 ; 设置PE(保护模式启用)位 mov cr0, eax ; 远跳转到32位模式(刷新CPU流水线) jmp 0x08:init_protected_mode ; ----------------- 32位保护模式 ----------------- [bits 32] init_protected_mode: mov ax, 0x10 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, 0x90000 ; 打印标记确认进入保护模式 mov dword [0xB8000], 0x2F4D2F4F ; 红色"MO" mov dword [0xB8004], 0x2F442F45 ; 红色"DE" ; 读取0x10000前4字节 ;mov esi, 0x10000 ;mov al, [esi] ;call print_hex_byte ; 打印第一字节 ;mov al, [esi + 1] ;call print_hex_byte ; 打印第二字节 ;mov al, [esi + 2] ;call print_hex_byte ; 打印第三字节 ;mov al, [esi + 3] ;call print_hex_byte ; 打印第四字节 ;mov esi, 0x10000 ;mov al, [esi] ;call print_hex_byte ; 若内核缺失则永久循环 cmp dword [esi], 0x00000000 je halt ; 前4字节为0则认为内核缺失,停机 ; 跳转到内核 ;call dword 0x10000 mov eax, 0x10000 jmp eax ; ----------------- 全局描述符表(GDT) ----------------- gdt: dq 0x0000000000000000 ; 空描述符 dq 0x00CF9A000000FFFF ; 32位代码段 dq 0x00CF92000000FFFF ; 32位数据段 gdt_descriptor: dw gdt_descriptor - gdt - 1 ; GDT大小 dd gdt ; GDT地址 loading_msg db "LOADING KERNEL", 0 kernel_loaded_msg db "JUMP!", 0 int_cmd db "INT-T", 0 in_protected_mode db "IN-PRT", 0 disk_error: mov si, error_msg call print_string jmp halt error_msg db "ERR!", 0 halt: hlt ; 停机 ; ----------------- 字符串打印函数 ----------------- print_string: pusha .loop: lodsb ; 从SI加载字符到AL or al, al ; 检查是否为终止符 jz .done mov ah, 0x0E ; BIOS电传打字功能 int 0x10 jmp .loop .done: popa ret print_hex_byte: push ax push bx push cx push dx mov ah, 0x0E ; BIOS电传打字输出 ; 转换高4位为十六进制 mov bl, al shr bl, 4 cmp bl, 9 jg hex_letter add bl, '0' jmp print1 hex_letter: add bl, 'A' - 10 print1: mov al, bl int 0x10 ; 转换低4位为十六进制 mov bl, al and bl, 0x0F cmp bl, 9 jg hex_letter2 add bl, '0' jmp print2 hex_letter2: add bl, 'A' - 10 print2: mov al, bl int 0x10 pop dx pop cx pop bx pop ax ret times 510 - ($ - $$) db 0 ; 填充至510字节 dw 0xAA55 ; 引导扇区签名
内容的提问来源于stack exchange,提问作者Sunil KhoRwal
相关产品推荐
相关产品推荐

