有段时间一直沉迷于裸机上跑程序,学习怎么让一个操作系统跑起来。使用过VirtualPC(现在被微软收购了),VMware,VirtualBOX,Bochs,最终还是觉得Bochs比较好用。它的最大好处是集合了完整的调试环境,对指令是软件解析的,速度虽然慢,但对程序员来说,一步步看代码是如何执行的,非常契合需求。
Bochs的文档非常的完整,使用起来也比较方便。我只针对如何调试Foxdisk,以及如何下断点进行记录,其他的可以查其自带的帮助文档。我使用的是Bochs2.2.6,帮助文档在Bochs-2.2.6\docs目录下,目前已经到了版本2.6.9,估计也差不多。
第一步是制作磁盘镜像。Bochs本身提供了镜像制作工具bximage,使用起来也比较简单。当然,也可以使用其他工具制作,比如windows下的winimage或者Linux下直接用dd命令。以制作一个4G虚拟硬盘文件为例,命令如下:
..\bximage.exe
-hd -size=4096 -mode=growing -q myhd.img
Bochs的菜单栏中(我使用的是windows)提供了Disk
Image Creation Tool的快捷菜单,其实就是bximage。
具体还可以参考帮助文档中Bochs User Manual-8.1 How to make a simple disk image。
第二步是编辑bochsrc.bxrc文件,指定需要启动的设备。可以启动虚拟软盘、虚拟光盘或者虚拟硬盘。找个例子看一下,修改为自己想启动的设备即可。比如虚拟硬盘,boot: c即可启动。需要提醒的是,在Foxdisk中,为了尽可能的模拟真实硬件,磁盘的参数要注意修改,一般spt=63,heads=16即可。很早以前的版本中,我的代码中没有主要到这些参数是可变的,虚拟机调试就出问题。现在的版本中,硬盘驱动的代码已经修补了这个问题,如果不去调试硬盘驱动,不必理会这些参数。双击bochsrc.bxrc即可启动,如图1,启动了一个DOS环境下的软件,当年和联想开发的一款安全计算机的界面。
图1 Bochs调试DOS程序
另外,还有一个问题,如何把需要使用的文件拷贝到虚拟硬盘中呢?我的方法比较简单,我做了一个虚拟软盘,使用winimage将需要的文件拷贝到虚拟软盘中。启动虚拟硬盘的时候,虚拟软盘作为一个挂接设备存在(注意修改bochsrc.bxrc,比如
floppya: 1_44=LBDEBUG.IMG, status=inserted ),从软盘中将文件拷贝过去即可。
一切准备就绪后,就可调试了。最后一步,启动bochsdbg.exe,命令行如下:
…\Bochs-2.2.6\bochsdbg.exe -q -f
bochsrc.bxrc。断在了bios最早的位置f000:fff0上,这时就可以进入调试了。如图2所示。
图2 bochsdbg界面
如何在bochs中对Foxdisk进行调试呢?最常用的是定位在mbr区,跟踪Foxdisk的运行。即利用bochs可以对内存下断点,定位想调试的位置,进行跟踪。典型的代码运行位置,一个是mbr的0x7c00,以及Foxdisk的Code_Seg(也即0x7000处)。以mbr定位为例:
<bochs:1> lb 0x7c00 //下断点
<bochs:2>c //继续执行直到遇到断点
参考Foxdisk的代码,对照跟踪,很容易理解系统的运作原理。
总觉得bochs的调试工具和linux下gdb很类似,为便于查询,将常用的一些命令记录下来,详细的仍可参考其官方文档。
执行控制命令
c 继续执行,遇到断点将停止
step [count] 执行count条指令, 默认为1条
s [count] step的缩写
Ctrl-C 停止执行,返回命令行
Ctrl-D 执行完所有命令后,退出
q(quit) 退出调试器
设置断点
vb(vbreak) seg:off 在指定的虚拟地址(段+偏移)设置断点,在保护模式下也可以使用
lb(lbreak) addr 在一个线性地址设置断点
pb(pbreak) [*] addr 在一个物理地址设置断点
info break 显示所有断点状态
d(delete) n 删除一个断点
查看内存
x /nuf addr 查看一个线性地址的内存
xp /nuf addr 查看一个物理地址的内存
n 显示多少个单位的内存
u 内存单位大小,可以是
b: 字节; h: 字(2个字节); w: 双字(4个字节); g : 4字(8字节)
f 打印格式,可以是
x: 16进制格式打印; d: 10进制格式打印; u: 无符号10进制格式打印;
o: 8进制格式打印; t: 2进制格式打印
寄存器操作
Info registers 列举CPU整型寄存器内容
dump_cpu 查看所有与CPU相关的寄存器状态
where 打印当前call stack
set $reg=val 改变寄存器的内容,如set $edx=25
最奇怪的是,在安装Foxdisk的时候,颜色会不对,到现在我也没搞清楚怎么回事。