前言
还记得我们在八篇文章中为了禁止CPU递归执行指令而取消的BX_NEXT_INSTR宏嚒,当时分析没有问题,但事后发现虚拟机跑不起来,其效果与日志如下:
解决方案一(失败)
我们先尝试着对比和正常的Bochs相比流程上存在哪些错误,我们用vscode的文本对比工具来进行对比,效果如下。经过仔细分析,其配置执行的流程已经被打乱,想要通过这种途径来分析代码似乎很困难。
解决方案二(成功)
我们只能从源代码中找答案,来看BX_NEXT_INSTR宏相关的上下文,发现其是一个 #IF..#ELSE..#ENDIF 这种数据结构。
#if BX_SUPPORT_HANDLERS_CHAINING_SPEEDUPS
...
#define BX_NEXT_INSTR(i) { \
BX_COMMIT_INSTRUCTION(i); \
if (BX_CPU_THIS_PTR async_event) return; \
++i; \
BX_EXECUTE_INSTRUCTION(i); \
}
#else
#define BX_NEXT_TRACE(i) { return; }
#define BX_NEXT_INSTR(i) { return; }
#define BX_LINK_TRACE(i) { return; }
#define BX_SYNC_TIME_IF_SINGLE_PROCESSOR(allowed_delta) \
if (BX_SMP_PROCESSORS == 1) BX_TICK1()
#endif
其实 BX_SUPPORT_HANDLERS_CHAINING_SPEEDUPS 这个宏,我们在第四篇编译调试模式时已经接触这个宏了。这个宏的意思从现在看,可以理解为:递归处理来提高CPU的处理速度。
我们在config.h文件中将这个宏设置为0,再重新编译,回看cpu.cc文件中的cpu执行路径,发现已经有些变化,走的是另一个版本。可以看到存在 "BX_DEBUGGER","BX_GDBSTUB" 这个宏,其是专门为调试器实现的,我们不需要自然不用处理。
for(;;) {
#if BX_DEBUGGER
if (BX_CPU_THIS_PTR trace)
debug_disasm_instruction(BX_CPU_THIS_PTR prev_rip);
#endif
// want to allow changing of the instruction inside instrumentation callback
BX_INSTR_BEFORE_EXECUTION(BX_CPU_ID, i);
RIP += i->ilen();
BX_CPU_CALL_METHOD(i->execute1, (i)); // might iterate repeat instruction
BX_CPU_THIS_PTR prev_rip = RIP; // commit new RIP
BX_INSTR_AFTER_EXECUTION(BX_CPU_ID, i);
BX_CPU_THIS_PTR icount++;
BX_SYNC_TIME_IF_SINGLE_PROCESSOR(0);
// note instructions generating exceptions never reach this point
#if BX_DEBUGGER || BX_GDBSTUB
if (dbg_instruction_epilog()) return;
#endif
if (BX_CPU_THIS_PTR async_event) break;
if (++i == last) {
entry = getICacheEntry();
i = entry->i;
last = i + (entry->tlen);
}
}
总结
在分析cpu时,我们一直苦恼于cpu对于递归的指令运行而无法很好的定位,然后自作聪明地想出了这个替换方案,然后在之后的运行中发现了问题,最后阅读源码发现bochs已经给出了一种很好的解决方案。
这教会了我们一个道理:遇到问题想要自己动手改源代码之前,先好好看看官方文档和代码,可能人家已经提供了一种解决办法!!!