自制简易OS跳转内核时三重故障,GDT/IDT相关报错求助
问题排查与解决方案
核心问题:带错误码的异常Stub未处理栈上的错误码,导致栈不平衡
Bochs日志里的fetch_raw_descriptor: GDT: index (bf) 17 > limit (17)是栈被破坏后的连锁反应:当发生带错误码的异常(比如你定义的isr_err_stub 30)时,CPU会自动将错误码压入栈中,但当前Stub没有弹出这个错误码就执行iret,导致:
iret错误地将栈中的错误码当作返回EIP,把原本的EIP当作CS选择子- 非法的CS选择子(如日志里的0xBF)触发GDT索引越界,进而引发通用保护故障(0x0D)
- 异常处理流程本身存在错误,最终导致三重故障
修复步骤
1. 修正带错误码的异常Stub
修改idt.asm中的isr_err_stub宏,在iret之前弹出栈上的错误码,恢复栈平衡:
; 带错误码的异常Stub:移除CPU自动压入的错误码 %macro isr_err_stub 1 isr_stub_%+%1: call exception_handler add esp, 4 ; 弹出错误码,保证iret时栈结构正确 iret %endmacro
如果后续需要在处理函数中获取错误码,可以调整Stub将错误码传入:
%macro isr_err_stub 1 isr_stub_%+%1: push eax ; 临时保存寄存器 mov eax, [esp+4] ; 读取栈上的错误码 push eax ; 将错误码作为参数传入处理函数 call exception_handler add esp, 8 ; 弹出错误码+临时保存的eax iret %endmacro
同时更新C语言处理函数的定义:
void exception_handler(u32 error_code){ __asm__ volatile("cli; hlt"); }
2. 验证GDT配置(当前GDT定义无问题)
你的GDT配置是正确的:
- 包含空描述符、32位代码段、32位数据段,共3个描述符,总大小24字节
g_GDTDesc中的limit计算为g_GDTDesc - g_GDT -1 = 24-1=23(0x17),符合GDT limit的定义(表示最后一个字节的偏移量)
3. 可选:推迟中断开启时机
如果内核初始化阶段还有未完成的操作,可以将STI()推迟到所有初始化工作完成后执行,避免提前触发待处理的异常。
内容的提问来源于stack exchange,提问作者neta cohen
相关产品推荐
相关产品推荐

