在开发基于虚拟化的调试器时遇到了很多问题,有些问题看似简单,但是解决起来很不容易。这是因为我们的调试器工作在VMXroot模式,而在这种模式下引发的崩溃性BUG,没有任何软件调试器可以对其进行跟踪分析,除非这个调试器同样运行在VMXroot模式。 专业的虚拟机开发人员可以使用硬件调试器来解决开发中遇到的问题。x86架构是支持外接硬件调试器的,类似于JTAG接口,这种调试器价格昂贵,大概两万美金左右,而且一般的主板并没有导出调试插座,只有Intel的一些高端主板才导出了配套的调试界面。 我在没有硬件调试器的情况下,遇到了一些很奇怪的问题,经过不断的摸索最终得到了解决。 5.1调试器运行后几秒内死机现象描述:在使用insmodvmxice.ko命令插入内核模块之后,系统在几秒钟内完全死机,游标不闪烁,不响应任何键盘操作。 故障诊断:如果崩溃发生在操作系统层应该会提示崩溃信息,而我遇到的这种死机是完全的死掉,所以死机可能是发生在VMExit之后。我尝试在进入VMExit处理函数时立即在屏幕上打印出计数器数值,这个计数器在每次进入VMExit之后都会递增,我以此可以证明死机发生点是VMExit中的某一段程序。结果出乎意料,在插入调试器内核模块后,屏幕上立即开始输出计数器数字,但是几秒后计数器停止输出,证明了死机是发生在VMExit,而且是崩溃性的,时钟中断都已经不再被处理。 这个故障我纠结了大概2个星期,后来重新阅读源代码时发觉,可能是页表出了问题,我写了一个小程序以验证我的思路。我首先在模块入口打印出CR3寄存器内容,然后再模块出口再次打印出CR3寄存器。我以此来判断内核模块的整个生命周期是否使用同一个页目录。 结果是页目录不同!后来经过详细分析认为这是正常的,因为在模块入口的函数被执行时,是属于insmod这个进程空间的,当入口函数执行完毕,insmod进程退出,这个页目录就顺其自然地被操作系统毁掉。而我的调试器依然在使用这个页目录,就发生了致命的错误:无法定位VMExit函数,因此在进行VMExit切换时一定会死机。 解决办法:在构造HOST端VMCS结构体时,为HOST重新分配一个页目录,并把当前页目录完整的复制过来,这样我们调试器就使用自己的页目录,操作系统在insmod进程退出后销毁insmod的页目录也不会影响我们调试器所使用的页目录了。 5.2呼出调试器后死机故障现象:按下F12,然后进行某项操作时突然死机,不响应任何键盘操作。 故障诊断:首先可以肯定死机是发生在VMExit处理函数中。我使用排除法。我猜测故障发生在某处,我就在他前面和后面分别加一个打印字符串的语句,这样,假如真是在这里发生死机,那么只会打印出前面的字符串,后面的字符串就打印不出来了。这种办法很有效,通过不断的排除,缩小范围,通常都可以定位到崩溃发生点。 解决办法:经过诊断,发觉是调试器访问了非法的内存页面,导致发生页异常,而我们的调试器没有接管处理页异常的中断,所以就死机了。我为HOST端构造了一个简单的页异常处理函数,只是简单的打开蜂鸣器,这样一旦发生页异常蜂鸣器就会长响,我就可以知道是发生了页异常错误。 解决页异常的方法,就是避免访问非法内存,所以,我在写代码时会特别注意,如果某段代码会可能访问非法内存,那么我会在访问前对页面进行判断,如果页面存在,则继续访问,如果页面不存在,则不访问,改为错误通知。 例如使用U 0这个指令试图反汇编内存地址0处的代码,而这个地址是不存在的,调试器就会返回INVALIDADDRESS的错误,而不是傻傻的真的去反汇编,导致崩溃的发生。
|