Step to UEFI (125)CPUBreakPoint 研究

最近在研究 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 的位置。

cpb1

之后,仔细研读了一下代码,发现停的这个位置实际上是 Int 3 的处理函数。这个处理函数的结尾使用 CpuDeadLoop 来让代码停下来。
ITP 支持直接修改内存,因此,我们把 CPUDeadLoop 的汇编判断修改为 NOP 指令之后可以返回到调用处。

cbp2

留下一个很类似的函数 CpuDeadLoop ,有兴趣的朋友可以去研究代码的具体实现。
ITP 是非常有力的武器,但是用起来还是非常麻烦,绝大多数情况下,串口输出信息依然是我们的首选。

参考:
1.http://www.cnblogs.com/wangguchangqing/p/5466301.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注