从后期引导加载器加载GDT进入32位保护模式时崩溃求助
问题排查与修复方案
核心问题出在GDT物理地址错误和保护模式跳转地址不匹配,导致CPU进入保护模式后访问无效内存,引发崩溃重启。
问题1:GDT的物理地址未正确设置
你的AdvBoot被加载到逻辑地址0x2000:0x0,对应物理地址0x20000(段基址=0x2000*16=0x20000)。但你用org 0x0编译AdvBoot时,汇编器计算的GDT_Start仅为相对于段基址的偏移,而lgdt指令需要的是GDT的物理地址。当前GDT_Descriptor中的dd GDT_Start只写入了偏移值,导致CPU加载了错误的GDT地址(0x000000XX而非0x000200XX),远跳转时无法找到有效代码段描述符,直接崩溃。
问题2:保护模式跳转地址不匹配
进入32位保护模式后,你的代码段基址为0(GDT中代码段的基址字段全为0),因此远跳转的偏移需要是代码的物理地址。但org 0x0编译下,ProtectedModeStart的偏移仅相对于段基址,远跳转时CPU会访问物理地址0x000000YY,而实际代码位于0x000200YY,导致访问无效内存。
修复方案
方案1:手动修正地址(保持org 0x0)
在lgdt指令前计算GDT物理地址,并修正跳转目标:
org 0x0 bits 16 Start: cli ; 计算GDT物理地址并更新描述符 xor eax, eax mov ax, cs ; cs=0x2000 shl eax, 4 ; 转换为物理基址0x20000 add eax, GDT_Start ; 加上GDT在代码中的偏移 mov [GDT_Descriptor + 2], eax ; 更新GDT描述符的基址字段 lgdt [GDT_Descriptor] ; 进入保护模式 mov eax, cr0 or al, 1 mov cr0, eax ; 修正远跳转目标:使用物理地址作为偏移 mov eax, 0x20000 add eax, ProtectedModeStart jmp GDT_CodeSegment:eax ; 跳转到正确的物理地址 bits 32 ProtectedModeStart: mov ax, GDT_DataSegment mov ds, ax mov ss, ax mov es, ax mov fs, ax mov gs, ax mov ebp, 0x90000 mov esp, ebp mov edx, 0xb8000 mov ah, 0x0f mov al, 'B' mov [edx], ax jmp $ %include "Boot/GDT.asm"
方案2:直接设置正确的org(更简洁)
因为AdvBoot被加载到物理地址0x20000,直接修改org为物理地址,汇编器会自动用物理地址计算所有标签:
org 0x20000 ; 替换原org 0x0 bits 16 Start: cli lgdt [GDT_Descriptor] ; GDT_Start已为物理地址,无需额外计算 ; 进入保护模式 mov eax, cr0 or al, 1 mov cr0, eax ; 直接跳转到物理地址的ProtectedModeStart jmp GDT_CodeSegment:ProtectedModeStart bits 32 ProtectedModeStart: mov ax, GDT_DataSegment mov ds, ax mov ss, ax mov es, ax mov fs, ax mov gs, ax mov ebp, 0x90000 mov esp, ebp mov edx, 0xb8000 mov ah, 0x0f mov al, 'B' mov [edx], ax jmp $ %include "Boot/GDT.asm"
额外优化建议
- 将
%include "Boot/GDT.asm"移到代码开头(Start标签前),避免GDT被放在死循环之后,提升代码可读性。 - 进入保护模式后,当前段寄存器的设置逻辑是正确的,无需修改。
内容的提问来源于stack exchange,提问作者triangleboi
相关产品推荐
相关产品推荐

