解决 ucore 实验 qemu 不断重启问题
做清华大学操作系统 ucore
实验 (x86版本),实验一编译后运行 qemu
发现系统不断重启,无法正常运行 kernel
。实验环境是 ubuntu 22.04
,gcc 11.4.0
,ld 2.38
。最终查证是链接脚本 kernel.ld
导致代码运行错误。解决方法需要小小修改 kernel.ld
。
问题查证
使用 gdb
单部跟踪,发现运行到下面的 lgdt
函数后,系统就重启了。怀疑是否是传入的 gdt_pd
参数有问题,导致触发了 CPU 保护异常机制。打印该变量发现其数值不正常。
代码里是这样设置参数的,上面打印的操作有明显问题。
static struct segdesc gdt[] = {
SEG_NULL,
[SEG_KTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_KERNEL),
[SEG_KDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_KERNEL),
[SEG_UTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_USER),
[SEG_UDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_USER),
[SEG_TSS] = SEG_NULL,
};
static struct pseudodesc gdt_pd = {
sizeof(gdt) - 1, (uint32_t)gdt
};
于是怀疑是代码出现了内存覆写问题,把这个变量写坏了。重新调试 kernel
,发现在刚进入 kern_init
时变量还是好的,结果紧接下面一句 memset(edata, 0, end - edata)
后,数值就被修改了。
于是查找 edata
和 end
是什么。在 kernel.ld
中是这样写的,意思是希望用 edata
和 end
来标记 bss
段的范围,然后在代码中将其清 0。
/* The data segment */
.data : {
*(.data)
}
PROVIDE(edata = .);
.bss : {
*(.bss)
}
PROVIDE(end = .);
gdt_pd
变量是有初始化值的,不应该在 bss
范围内,使用 readelf -s
来查看符号地址就可以看到 gdt_pd
确实是被 edata
和 end
包住了,所以是错误的。
使用 readelf -S
查看段信息
edata
标记的位置是 0x10f90
,而 .got.plt
的起始地址也是 0x10f950
。而在 0x10f950
后,bss
前还包含了 .got.plt
, .data.rel.local
, .data.rel.ro.local
,这部分内容也会被错误清 0。由于 gdt_pd
是个 static
变量,他就位于 data.rel.local
里,所以就被修改了。
解决方法
需要 edata
和 end
精确标记 .bss
的范围,不能包含其他的段,kernel.ld
修改如下
结果验证
修改完后再编译,就能看到正确结果了
由于中断向量表尚未设置好,此时发生定时器中断还是错误重启。所以先注释掉启用中断的代码再运行,就不会再重启了。
int
kern_init(void) {
// ....
clock_init(); // init clock interrupt
// intr_enable(); // enable irq interrupt
while(1);