3. 编译可调试内核:获取Linux内核源码、配置内核开启kgdb支持、编译内核与设备树
本章是实战流程的基石。要使用QEMU进行kgdb远程调试,必须拥有一份开启了调试符号且配置了kgdb支持的Linux内核镜像。我们将从获取源码开始,逐步完成配置、编译,并生成QEMU启动所需的设备树文件。
3.1 获取Linux内核源码
推荐从kernel.org获取长期支持(LTS)版本,稳定性与社区支持最佳。本课程以v6.1.y系列为例。
🔍 版本选择建议:LTS版本(如6.1.x、5.15.x)拥有更长的维护周期,且QEMU与kgdb的兼容性问题较少。避免使用最新的rc候选版或EOL版本。
# 下载指定版本源码包
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.76.tar.xz
# 解压
tar -xf linux-6.1.76.tar.xz
cd linux-6.1.76
解压后,源码目录结构如下表所示:
| 目录/文件 | 说明 |
|---|---|
arch/ |
体系结构相关代码(x86、arm64、riscv等) |
kernel/ |
核心内核代码(调度、进程管理、kgdb核心) |
drivers/ |
设备驱动 |
scripts/ |
编译与配置脚本 |
Makefile |
顶层构建文件 |
Kconfig |
内核配置系统入口 |
3.2 配置内核:开启kgdb与调试支持
内核配置是决定调试能力的关键步骤。必须显式开启KGDB、调试符号以及串口控制台支持。
3.2.1 配置流程
- 生成默认配置:基于目标架构(x86_64)生成基础配置。
- 手动调整:使用
menuconfig图形界面或直接修改.config文件。 - 验证关键选项:确保以下配置项已启用。
# 生成x86_64默认配置
make defconfig
# 进入图形化配置界面
make menuconfig
3.2.2 必须开启的配置项
以下配置项缺一不可,否则kgdb无法正常工作:
| 配置项 | 路径 | 说明 |
|---|---|---|
CONFIG_KGDB |
Kernel hacking → KGDB | kgdb核心支持 |
CONFIG_KGDB_SERIAL_CONSOLE |
Kernel hacking → KGDB: KGDB serial console | 通过串口进行kgdb I/O |
CONFIG_DEBUG_INFO |
Kernel hacking → Compile-time checks → Debug information | 生成DWARF调试符号(vmlinux必备) |
CONFIG_DEBUG_INFO_DWARF4 |
Kernel hacking → DWARF version | 推荐使用DWARF4格式,gdb兼容性好 |
CONFIG_MAGIC_SYSRQ |
Kernel hacking → Magic SysRq key | 用于触发kgdb断点(SysRq+g) |
CONFIG_SERIAL_8250_CONSOLE |
Device Drivers → Character devices → Serial → 8250/16550 console | 串口控制台,kgdb通信依赖此驱动 |
💡 快速验证:配置完成后,执行
grep -E "CONFIG_KGDB|CONFIG_DEBUG_INFO|CONFIG_MAGIC_SYSRQ" .config 确认所有选项均为 =y。
3.2.3 配置关系流程图
下图展示了内核配置中kgdb相关模块的依赖关系:
3.3 编译内核与设备树
配置完成后,进入编译阶段。对于QEMU调试场景,我们主要关注vmlinux(未压缩的内核映像,包含完整调试符号)和设备树(用于描述硬件拓扑)。
3.3.1 编译内核
使用make命令并行编译。建议开启-j参数利用多核加速。
# 编译内核与模块(-jN 中N为CPU核心数+1)
make -j$(nproc)
# 编译完成后,关键产出物位于:
# arch/x86/boot/bzImage —— 压缩后的内核镜像(QEMU启动用)
# vmlinux —— 未压缩内核(包含调试符号,gdb连接用)
⚠️ 常见错误:如果编译过程中出现
undefined reference to `kgdb_... 错误,请返回 menuconfig 检查 CONFIG_KGDB 及其子选项是否已正确开启。缺少 CONFIG_KGDB_SERIAL_CONSOLE 是导致链接失败的最常见原因。
3.3.2 编译设备树
设备树(Device Tree)用于向内核描述非可枚举的硬件信息。在x86平台上,QEMU通常使用ACPI,但为了调试与学习,我们也可以生成并加载设备树。对于ARM64或RISC-V平台,设备树是必需的。
以下以ARM64为例展示设备树编译过程(x86平台可跳过此步,直接使用QEMU默认的ACPI):
# 设置交叉编译环境(ARM64示例)
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
# 生成设备树二进制文件(.dtb)
make defconfig
make -j$(nproc) dtbs
# 产出物位于 arch/arm64/boot/dts/ 目录下
# 例如:arch/arm64/boot/dts/qemu/virt.dtb
3.3.3 编译产物清单
编译完成后,请确认以下文件已生成:
| 文件 | 路径 | 用途 |
|---|---|---|
bzImage |
arch/x86/boot/bzImage |
QEMU -kernel 参数加载的内核镜像 |
vmlinux |
源码根目录 | GDB file 命令加载的调试符号文件 |
virt.dtb (ARM64) |
arch/arm64/boot/dts/qemu/virt.dtb |
QEMU -dtb 参数加载的设备树 |
System.map |
源码根目录 | 内核符号表,辅助地址查找 |
3.4 编译流程总览
下图总结了从源码获取到产出物生成的完整编译流程:
3.5 本章小结
- 源码获取:选择LTS版本,解压后进入源码根目录。
- 配置要点:
CONFIG_KGDB、CONFIG_KGDB_SERIAL_CONSOLE、CONFIG_DEBUG_INFO三项必须同时开启。 - 编译产出:
bzImage用于QEMU启动,vmlinux用于GDB加载调试符号。 - 设备树:ARM64/RISC-V平台需额外编译
.dtb文件;x86平台通常使用ACPI,可跳过。