Linux下使用QEMU和OVMF镜像,配合GDB直接调试UEFI程序
以MdeModulePkg下的HelloWorld为目标调试程序举例。
步骤1 在HelloWorld.c中添加调试代码
主要是为了辅助调试,比如确定符号是否正确加载、程序是否正确执行等。
添加头文件:
#include <Library/DebugLib.h>
在主程序UefiMain中添加获取程序入口地址的日志。
Print(L"HelloWorld:My Entry Point is 0x%08x\n", (CHAR16 *)UefiMain);
步骤2 编译OVMF镜像
首先将HelloWorl添加到OvmfPkg中,方便一起编译。打开OvmfPkgX64.dsc在
[Components]后面添加:
MdeModulePkg/Application/HelloWorld/HelloWorld.inf {
<LibraryClasses>
DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
}
接着编译OVMF:
source edksetup.sh
build -p OvmfPkg/OvmfPkgX64.dsc -t GCC5 -a X64 -b DEBUG
步骤3 准备调试文件夹
mkdir ovmf_dbg
cd ovmf_dbg
mkdir hda-contents
cp ../Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd ./
cp ../Build/OvmfX64/DEBUG_GCC5/X64/HelloWorld.* ./hda-contents/
步骤4 启动QEMU运行UEFI Shell
qemu-system-x86_64 -s -pflash OVMF.fd -hadfat:rw:hda-contents/ -net none -debugcon file:debug.log -global isa-debugcon.iobase=0x402
进入到UEFI shell执行HelloWorld.efi程序。步骤1中添加的内容会被打印出来,同时去debug.log中查看HelloWorld.efi加载的位置,后面会用到。
fs0:
HelloWorld.efi
步骤5 启动GDB,挂载调试程序
再打开一个终端进入到子目录hda-contents,启动gdb。
gdb
(gdb) file HelloWorld.efi
Reading symbols from HelloWorld.efi...
(No debugging symbols found in HelloWorld.efi)
(gdb) info file
Symbols from "/home/code/edk2/_UDK_gdb/hda-contents/HelloWorld.efi".
Local exec file:
`/home/code/edk2/_UDK_gdb/hda-contents/HelloWorld.efi',
file type pei-x86-64.
Entry point: 0x124b
0x0000000000000240 - 0x0000000000001f00 is .text
0x0000000000001f00 - 0x0000000000002000 is .data
0x0000000000002000 - 0x00000000000021c0 is .rsrc
(gdb)
根据上一个步骤中得到镜像加载的地址:
计算代码段和数据段的偏移地址:
text = 0x5D3E000 + 0x240 = 0x5D3E240
data = 0x5D3E000 + 0x240 + 0x1F00 = 0x5D40140
计算完成后可以卸载GDB加载的efi文件。
接下来可以加载符号,设定断点,并将GDB挂载到QEMU上准备调试。
(gdb) add-symbol-file HelloWorld.debug 0x5d3e240 -s .data 0x5d40140
add symbol table from file "HelloWorld.debug" at
.text_addr = 0x5d3e240
.data_addr = 0x5d40140
(y or n) y
Reading symbols from HelloWorld.debug...
(gdb) break UefiMain
Note: breakpoint 1 also set at pc 0x5d3f21d.
Note: breakpoint 1 also set at pc 0x5d3f238.
Breakpoint 2 at 0x5d3f21d: UefiMain. (2 locations)
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x0000000006d27061 in ?? ()
(gdb) c
Continuing.
Breakpoint 1, UefiMain (ImageHandle=ImageHandle@entry=0x5d88d18, SystemTable=SystemTable@entry=0x75ec018)
at /home/code/edk2/MdeModulePkg/Application/HelloWorld/HelloWorld.c:42
42 {
(gdb)
步骤6 调试代码
环境准备好之后就可以尽情的使用gdb进行各种调试了。
以上是一个UEFI application的调试例子,驱动和应用的调试一样,都可以用上述的方法进行调试。