加载GDT时操作系统触发三重故障的技术求助
加载GDT时触发三重故障的排查
加载GDT(全局描述符表)时触发三重故障,参考过OSDEV的GDT相关文档。
怀疑有问题的汇编代码
global loadGDT loadGDT: mov ax, [esp + 4] mov [gdtr], ax mov eax, [esp + 8] mov [gdtr + 2], eax lgdt [gdtr] ret global reloadSegments reloadSegments: jmp 0x00:.reload_cs .reload_cs: mov ax, 0x00 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax ret
段配置信息
- 代码段:
- 基址:0
- 限长:0xFFFFF
- 访问字节:10011011
- 标志位:1000
- 数据段:
- 基址:0
- 限长:0xFFFFF
- 访问字节:10010011
- 标志位:1000
原本使用0x08和0x10作为段选择子,因基址都是0改成0,故障依旧。
补充的C代码
#include <gdt.h> uint64_t GDTEntryToInt(SegmentDescriptor descriptor){ uint64_t rv = 0; rv = rv | ((descriptor.limit & 0xFFFF) << 0); rv = rv | ((descriptor.base & 0xFFFFF) << 16); rv = rv | ((descriptor.accessByte & 0xFF) << 40); rv = rv | ((descriptor.limit & 0xF0000) << 48); rv = rv | ((descriptor.flags & 0xF) << 52); rv = rv | ((descriptor.base & 0xFF0000) << 56); } void GDT_Load(){ gdt[0] = 0; SegmentDescriptor codeDescriptor; codeDescriptor.base = 0; codeDescriptor.limit = 0xFFFFF; codeDescriptor.accessByte = GDT_PRESENT | GDT_RING_KERNEL | GDT_DATA_OR_CODE_SEGMENT | GDT_CODE_SEGMENT | GDT_CODE_ONLY_RING_SET | GDT_CODE_READABLE | GDT_ACCESSED; codeDescriptor.flags = GDT_GRANULARITY | GDT_16BIT_PROTECTED_SEGMENT; SegmentDescriptor dataDescriptor; dataDescriptor.base = 0; dataDescriptor.limit = 0xFFFFF; dataDescriptor.accessByte = GDT_PRESENT | GDT_RING_KERNEL | GDT_DATA_OR_CODE_SEGMENT | GDT_DATA_SEGMENT | GDT_DATA_GROW_UP | GDT_DATA_WRITEABLE | GDT_ACCESSED; dataDescriptor.flags = GDT_GRANULARITY | GDT_16BIT_PROTECTED_SEGMENT; gdt[1] = GDTEntryToInt(codeDescriptor); gdt[2] = GDTEntryToInt(codeDescriptor); g_GDTR.offset = (uint32_t)gdt; g_GDTR.size = sizeof(gdt) - 1; loadGDT(g_GDTR.size, g_GDTR.offset); reloadSegments(); }
核心问题与修复方法
GDTEntryToInt函数缺失返回值
函数最后未添加return rv;,导致生成的GDT条目是随机垃圾值,直接破坏GDT结构。修复后函数末尾加上return rv;。GDT条目初始化错误
gdt[2] = GDTEntryToInt(codeDescriptor);错误地将数据段位置赋值为代码描述符,应改为gdt[2] = GDTEntryToInt(dataDescriptor);。段选择子使用错误
reloadSegments中jmp 0x00:.reload_cs使用空描述符的选择子0x00,无法正确加载代码段,应改为代码段选择子0x08(对应GDT第1个非空条目)。- 后续加载数据段寄存器时,同样不能用0x00,应使用数据段选择子0x10(对应GDT第2个非空条目)。修改后的
reloadSegments代码:global reloadSegments reloadSegments: jmp 0x08:.reload_cs .reload_cs: mov ax, 0x10 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax ret
确认GDTR结构匹配
确保gdtr的结构是低2字节为size,高4字节为基址,与汇编中mov [gdtr], ax(存size)、mov [gdtr+2], eax(存基址)的操作一致。
内容的提问来源于stack exchange,提问作者bessieTheBoy
相关产品推荐
相关产品推荐

