You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

32位OS开发:切换保护模式后无法调用C函数kmain求助

排查32位保护模式内核调用失败的问题

看起来你已经成功闯过了进入保护模式的关卡,但内核调用这块卡壳了,咱们一步步拆解问题,定位根源:

1. 链接器格式错误(最核心的问题)

你当前用ld -m i386pe链接内核,这是Windows平台的PE可执行文件格式——这种格式会在二进制头部插入额外的PE结构信息,导致你的实际代码根本不是从0x100000开始的!PE头会占用一部分空间,kernel_entry.asm的代码被挤到了0x100000 + PE头大小的位置,你从0x100000调用的话,执行的是PE头里的垃圾数据,自然会直接崩溃退出。

解决方案:改用适合裸机内核的纯二进制格式链接,把链接命令替换成:

ld -m elf_i386 -Ttext 0x100000 --oformat binary kernel_entry.o kernel.o -o kernel.bin
  • -m elf_i386:指定针对32位x86的ELF目标文件
  • -Ttext 0x100000:直接将代码段起始地址锁定为0x100000
  • --oformat binary:输出纯二进制文件,无任何额外头部,确保代码精准落在你预期的内存地址上

2. 符号名与链接地址验证

虽然你的kernel_entry.asm里用了[extern _kmain](gcc默认会给C函数名加下划线,比如kmain变成_kmain,这点是对的),但可以用nm工具验证符号地址是否正确:

nm kernel.o

你应该能看到_kmain的地址是0x1000XX(相对于0x100000的偏移),确保链接后的符号位置符合预期。

3. 内核加载扇区数是否足够

你用dh=15加载15个扇区,总容量是15*512=7680字节,要确保你的kernel.bin大小不超过这个值。可以用dir kernel.bin(Windows)或ls -l kernel.bin(Linux)查看文件大小,如果超过了,把dh改成更大的数值(比如20)。

4. 保护模式调用逻辑修正

在boot_sect.asm的BEGIN_PM段里,你直接call KERNEL_OFFSET的逻辑是对的,但只有当kernel.bin开头就是kernel_entry.asm的代码时才会生效——换成上面的链接命令后,kernel.bin的第一个字节就是call _kmain指令,调用就能正确执行。

5. 无限循环测试验证

修改你的kmain函数,加上无限循环:

void kmain() {
    char* screen = (char*)0xb8000;
    screen[0] = 'X';
    while(1); // 无限循环挂起程序
}

如果链接正确,调用后程序会稳定挂在这个循环里,不会退出,这就说明地址问题已经解决了。

额外小提示

Windows的type命令合并文件时,偶尔会在文件之间插入额外空字符,建议用二进制模式合并:

copy /b boot_sect.bin + kernel.bin os-image

/b参数会确保以二进制方式合并,避免额外字符干扰。


内容的提问来源于stack exchange,提问作者Kaan99__

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:29:14