4. 内核调试符号与配置:开启调试选项与生成关键文件

内核调试的第一步是让内核在编译时携带足够的符号与追踪信息。没有这些信息,gdb、ftrace、perf 等工具将无法将地址解析为函数名、变量名或行号。本章详细讲解必须开启的内核配置项,以及编译后生成的 vmlinux 与 System.map 的作用与获取方法。

4.1 核心调试配置项一览

以下配置项是内核调试的基石,必须在 make menuconfig 或直接编辑 .config 时开启:

配置项 功能 调试场景
CONFIG_DEBUG_INFO 生成 DWARF 格式的调试符号(.debug_* 段) gdb/kgdb、crash、addr2line
CONFIG_KALLSYMS 导出内核符号表,支持地址到符号的解析 ftrace、perf、dmesg 栈回溯
CONFIG_KALLSYMS_ALL 额外导出静态函数与局部符号 深度 ftrace 追踪、内核函数级调试
CONFIG_FTRACE 开启内核内置追踪框架 函数调用追踪、延迟分析、事件追踪
CONFIG_FUNCTION_TRACER 启用函数级追踪(mcount/fentry) ftrace function tracer
CONFIG_DEBUG_KERNEL 内核调试主开关,依赖项自动选择 所有调试功能的父选项
提示: 生产环境通常关闭 CONFIG_DEBUG_INFO 以减小内核体积;调试环境建议开启 CONFIG_DEBUG_INFO_REDUCED 作为折中方案,它只保留部分符号,体积减少约 50%。

4.2 配置操作流程

  1. 进入配置界面:
    cd /path/to/linux-source
    make menuconfig
  2. 依次开启以下路径:
    • Kernel hacking → Compile-time checks and compiler options → Compile the kernel with debug info(选中 CONFIG_DEBUG_INFO)
    • Kernel hacking → Kernel symbol table → Export kernel symbols(选中 CONFIG_KALLSYMS 与 CONFIG_KALLSYMS_ALL)
    • Kernel hacking → Tracers → Enable ftrace framework(选中 CONFIG_FTRACE 及 CONFIG_FUNCTION_TRACER)
  3. 验证配置:
    grep -E "DEBUG_INFO|KALLSYMS|FTRACE" .config
    输出应全部为 =y,例如:
    CONFIG_DEBUG_INFO=y
    CONFIG_KALLSYMS=y
    CONFIG_KALLSYMS_ALL=y
    CONFIG_FTRACE=y
    CONFIG_FUNCTION_TRACER=y
注意: 如果使用模块化编译(=m),CONFIG_DEBUG_INFO 对模块同样生效,但 CONFIG_KALLSYMS 必须为 =y 才能在内核启动时加载完整符号表。

4.3 编译产物:vmlinux 与 System.map

完成配置并执行 make -j$(nproc) 后,源码根目录下会生成两个关键调试文件:

文件 类型 内容 调试用途
vmlinux ELF 可执行文件(未压缩) 包含完整代码、数据、调试符号段(.debug_*) gdb 加载目标文件、crash 分析、addr2line 反查行号
System.map 纯文本符号表 每行格式:地址 类型 符号名 手动地址查询、脚本解析、ftrace 过滤函数名
关键区别: vmlinux 包含二进制指令与调试信息,体积较大(通常 500MB+);System.map 仅为文本映射,体积约 5-10MB,适合快速查看符号地址。

4.4 调试符号与符号表的关系(SVG 架构图)

下图展示了从内核源码到调试文件的编译流程,以及 vmlinux 与 System.map 在调试工具链中的位置:

内核调试符号生成与使用架构 内核调试符号生成与使用架构 Linux 内核源码 .config (调试选项开启) make (编译 + 链接) vmlinux ELF + DWARF 调试符号 System.map 文本符号表 (地址→符号) gdb / crash / addr2line ftrace / perf / dmesg 脚本解析 / 手动查地址 输入 配置 编译 产物 使用

4.5 验证调试符号是否生效

编译完成后,使用以下命令验证调试信息是否完整:

# 检查 vmlinux 是否包含调试段
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

# 检查 System.map 是否包含关键符号
grep " start_kernel" System.map
# 输出示例:ffffffff81000000 T start_kernel

# 使用 addr2line 验证地址解析
addr2line -e vmlinux -f ffffffff81000000
# 输出示例:start_kernel /path/to/linux/init/main.c:892
实用技巧: 如果 vmlinux 体积过大(超过 1GB),可以使用 objcopy --only-keep-debug vmlinux vmlinux.debug 剥离调试符号到单独文件,运行时通过 gdb -s vmlinux.debug -e vmlinux 加载。

4.6 常见问题与排查

问题现象 原因 解决方案
readelf -S vmlinux 无 .debug_* 段 CONFIG_DEBUG_INFO 未开启或编译时被覆盖 检查 .config 确认 CONFIG_DEBUG_INFO=y,重新 make clean && make
System.map 为空或缺失 CONFIG_KALLSYMS 未开启 开启 CONFIG_KALLSYMS 与 CONFIG_KALLSYMS_ALL 后重新编译
ftrace 无法显示函数名 内核运行时未加载 kallsyms 或 /proc/kallsyms 被限制 确认 CONFIG_KALLSYMS=y,且 kernel.kptr_restrict=0
编译时间过长 CONFIG_DEBUG_INFO 增加大量 DWARF 数据 使用 CONFIG_DEBUG_INFO_REDUCED 或 CONFIG_DEBUG_INFO_SPLIT 加速

4.7 本章小结

  • 必须开启 CONFIG_DEBUG_INFO、CONFIG_KALLSYMS、CONFIG_FTRACE 三大配置项,才能获得完整的调试能力。
  • vmlinux 是带调试符号的 ELF 文件,供 gdb/crash 等工具使用;System.map 是纯文本符号表,供 ftrace/perf 及脚本快速查询。
  • 编译后务必使用 readelf 和 addr2line 验证调试符号是否生效,避免调试时“符号找不到”的困境。
  • 生产环境与调试环境的配置应分离,避免将带完整符号的内核部署到线上。