调试QEMU运行xv6时黑框闪烁问题的过程

前言

最近一段时间终于有了连续的空闲,重新拿起xv6,复习os内容,同时记录一下一个bug的调试经历。

问题

在看过xv6和类似os的相关文章后,我开始自己动手写一个minios。发现,在笔记本电脑上,qemu运行minios很顺利,但在我另一个台式机上,qemu运行minios出现问题,弹出的黑框一直在闪烁,同时无法运行到main()的位置。

分析

首先排除开发环境的问题。笔记本和台式机均在ubuntu18.04环境下,qemu版本相同,不应该是开发环境有问题。
确定是代码的问题,开始调试。

调试

查看kernel.asm文件,确定entry()0x7d87位置,于是将xv6(minios)qemu-gdb模式下运行,在该位置打个断点。

(gdb) b *0x7d87
Breakpoint 1 at 0x7d87
(gdb) c
Continuing.
The target architecture is assumed to be i386
=> 0x7d87:	call   *0x10018

Breakpoint 1, 0x00007d87 in ?? ()
(gdb) si
=> 0x10000c:	mov    %cr4,%eax
0x0010000c in ?? ()
(gdb) 
=> 0x10000f:	or     $0x10,%eax
0x0010000f in ?? ()
(gdb) 
=> 0x100012:	mov    %eax,%cr4
0x00100012 in ?? ()
(gdb) 
=> 0x100015:	mov    $0x103000,%eax
0x00100015 in ?? ()
(gdb) x/i 0x00100028
   0x100028:	mov    $0x80105790,%esp
(gdb) si
=> 0x10001a:	mov    %eax,%cr3
0x0010001a in ?? ()
(gdb) 
=> 0x10001d:	mov    %cr0,%eax
0x0010001d in ?? ()
(gdb) 
=> 0x100020:	or     $0x80010000,%eax
0x00100020 in ?? ()
(gdb) 
=> 0x100025:	mov    %eax,%cr0
0x00100025 in ?? ()
(gdb) 
=> 0x100028:	Error while running hook_stop:
Cannot access memory at address 0x100028
0x00100028 in ?? ()

在单步运行的情况下,发现qemu报错Cannot access memory at address 0x100028
报错的指令为mov $0x80105790,%esp,更新stack基址,不设计访问内存的相关问题,那就是说可能是无法读取指令。
下一步,通过objdunp -h kernel,分析指令为何无法读取。

kernel:     文件格式 elf32-i386

节:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00001f55  80100000  00100000  00001000  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .rodata       00000311  80101f60  00101f60  00002f60  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .stab         00000001  80102271  80102271  00003271  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  3 .data         00001770  80103000  80103000  00004000  2**12
                  CONTENTS, ALLOC, LOAD, DATA
  4 .bss          00004260  80104780  80104780  00005770  2**5
                  ALLOC
  5 .debug_line   00000edc  00000000  00000000  00005770  2**0
                  CONTENTS, READONLY, DEBUGGING
  6 .debug_info   00005b16  00000000  00000000  0000664c  2**0
                  CONTENTS, READONLY, DEBUGGING
  7 .debug_abbrev 0000171c  00000000  00000000  0000c162  2**0
                  CONTENTS, READONLY, DEBUGGING
  8 .debug_aranges 00000220  00000000  00000000  0000d880  2**3
                  CONTENTS, READONLY, DEBUGGING
  9 .debug_str    00000698  00000000  00000000  0000daa0  2**0
                  CONTENTS, READONLY, DEBUGGING
 10 .debug_ranges 00000248  00000000  00000000  0000e138  2**0
                  CONTENTS, READONLY, DEBUGGING
 11 .comment      00000029  00000000  00000000  0000e380  2**0
                  CONTENTS, READONLY
 12 .debug_loc    00001d48  00000000  00000000  0000e3a9  2**0
                  CONTENTS, READONLY, DEBUGGING

从上述中发现,kernel中,从.stab段开始,LMA地址就发生了巨大变化,相互之间不再是连续的地址。
这样就明确了,是kernel.ld链接脚本有一点问题,导致链接器运行时,没有将各个段之间连续排列。

	/* Include debugging information in kernel memory */
	.stab : {
		PROVIDE(__STAB_BEGIN__ = .);
		*(.stab);
		PROVIDE(__STAB_END__ = .);
		BYTE(0)		/* Force the linker to allocate space
				   for this section */
	}

	.stabstr : {
		PROVIDE(__STABSTR_BEGIN__ = .);
		*(.stabstr);
		PROVIDE(__STABSTR_END__ = .);
		BYTE(0)		/* Force the linker to allocate space
				   for this section */
	}

xv6相比,我的kernel.ld脚本多了BYTE(0)内容–我不知道是从哪个博客/知乎文章里复制粘贴的。
将这两个BYTE(0)删除后,kernel的各个段加载地址变为连续,qmeu成功运行,不再闪烁。

解释

花时间学习ld脚本语言后,总算对这个bug有了解释。
BYTE(0)命令表示,在位置计数器处存储一个单字节数据0。然而,此时的位置计数器.已经在第一行表明,是0x80100000,因此,从.stab后续的几个段,位置发生变化,与.text段不再连续,导致了后续的错误。
为了验证这个想法,对kernel.ld进行如下改变。

	. = ALIGN(0x1000);
	.stab : {
		PROVIDE(__STAB_BEGIN__ = .);
		*(.stab);
		PROVIDE(__STAB_END__ = .);
		BYTE(0)		
	}

kernel.ld后面,有再次对位置寄存器.进行赋值,将.stab剪贴到该赋值命令后,编译运行,发现qemu成功启动,没有闪烁。
表明上述解释是正确的。

总结

遇到一个不同平台上运行效果不一致的问题,着实伤脑筋。但调试解决后,收获的成长和喜悦也是巨大的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值