EDK II RISC-V调试:QEMU与GDB联合调试环境搭建
【免费下载链接】edk2 EDK II 项目地址: https://gitcode.com/gh_mirrors/ed/edk2
引言:RISC-V固件调试的痛点与解决方案
在RISC-V架构(精简指令集计算机第五代架构)的固件开发中,开发者常面临三大挑战:缺乏成熟调试工具链、硬件平台适配复杂、固件启动流程不透明。本文将系统讲解如何基于EDK II(EFI开发工具包第二代)构建QEMU(快速模拟器)与GDB(GNU调试器)的联合调试环境,解决RISC-V UEFI(统一可扩展固件接口)固件的开发调试难题。
完成本文学习后,你将掌握:
- EDK II RISC-V平台的调试配置方法
- QEMU虚拟化环境的调试参数设置
- GDB远程调试会话的建立与常用调试技巧
- 固件启动流程中的关键断点设置策略
环境准备:工具链与源码获取
必要工具链安装
| 工具 | 版本要求 | 用途 |
|---|---|---|
| riscv64-unknown-elf-gcc | ≥10.2.0 | RISC-V交叉编译器 |
| qemu-system-riscv64 | ≥6.0.0 | RISC-V系统模拟器 |
| gdb-multiarch | ≥9.2 | 多架构调试器 |
| python3 | ≥3.8 | 辅助脚本执行 |
| git | ≥2.25.0 | 源码管理 |
在Ubuntu系统中可通过以下命令安装基础依赖:
sudo apt update && sudo apt install -y \
gcc-riscv64-unknown-elf \
qemu-system-misc \
gdb-multiarch \
git python3
EDK II源码获取
通过以下命令克隆EDK II仓库及子模块:
git clone https://gitcode.com/gh_mirrors/ed/edk2.git
cd edk2
git submodule update --init
EDK II调试版本构建
配置构建环境
初始化EDK II构建环境:
source edksetup.sh
调试版本编译参数配置
创建自定义构建配置文件Conf/tools_def.txt,添加RISC-V调试选项:
[RISCV64]
...
DEFINE GCC_RISCV64_DEBUG_FLAGS = -g -O0 -fno-omit-frame-pointer
...
构建RISC-V调试固件
使用以下命令构建QEMU目标的RISC-V64调试版本固件:
build -a RISCV64 -t GCC -p OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc -b DEBUG
构建完成后,在Build/RiscVVirtQemu/DEBUG_GCC/FV目录下生成:
QEMU_EFI.fd:RISC-V UEFI固件镜像QEMU_VARS.fd:非易失性变量存储镜像
QEMU调试环境配置
调试参数解析
QEMU启动命令的关键调试参数说明:
| 参数 | 作用 |
|---|---|
| -s | 等价于-gdb tcp::1234,开启GDB远程调试端口 |
| -S | 启动后暂停CPU,等待GDB连接 |
| -machine virt | 使用RISC-V virt平台 |
| -bios | 指定UEFI固件镜像路径 |
| -serial stdio | 将串口输出重定向到终端 |
启动QEMU调试会话
创建调试启动脚本run_qemu_debug.sh:
#!/bin/bash
qemu-system-riscv64 \
-machine virt \
-cpu rv64 \
-m 2G \
-smp 4 \
-bios Build/RiscVVirtQemu/DEBUG_GCC/FV/QEMU_EFI.fd \
-serial stdio \
-s -S
添加执行权限并运行:
chmod +x run_qemu_debug.sh
./run_qemu_debug.sh
成功启动后,QEMU将显示以下信息并暂停等待GDB连接:
QEMU 6.2.0 monitor - type 'help' for more information
(qemu)
GDB调试会话建立
调试脚本编写
创建GDB初始化脚本debug.gdb:
# 设置架构
set architecture riscv:rv64
# 加载符号表
file Build/RiscVVirtQemu/DEBUG_GCC/RiscVVirtQemu.fd
# 设置远程目标
target remote localhost:1234
# 设置断点
break SecMain
break DxeMain
break BdsInitialize
# 继续执行
continue
启动GDB调试会话
在新终端中执行:
gdb-multiarch -x debug.gdb
GDB将连接到QEMU并在SecMain函数处中断,显示类似以下信息:
Breakpoint 1, SecMain () at OvmfPkg/RiscVVirt/Sec/SecMain.c:42
42 {
(gdb)
高级调试技巧
关键阶段断点设置
EDK II启动流程中的关键断点位置:
设置符号断点示例:
# 在PEI核心入口处设置断点
break PeiCoreEntryPoint
# 在内存初始化完成后设置断点
break InitializeMemory
# 继续执行到下一个断点
continue
内存与寄存器查看
查看RISC-V通用寄存器:
(gdb) info registers
zero 0x0 0
ra 0x80000000 2147483648
sp 0x80f00000 2155890688
gp 0x80004e40 2147492416
tp 0x0 0
t0 0x1 1
t1 0x80001234 2147487284
...
查看内存内容(以16进制和ASCII格式):
# 查看0x80000000处的内存,显示16字节
x/16xb 0x80000000
# 以32位整数格式查看4个值
x/4dw 0x80000000
固件启动日志获取
配置QEMU串口重定向到文件,保存完整启动日志:
qemu-system-riscv64 ... -serial file:debug.log
使用GDB命令将调试信息输出到文件:
set logging file gdb_debug.log
set logging on
常见问题解决
断点无法命中
问题现象:设置DxeMain断点后无法命中。
解决方案:
- 确认构建类型为DEBUG(非RELEASE)
- 检查符号表是否正确加载:
info functions DxeMain - 尝试使用地址断点替代符号断点:
# 获取DxeMain函数地址
info address DxeMain
# 假设返回0x80200000,则设置地址断点
break *0x80200000
QEMU启动卡死
问题现象:QEMU启动后无输出,CPU占用率100%。
解决方案:
- 检查固件镜像路径是否正确
- 确认QEMU版本支持RISC-V virt平台(≥6.0.0)
- 减少分配内存:
-m 1G(某些环境下2G内存可能导致问题)
GDB连接超时
问题现象:target remote localhost:1234失败,提示连接超时。
解决方案:
- 确认QEMU已使用
-s参数启动 - 检查本地1234端口是否被占用:
netstat -tln | grep 1234 - 尝试更换调试端口:
qemu ... -gdb tcp::1235,GDB中对应使用target remote localhost:1235
调试实战:跟踪RISC-V中断处理流程
中断向量表设置断点
在RISC-V中断向量表初始化处设置断点:
break InitializeInterrupts
continue
跟踪异常处理流程
查看中断处理函数调用栈:
# 触发中断后查看调用栈
bt
# 查看当前执行上下文
frame 0
# 反汇编当前函数
disassemble
总结与进阶
环境搭建关键点回顾
- 确保EDK II构建时启用调试符号(
-b DEBUG参数) - QEMU必须使用
-s -S参数启动以支持GDB调试 - GDB需加载正确的符号表文件以解析函数名和变量
进阶学习路径
- 源码级调试:结合EDK II源码理解固件启动流程
- UEFI协议调试:使用
uefi_debug库函数打印协议信息 - 设备驱动调试:针对特定硬件设备驱动设置条件断点
- 覆盖率分析:结合
gcov工具分析测试覆盖率
常用调试命令速查表
| 功能 | GDB命令 |
|---|---|
| 设置断点 | break <函数名/地址> |
| 查看断点 | info breakpoints |
| 单步执行 | step 或 s |
| 继续执行 | continue 或 c |
| 查看调用栈 | backtrace 或 bt |
| 打印变量 | print <变量名> 或 p <变量名> |
| 内存查看 | x/<n><f><u> <地址> |
| 修改变量 | set var <变量名>=<值> |
| 反汇编 | disassemble <函数名> |
通过本文介绍的方法,开发者可以构建完整的EDK II RISC-V调试环境,有效定位和解决固件开发中的问题。建议结合EDK II官方文档和RISC-V架构手册深入理解固件运行机制,提升调试效率。
附录:EDK II RISC-V调试配置文件
RiscVVirtQemu.dsc关键调试配置
以下是OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc中与调试相关的关键配置:
[Defines]
...
DEFINE DEBUG_ON_SERIAL_PORT = TRUE
DEFINE BUILD_SHELL = TRUE
...
[BuildOptions]
...
!ifdef $(SOURCE_DEBUG_ENABLE)
GCC:*_*_RISCV64_GENFW_FLAGS = --keepexceptiontable
!endif
...
QEMU启动脚本完整版
#!/bin/bash
# 调试版固件路径
FIRMWARE_PATH="Build/RiscVVirtQemu/DEBUG_GCC/FV/QEMU_EFI.fd"
# 变量存储路径
VARS_PATH="Build/RiscVVirtQemu/DEBUG_GCC/FV/QEMU_VARS.fd"
# 调试端口
DEBUG_PORT=1234
# 检查固件文件是否存在
if [ ! -f "$FIRMWARE_PATH" ]; then
echo "错误:固件文件 $FIRMWARE_PATH 不存在,请先构建调试版本"
exit 1
fi
# 创建变量存储文件(如果不存在)
if [ ! -f "$VARS_PATH" ]; then
dd if=/dev/zero of="$VARS_PATH" bs=1M count=64
fi
# 启动QEMU调试会话
qemu-system-riscv64 \
-machine virt \
-cpu rv64 \
-m 2G \
-smp 4 \
-bios "$FIRMWARE_PATH" \
-drive file="$VARS_PATH",format=raw,if=virtio \
-serial stdio \
-monitor telnet:localhost:5555,server,nowait \
-gdb tcp::$DEBUG_PORT \
-S
【免费下载链接】edk2 EDK II 项目地址: https://gitcode.com/gh_mirrors/ed/edk2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



