最近在研究 Intel ITP,这是可以进行源码级别调试的硬件工具。刚使用就遇到一个有意思的问题:如何让代码在需要的时候停下来。最直接的想法就是编写死循环,但是很快发现因为编译器的优化,当它发现你编写的代码是死循环,那么它会自动优化掉后面的它认为跑不到的代码。因此需要另辟蹊径找到更标准的方法。
找人请教了一下,对方指点说有一个 CpuBreakpoint() 函数可以使用。首先看这个函数的原型,在\MdePkg\Include\Library\BaseLib.h 中如下:
/** Generates a breakpoint on the CPU. Generates a breakpoint on the CPU. The breakpoint must be implemented such that code can resume normal execution after the breakpoint. **/ VOID EFIAPI CpuBreakpoint ( VOID );
具体实现代码可以在 \MdePkg\Library\BaseLib\X64\CpuBreakpoint.c 中找到
void __debugbreak (); #pragma intrinsic(__debugbreak) /** Generates a breakpoint on the CPU. Generates a breakpoint on the CPU. The breakpoint must be implemented such that code can resume normal execution after the breakpoint. **/ VOID EFIAPI CpuBreakpoint ( VOID ) { __debugbreak (); }
上面代码使用到了 Intrinsic ,对于这个关键字,我的理解是 VS 编译器插入汇编的方法(X64 是不可以直接使用 __ASM进行内嵌汇编的)。这个问题在之前的文章中涉及到,下面用实验来说明,在Shell Application,加入如下代码:
__asm{ int 3; }
在 -a IA32 的情况下,可以编译通过;在 -a X64 的情况下,编译期有如下错误提示:
d:\udk2017\AppPkg\Applications\BreakPointDemo\BreakPointDemo.c(45) : error C4235
: nonstandard extension used : '_asm' keyword not supported on this architecture
d:\udk2017\AppPkg\Applications\BreakPointDemo\BreakPointDemo.c(46) : error C2143
: syntax error : missing ';' before 'constant'
d:\udk2017\AppPkg\Applications\BreakPointDemo\BreakPointDemo.c(46) : warning C40
91: ' ' : ignored on left of 'int' when no variable is declared
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio 12.0
\Vc\bin\x86_amd64\cl.exe"' : return code '0x2'
Stop.
为此, VS 定义了一个关键字Intrinsic 用来内嵌汇编。“大多数的函数是在库中,Intrinsic Function却内嵌在编译器中(built in to the compiler)” 【参考1】。
为了进一步验证,需要看到编译之后的结果,我们在 \MdePkg\Library\BaseLib\BaseLib.inf 中加入如下指令:
[BuildOptions] MSFT:*_*_X64_CC_FLAGS = /Oi- /FAcs /Od
重新编译,会生成cpubreakpoint.c对应的 cod 文件。
PUBLIC CpuBreakpoint ; Function compile flags: /Odsp ; File d:\udk2017\mdepkg\library\baselib\x64\cpubreakpoint.c ; COMDAT CpuBreakpoint _TEXT SEGMENT CpuBreakpoint PROC ; COMDAT ; 36 : { 00000 66 90 npad 2 ; 37 : __debugbreak (); 00002 cc int 3 ; 38 : } 00003 c3 ret 0 CpuBreakpoint ENDP _TEXT ENDS END
可见,产生了一个 int 3 。
下面是完整的测试代码
运行之后会在 Shell 下面 Hang 住,运行调试工具,发现是停在PeiDxeSmmCpuException.c 文件中。这个让我很意外,因为在之前的调试工具中,看到 Int3 都是会停在调用 Int 3 的位置。
之后,仔细研读了一下代码,发现停的这个位置实际上是 Int 3 的处理函数。这个处理函数的结尾使用 CpuDeadLoop 来让代码停下来。
ITP 支持直接修改内存,因此,我们把 CPUDeadLoop 的汇编判断修改为 NOP 指令之后可以返回到调用处。
留下一个很类似的函数 CpuDeadLoop ,有兴趣的朋友可以去研究代码的具体实现。
ITP 是非常有力的武器,但是用起来还是非常麻烦,绝大多数情况下,串口输出信息依然是我们的首选。
参考:
1.http://www.cnblogs.com/wangguchangqing/p/5466301.html