KGDB / GDB 远程调试
KGDB 原理
KGDB(Kernel GNU Debugger)在内核中实现了 GDB 远程协议,通过串口或网络连接到宿主机的 GDB,实现源码级内核调试。
目标机(被调试) 宿主机(调试)
Linux 内核
KGDB stub ←── 串口/网络 ──→ GDB
/dev/ttyS0 arm-linux-gnueabi-gdb vmlinux内核配置
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
CONFIG_KGDB_KDB=y # 内核调试器(无需 GDB 客户端)
CONFIG_DEBUG_INFO=y # 调试符号(必须)
CONFIG_FRAME_POINTER=y # 准确的调用栈
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y目标机配置
bash
# 内核启动参数(通过 bootargs 或 /proc/cmdline)
kgdboc=ttyS0,115200 kgdbwait
# kgdbwait:启动时等待 GDB 连接
# kgdboc:KGDB over console,指定串口和波特率
# 运行时触发断点(进入 KGDB)
echo g > /proc/sysrq-trigger
# 或
echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc宿主机 GDB 连接
bash
# 启动 GDB(使用带调试符号的 vmlinux)
arm-linux-gnueabi-gdb vmlinux
# 连接到目标机串口
(gdb) set remotebaud 115200
(gdb) target remote /dev/ttyUSB0
# 或通过网络(kgdboe)
(gdb) target remote 192.168.1.100:2345常用 GDB 命令
bash
# 查看调用栈
(gdb) bt
(gdb) bt full # 包含局部变量
# 查看当前代码
(gdb) list
(gdb) list my_driver_probe
# 设置断点
(gdb) break my_driver_probe
(gdb) break drivers/mydriver/my_driver.c:42
(gdb) break my_driver_write if len > 100 # 条件断点
# 查看变量
(gdb) print priv
(gdb) print *priv
(gdb) print priv->base_addr
(gdb) x/16xb priv->buf # 以十六进制查看内存
# 单步执行
(gdb) step # 进入函数
(gdb) next # 不进入函数
(gdb) finish # 执行到函数返回
(gdb) continue
# 查看寄存器
(gdb) info registers
(gdb) print $pc # 程序计数器
# 查看内核线程
(gdb) info threads
(gdb) thread 3
# 查看模块加载地址(用于调试可加载模块)
(gdb) info sharedlibrary调试可加载模块
bash
# 1. 在目标机上查看模块加载地址
cat /proc/modules | grep my_driver
# my_driver 16384 0 - Live 0xffffffffc0a00000
# 2. 在 GDB 中加载模块符号
(gdb) add-symbol-file my_driver.ko 0xffffffffc0a00000
# 3. 现在可以在模块函数上设置断点
(gdb) break my_driver_probeKDB — 无需宿主机的内核调试器
KDB 是内置的命令行调试器,直接在目标机控制台操作:
bash
# 进入 KDB
echo g > /proc/sysrq-trigger
# KDB 命令
[0]kdb> help # 帮助
[0]kdb> bt # 调用栈
[0]kdb> ps # 进程列表
[0]kdb> md 0xffff... 16 # 查看内存(16 字节)
[0]kdb> mm 0xffff... 0x1234 # 修改内存
[0]kdb> bp my_driver_probe # 设置断点
[0]kdb> go # 继续运行
[0]kdb> lsmod # 模块列表QEMU 调试(开发阶段推荐)
无需真实硬件,用 QEMU 模拟目标机:
bash
# 启动 QEMU,等待 GDB 连接(端口 1234)
qemu-system-arm \
-M virt \
-kernel arch/arm/boot/zImage \
-dtb my_board.dtb \
-append "kgdboc=ttyAMA0,115200 kgdbwait console=ttyAMA0" \
-serial tcp::1234,server,nowait \
-nographic
# 宿主机连接
arm-linux-gnueabi-gdb vmlinux
(gdb) target remote localhost:1234