解决 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);

![[office] excel2016怎么求最大值和最小值 #职场发展#知识分享](https://img-blog.csdnimg.cn/img_convert/b2e30ad30245887ba39845b64fdd2c67.jpeg)

















