Linux内核调试之KGDB
KGDB调试ARM-LINUX内核
目标机内核配置
1.配置kgdb
1 | Kernel hacking ---> |
2.配置vmlinux符号信息
1 | Kernel hacking ---> |
gdb移植
下载gdb源码(以8.0.1版本为例):
1 | wget http://ftp.gnu.org/gnu/gdb/gdb-8.0.1.tar.xz |
安装libexpat:
1 | apt install libexpat1-dev |
解压并配置gdb源码:
1 | tar -xf gdb-8.0.1.tar.xz |
编译和安装gdb:
1 | make |
安装完成后在配置指定的prefix目录中的bin目录下可找到目标程序arm-linux-gdb。
调试实例
Qemu ARM虚拟机调试(通过tcp调试)
启动ARM虚拟机,启动内核gdb服务,并将gdb服务绑定到本地tcp的1234端口,启动参数如下:
1 | qemu-system-arm -M vexpress-a9 -m 512M -kernel zImage -dtb vexpress-v2p-ca9.dtb -nographic -append "root=/dev/mmcblk0 rw console=ttyAMA0" -sd virtsd.ext2 -net nic,vlan=0 -net tap,vlan=0 -S -s |
上面启动虚拟机的参数中,-S表示启动后冻结CPU,在gdb客户端通过c命令启动,-s表示-gdb tcp::1234,表示默认端口1234,如果不想用默认端口,则可以使用-gdb tcp::1234选项,修改后面的1234。
接着在宿主机内执行gdb程序调试vmlinux(参数根据绝对路径或相对路径设置):
1 | arm-linux-gdb vmlinux |
也可以在启动gdb的时候不传入vmlinux,而在启动gdb后通过下面的命令指定调试文件:
1 | file ./vmlinux |
成功进入gdb后输出内容如下:
1 | GNU gdb (GDB) 8.0.1 |
在gdb终端输入以下命令连接gdb服务器:
1 | target remote::1234 |
连接到gdb服务器后输出内容如下:(本地调试可以默认不填ip,即remote::1234,或者填写ip,即remote localhost:1234)
1 | (gdb) target remote localhost:1234 |
此时即可使用gdb命令调试内核。下例在start_kernel处打断点,查看代码,直接执行代码到断点,然后单步执行,然后直接运行代码到系统启动:(在gdb调试终端进行单步执行时,在qemu启动内核终端可看到同步执行信息)
1 | (gdb) b start_kernel //在start_kernel函数开始地址加入断点 |
开发板调试(通过串口调试)
正常启动目标机系统后通过以下命令进入kdb终端(假设串口是ttyAMA0):
1 | echo ttyAMA0 > /sys/module/kgdboc/parameters/kgdboc |
如果需要开机即进入kgdb状态,则在目标板的启动参数中添加如下参数即可:
1 | kgdboc=ttyAMA0,115200 kgdbwait |
在本实验中,要开机进入kgdb的启动参数部分为:
1 | "root=/dev/mmcblk0 rw console=ttyAMA0 kgdboc=ttyAMA0,115200 kgdbwait" |
进入kdb终端后输入kgdb命令按回车进入kgdb模式,如果内核未开启gdb,则不需要kgdb命令。
(如果目标机只有一个输出串口,则在调试机操作目标机和运行gdb使用同一个串口,在目标机上完成上述操作后该串口将被gdb用来连接目标机的gdb服务)
在调试机中首先将串口波特率设置和目标板一致:
1 | stty ispeed 115200 ospeed 115200 -F /dev/ttyUSB0 |
接着运行gdb调试vmlinux:
1 | arm-linux-gdb vmlinux |
进入gdb终端后输入以下指令连接到目标及kgdb:
1 | target remote /dev/ttyUSB0 |
成功连接到目标板后输出和Qemu ARM虚拟机调试中一致。
KDB调试
先决条件
和kgdb不同,kdb不是源码级别的调试工具,Kdb的主要目的是做一些分析,以帮助开发或诊断内核问题。但是kdb仍然可以使用简单的断点。
kdb可以通过名字访问内核中的一些内核中内建的或模块中的符号,但需要内核开启CONFIG_KALLSYMS设置。
kdb内核配置:
主功能配置:CONFIG_KGDB_KDB
串口配置:CONFIG_KGDB_SERIAL_CONSOLE
键盘配置:CONFIG_KDB_KEYBOARD
SYSRQ配置:CONFIG_MAGIC_SYSRQ
进入和退出kdb
1.通过kgdboc配置串口
1 | echo ttyAMA0 > /sys/module/kgdboc/parameters/kgdboc |
2.配置进入debug模式
1 | echo g > /proc/sysrq-trigger |
3.退出kdb命令行
1 | kdb> g |
kdb获取help信息
输入help命令,得到kdb命令列表说明:(kdb>是命令提示符)
1 | help |
kdb命令说明
命令 | 参数 | 说明 |
---|---|---|
md | <vaddr> | 显示指定内存地址的内容 |
mdr | <vaddr> <bytes> | 显示原始内存 |
mdp | <paddr> <bytes> | 显示物理内存 |
mds | <vaddr> | 显示内存符号 |
mm | <vaddr> <contents> | 修改指定地址的内存内同 |
go | [<vaddr>] | 继续执行,或跳到指定地址执行 |
rd | 显示寄存器的值 | |
rm | <reg> <contents> | 修改寄存器的值 |
ef | <vaddr> | 显示异常帧 |
bt | [<vaddr>] | 栈回溯 |
btp | <pid> | 指定地址上的栈显示 |
bta | [D/R/S/T/C/Z/E/U/I/M/A] | 栈回溯所有匹配到状态标记的进程 |
btc | 当前进程在每个cpu上的栈回溯 | |
btt | <vaddr> | 栈回溯指定struct task地址的进程 |
env | 显示环境变量 | |
set | 设置环境变量 | |
cpu | <cpunum> | 切换到指定cpu上 |
kgdb | 进入kgdb模式 | |
ps | [<flags> / A] | 显示活动进程列表 |
pid | <pidnum> | 切换到另一个进程 |
reboot | 立即重启 | |
lsmod | 查看加载的内核模块 | |
sr | <key> | Magic SysRq key |
dmesg | [lines] | 显示syslog缓冲区 |
defcmd | name “usage” “help” | 定义命令合集, |
kill | <-signal> <pid>-signal> | 向进程发送信号 |
summary | 系统总计和概况 | |
per_cpu | <sym> [<bytes>] [<cpu>] | 显示每一个cpu上的变量 |
grephelp | 通过管道和grep显示help信息 | |
bp | [<vaddr>] | 设置/显示断点 |
bl | [<vaddr>] | 显示断点 |
bc | <bpnum> | 清除断点 |
be | 使能断点 | |
bd | 禁止断点 | |
ss | 单步执行 | |
dumpcommon | Common kdb debugging | |
dumpall | First line debugging | |
dumpcpu | same as dumpall but only task on cpus | |
ftdump | [skip_#entries] [cpu] | Dump ftrace log; -skip dumps last #entries |