3. 编译支持调试的内核:内核源码获取、配置选项与编译流程

要使用 KGDB 进行内核调试,首先需要构建一个包含调试符号且启用了 KGDB 相关功能的内核镜像。本章将详细讲解从源码获取、关键配置选项到完整编译流程的每一个步骤,确保你能够生成一个可被 QEMU 加载并支持远程 GDB 连接的内核。

3.1 内核源码获取与版本选择

推荐从 kernel.org 获取主线或长期支持(LTS)版本的内核源码。对于 KGDB 与 QEMU 的集成实践,建议选择 5.x 或 6.x 的 LTS 版本,这些版本对 KGDB 和 QEMU 的虚拟化支持最为成熟。

🔍 版本选择建议: 避免使用过于古老(如 2.6.x)或过于前沿的 RC 版本。LTS 版本通常具有更好的稳定性和文档支持。
# 以 Linux 6.1 LTS 为例
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz
tar -xf linux-6.1.tar.xz
cd linux-6.1

3.2 关键内核配置选项详解

内核配置是决定调试功能是否可用的核心步骤。以下表格列出了与 KGDB 和 QEMU 调试相关的所有关键选项及其作用。

配置选项 依赖/位置 说明 推荐值
CONFIG_DEBUG_INFO Kernel hacking → Compile-time checks 生成 DWARF 格式的调试符号,是 GDB 进行源码级调试的基础 y
CONFIG_DEBUG_INFO_DWARF5 Kernel hacking → Compile-time checks 使用 DWARF5 格式(较新工具链支持),提供更丰富的调试信息 y(可选)
CONFIG_KGDB Kernel hacking → KGDB 主开关,启用内核调试器核心框架 y
CONFIG_KGDB_SERIAL_CONSOLE Kernel hacking → KGDB 允许通过串口(QEMU 的 -serial)进行 KGDB 通信 y
CONFIG_KGDB_KDB Kernel hacking → KGDB 启用 KDB 内核调试器(可选,用于本地串口调试) n(纯 GDB 模式)
CONFIG_MAGIC_SYSRQ Kernel hacking 启用魔术键,可通过 SysRq-G 手动进入 KGDB 断点 y
CONFIG_DEBUG_KERNEL Kernel hacking 启用内核调试菜单,是许多调试选项的父开关 y
CONFIG_FRAME_POINTER General setup → Compiler options 保留帧指针寄存器,使堆栈回溯更可靠 y
CONFIG_RANDOMIZE_BASE Processor type and features 内核地址空间随机化(KASLR),会干扰 GDB 符号加载 n
⚠️ 关键冲突: CONFIG_RANDOMIZE_BASE(KASLR)必须关闭。如果启用,内核加载地址每次启动都会变化,导致 GDB 无法正确解析符号地址,断点设置会失败。

3.3 配置流程与自动化脚本

手动通过 menuconfig 逐项查找效率较低。推荐使用以下方法快速生成最小化调试配置。

方法一:基于现有配置修改

# 使用 x86_64 默认配置作为起点
make defconfig

# 通过脚本批量启用调试选项
scripts/config --enable CONFIG_DEBUG_INFO
scripts/config --enable CONFIG_DEBUG_INFO_DWARF5
scripts/config --enable CONFIG_KGDB
scripts/config --enable CONFIG_KGDB_SERIAL_CONSOLE
scripts/config --enable CONFIG_MAGIC_SYSRQ
scripts/config --enable CONFIG_DEBUG_KERNEL
scripts/config --enable CONFIG_FRAME_POINTER
scripts/config --disable CONFIG_RANDOMIZE_BASE

# 验证配置
make olddefconfig

方法二:使用最小化调试配置文件

你也可以直接创建一个 .config 文件,仅包含必要选项。以下是一个经过验证的最小配置片段:

CONFIG_64BIT=y
CONFIG_X86_64=y
CONFIG_SMP=y
CONFIG_NR_CPUS=2
CONFIG_HZ_100=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_PRINTK=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF5=y
CONFIG_FRAME_POINTER=y
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
CONFIG_MAGIC_SYSRQ=y
# 关闭 KASLR
CONFIG_RANDOMIZE_BASE=n
💡 提示: 使用 make olddefconfig 可以自动补全所有未显式设置的选项为默认值,避免因缺少依赖导致编译失败。

3.4 编译流程与产物验证

配置完成后,执行编译。建议使用 -j 参数加速多核编译。

# 编译内核和模块(使用 4 个并行任务)
make -j4

# 编译完成后,关键产物位于 arch/x86/boot/ 目录
ls -lh arch/x86/boot/bzImage
# 输出示例:-rw-r--r-- 1 user user 8.5M bzImage

# 调试符号文件位于 vmlinux(未压缩的内核镜像)
ls -lh vmlinux
# 输出示例:-rwxr-xr-x 1 user user 580M vmlinux

编译完成后,需要验证调试符号是否完整生成。

# 使用 readelf 检查调试信息段
readelf -S vmlinux | grep debug
# 应输出类似:
#  [26] .debug_aranges    PROGBITS
#  [27] .debug_info       PROGBITS
#  [28] .debug_abbrev      PROGBITS
#  [29] .debug_line        PROGBITS
#  [30] .debug_str         PROGBITS

# 使用 nm 检查 KGDB 相关符号
nm vmlinux | grep kgdb
# 应输出大量 kgdb_ 开头的符号,如 kgdb_breakpoint、kgdb_handle_exception
✅ 验证要点:
  • vmlinux 文件大小通常在几百 MB,包含完整符号;bzImage 是压缩后的启动镜像,约 8-15 MB。
  • 如果 readelf 未找到 .debug_* 段,说明 CONFIG_DEBUG_INFO 未生效,请检查配置。
  • 如果 nm 未找到 kgdb_breakpoint,说明 CONFIG_KGDB 未正确启用。

3.5 编译产物与调试文件关系

以下 SVG 图展示了内核编译产物的结构以及它们在 QEMU+KGDB 调试中的角色。

内核编译产物与调试文件关系图 内核编译产物与调试文件关系 Linux 内核源码 .config (调试选项) make -jN 编译 vmlinux (未压缩) 包含完整 DWARF 调试符号 bzImage (压缩镜像) QEMU 加载的启动内核 GDB 调试目标 gdb vmlinux QEMU 启动参数 -kernel bzImage KGDB 串口协议

3.6 常见编译问题与解决方案

编译过程中可能遇到以下典型问题,提前了解可以节省排错时间。

问题现象 可能原因 解决方案
No rule to make target 'debian/certs/signing_key.pem' 内核模块签名证书缺失 scripts/config --disable CONFIG_MODULE_SIG 或生成自签名证书
undefined reference to 'kgdb_breakpoint' KGDB 核心未编译进内核 确认 CONFIG_KGDB=y 且未设置为 m(模块)
vmlinux 文件极小(几 MB) CONFIG_DEBUG_INFO 未生效 检查 .config 中 CONFIG_DEBUG_INFO 是否为 y,运行 make olddefconfig 补全依赖
QEMU 启动后内核 panic 缺少根文件系统或控制台参数 添加 console=ttyS0 内核启动参数,并确保提供 initrd
💡 最佳实践: 建议在编译完成后,立即使用 file vmlinux 命令检查文件类型。如果输出包含 ELF 64-bit LSB executable, x86-64, with debug_info, not stripped,则说明调试符号已正确嵌入。