自制业余操作系统:保护模式下加载平二进制文件执行遇通用保护错误
问题排查与解决方案
核心原因分析
触发中断13(通用保护错误),大概率是以下几个问题导致:
1. 内存页可执行权限未配置
在保护模式(尤其是启用分页的场景下),如果0x600000所在的内存页未设置可执行权限,CPU尝试执行该地址代码时会直接触发GPF。你需要检查页表项的权限位,确保目标页的NX(禁止执行)位未被置位(x86-64架构),或者在32位x86模式下,页表项权限标记允许代码执行。
2. 函数包含位置相关代码
你的aa函数调用了printkc,编译生成的汇编指令大概率是相对跳转/调用(比如call printkc是基于当前指令指针的相对偏移)。当把函数复制到0x600000后,原本指向printkc的相对偏移会指向错误内存位置,执行非法操作触发GPF。
如果函数后续涉及全局变量访问,同样会因为相对寻址失效引发问题。
3. 函数长度估算错误
你手动指定了45字节的复制长度,但实际编译后的aa函数长度可能不等于45字节。若复制的指令不完整,执行到一半会遇到非法操作码,间接触发GPF。
具体修复步骤
- 配置内存页权限:修改页表初始化代码,将0x600000所在页标记为可读、可写、可执行(内核态内存需符合内核段权限要求)。
- 编译为位置无关代码(PIC):如果要动态加载执行代码,需将函数编译为位置无关代码。GCC中可添加
-fPIC或-fpie编译选项,生成的指令会使用绝对寻址或基于寄存器的相对寻址,复制到任意地址都能正常执行。 - 准确获取函数长度:通过
objdump -d your_kernel.elf | grep -A 20 "aa:"查看汇编输出,统计aa函数的实际字节数;也可在链接脚本中定义函数起止符号,或用__attribute__((section("myfunc")))将函数放到单独段,通过段起止地址计算长度。 - 检查段描述符:若未启用分页,需确认GDT中对应0x600000地址范围的段描述符,已设置代码可执行的类型属性。
举个编译位置无关代码的Makefile示例:
CFLAGS += -fPIC
内容的提问来源于stack exchange,提问作者Soubhik Biswas
相关产品推荐
相关产品推荐

