为什么C代码无法访问汇编定义的Tss变量且写入时触发页错误?
问题根因
你遇到的页错误完全是符号地址没有匹配更高半内核的虚拟地址偏移导致的,细节对应如下:
- 你的内核采用了更高半设计,
KERNEL_VMA固定为0xffff800000000000,所有内核代码/数据都需要运行在这个偏移后的虚拟地址空间,页表也只映射了这个高地址范围,低地址(<几十M的物理地址范围)没有做虚拟地址映射。 - 汇编中导出的
Tss符号默认是低地址的物理地址值(gdb返回的0x20103a就是未加偏移的物理地址),C代码直接引用这个低地址访问,必然触发未映射页错误。你打印的异常信息里read_cr2()返回0x20103EH,刚好是Tss地址+rsp0成员的4字节偏移,完全匹配这个问题特征。 - 你在
_start64h启动代码里只给GDT基址加了KERNEL_VMA偏移,没有处理Tss、TssDesc这类汇编导出的全局符号的地址偏移,导致C侧拿到的符号地址全是不可访问的低物理地址。
修复方案
快速修复(修改C代码)
手动给Tss地址加KERNEL_VMA偏移即可,修改proc.c的代码:
#include <stdint.h> // 确保此处可以引用到KERNEL_VMA的宏定义,通常是0xffff800000000000 extern struct TSS Tss; // 计算得到Tss的高半虚拟地址 #define TSS_VA ((struct TSS*)((uintptr_t)&Tss + KERNEL_VMA)) static void set_tss(struct Process *proc) { TSS_VA->rsp0 = proc->stack + STACK_SIZE; }
规范修复(修改链接逻辑)
直接在链接脚本中将所有内核段的基址设置为KERNEL_VMA,这样汇编导出的所有符号、C代码编译的符号自动就是高半虚拟地址,不需要手动加偏移,是长期开发的最优方案。
额外检查点
- 确认
struct TSS的内存布局和汇编中定义的Tss结构完全一致,避免成员偏移错误。 - 确认Tss所在的内存段在页表中被标记为可写,不要出现只读权限限制。
内容的提问来源于stack exchange,提问作者JustOneMan
相关产品推荐
相关产品推荐

