因为学习需要,要看虚拟机Bochs的源代码。写随笔主要为了学习总结,其次是分享大家共同研究,大神勿喷,欢迎评论。
手头资料:bochs源代码,下于:bochs.sourceforge.net,还有喻强写的源码分析电纸书。
Bochs模拟器具有丰富的调试功能,今天总结Bochs的调试命令,以及相应的代码实现。
首先说明因为Bochs模拟的是X86系统,而Inter的CPU本身就支持丰富的调试功能,其中有调试寄存器(DB0-DB7)主要用来设置断点,并在运行过程中监视断点,和model-specific registers(MSRs)主要用来监视分支、终端、异常并记录当时地址。(具体内容可见Intel Architecture Software Developer’s Manual Volume 3)。
再讲Bochs的命令可以分为几类。
1:显示命令。如:info CPU,info eflags,reg,print stack:查看堆栈,x /nuf addr,查看线性地址内容等等,这些命令比较简单,因为此时模拟机进入停止状态,终端处理控制界面,程序流位于debug_main.cpp的bx_dbg_user_input_loop()函数中,该函数可以直接访问CPU、内存内容,所以直接按命令输出就行,比如:输出此时的EAX:
reg = BX_CPU(dbg_cpu)->get_reg32(BX_64BIT_REG_RAX);
dbg_printf("rax: 0x%08x:%08x ", GET32H(reg), GET32L(reg));
2:CTR+C,在模拟机运行中,你可以随时按CTR+C使其停止,进入控制状态,代码实现是:debug_main.cpp455行signal(SIGINT, bx_debug_ctrlc_handler);注册一个键盘终端函数,在处理函数中: bx_guard.interrupt_requested = 1;修改该值。而在cpu_loop()函数每次指令循环都会进行检查该值,具体:cpu.cpp971行:
if (bx_guard.interrupt_requested &&
(bx_guard.guard_for & BX_DBG_GUARD_CTRL_C))
{
BX_CPU_THIS_PTR guard_found.guard_found = BX_DBG_GUARD_CTRL_C;
return(1); // Ctrl-C pressed
} 从而从cpu_loop()调回控制界面,当然再跳回时会使用:SIM->set_display_mode(DISP_MODE_CONFIG);进行终端模式的转换。
3:trace on/off命令,反编译每条指令,代码实现:debug_main.cpp505行:
void bx_dbg_trace_command(bx_bool enable)
{
BX_CPU(dbg_cpu)->trace = enable;
dbg_printf("Tracing %s for %s
", enable ? "enabled" : "disabled",
BX_CPU(dbg_cpu)->name);
}该函数,令变量trace=1,该变量定义于cpu.h1140行: Bit8u trace;是CPU的内部变量,在cpu_loop()每次循环会检测改变量,具体代码,cpu.cpp269行:
#if BX_DISASM
if (BX_CPU_THIS_PTR trace) {
// print the instruction that is about to be executed
#if BX_DEBUGGER
bx_dbg_disassemble_current(BX_CPU_ID, 1); // only one cpu, print time stamp
#else
debug_disasm_instruction(BX_CPU_THIS_PTR prev_eip);
#endif
}从而实现功能。
4:show命令:
show - shows current show mode
show mode - show, when processor switch mode
show int - show, when interrupt is happens
show call - show, when call is happens
show ret - show, when iret is happens
show off - toggles off symbolic info
show dbg-all - turn on all show flags
show dbg-none - turn off all show flags
该命令要求,模拟机在运行命令的过程中不断检测,可能发生的事件,如:show call,一旦遇见call命令,就要输出call+当时的地址来进行提示,应为该命令和具体的指令有关,所以该命令的实现要在call指令的实现代码中。具体实现过程:debug_main.cpp826行
void bx_dbg_show_command(const char* arg)
{
。。。。。
else if(!strcmp(arg,"call")) {
if (dbg_show_mask & BX_DBG_SHOW_CALLRET) {
dbg_show_mask &= ~BX_DBG_SHOW_CALLRET;
dbg_printf("show calls/returns: OFF
");
}
。。。}其中函数主要改变变量dbg_show_mask的值。
而在本文件另一个函数中:会用到show_flag这个变量,该变量定义于cpu.h1127行
#if BX_DEBUGGER
Bit32u watchpoint;
Bit8u break_point;
#if BX_MAGIC_BREAKPOINT
Bit8u magic_break;
#endif
Bit8u stop_reason;
Bit8u trace_reg;
Bit8u mode_break;
bx_bool dbg_cpu_mode; /* contains current mode */
unsigned show_flag;
bx_guard_found_t guard_found;
#endif
Bit8u trace;
在call指令的执行代码中cpuctrl_xfer32.cpp(60):
#if BX_DEBUGGER
BX_CPU_THIS_PTR show_flag |= Flag_ret;
#endif
进行标记。
在cpu_loop()循环时cpu.cpp857行:
if(dbg_show_mask) {
int rv = bx_dbg_show_symbolic();
if (rv) return(rv);
}
对其进行检测,并返回debug_main.cpp文件里面的int bx_dbg_show_symbolic(void)函数,该函数里面会对call指令实现代码改变的show_flag变量进行检测:
int bx_dbg_show_symbolic(void){
。。。。。
if (dbg_show_mask & BX_DBG_SHOW_IRET) {
if(BX_CPU(dbg_cpu)->show_flag & Flag_iret) {
。。。}
从而知道发生了call指令,并输出。
总结1:可见该实现方案需要cpu_loop的每次循环都要返回该函数,处理完再继续循环,打打降低了运行效率,这也是Bochs虚拟机运行效率低的很大原因。
总结2:在这些命令里面,有一些较简单,是控制状态下的模拟机内容显示命令,还有一些命令作用于CPU的每次指令循环,如:trace-on,这要在cpu_loop()每次要加上相关的处理代码,最复杂的是和指令相关的检测命令,这需要相应指令实现代码的配合,和CPU每次循环相应的检测,一旦检测到便返回debug_main()中去。