步骤一
在运行驱动的目标机 ubuntu 系统中,新建目录用于下载内核代码 使用命令
apt source linux-image-unsigned-$(uname -r)
下载内核 进入下载好的内核目录,使用
make menuconfig
编译配置文件 .config 确保
- CONFIG_KGDB=y:支持内核调试。
- CONFIG_KGDB_SERIAL_CONSOLE=y:支持串口调试。
- CONFIG_DEBUG_INFO=y:生成调试符号。
- CONFIG_MODULE_DEBUG=y:支持模块调试。
- CONFIG_MAGIC_SYSRQ=y:支持 SysRq。
编译内核
make -j 32
make INSTALL_MOD_STRIP=1modules_install
make install
然后修改grub,是能kgdb串口链接
编辑 `/etc/default/grub
,在 GRUB_CMDLINE_LINUX_DEFAULT 中添加以下参数:
console=ttyS0,115200,kgdboc=ttyS0,115200 kgdbwait
使用update-grub 跟新grub,然后重启系统,关于ttyS0 可以在命令行通过查看。
dmesg | grep tty
步骤二
使用串口将目标机与进行调试的调试机链接,在调试机器中,使用
screen /dev/ttyUSB0 115200
链接目标机器,出现kdb>,说明串口链接成功,输入
go
使得目标机退出kgdbwait,正常启动。
启动后,在 kernel/kgdb/agent-proxy.git - agent-proxy for kgdb
下载 agent-proxy
工具,下载好后通过 make 编译,编译完成后拷贝agent-proxy
到/user/local/bin
下一份,使其加入到环境变量。
然后新开终端,输入
agent-proxy 5550^5551 0 /dev/ttyUSB0,115200
这里使用刚刚安装的软件将ttyUSB0这个端口复用为两个 telnet端口,这个窗口不能关闭。 再新开一个终端,输入
telnet localhost 5550
用来链接目标机,出现 Escape caharactor is '^]'
. 说明链接成功,回车会进入目标机的 shell 界面,然后将之前编译出来的vmlinux,以及要调试的驱动 test.ko 拷贝到调试机器的同一目录,通过
cat /sys/module/module_name/sections/.text
查看驱动的链接位置,然后在链接目标机的串口窗口输入
echo g > /proc/sysrq-trigger
使其进入kdb界面,在kdb界面输入
kgdb
使其进去kgdb的远程链接 然后在调试机的新窗口使用gdb 调试拷贝来的vmlinux
gdb vmlinux
进入gdb界面后,使用
target remote localhost:5551
链接调试机器,然后通过
add-symbol-file test.ko 0xffffffffa022a000
添加驱动的调试信息,然后创建驱动的断点,比如open函数,断点完成后输入c ,使得目标机器正常运行,然后再链接目标机的窗口运行用户端程序,使其触发到断点的地方,就可以正常调试了。 vmlinux合驱动的目录在调试机和目标机的位置可以任意放置,但是驱动源码的位置两边的目录结构必须一致。
动态驱动的部分调试
ko 添加gdb 的symbol信
make clean; make COPTS=-g CFLAGS_MODULE=-Og
添加symbol信息 add-symbol-file /xx/xx/xx.lib
(info sharelib 下的首列地址)
查看加载后地址
cat /sys/module/module_name/sections/.text
0xffffffffa022a000
cat /sys/module/module_name/sections/.data
0xffffffffa026e000
cat /sys/module/module_name/sections/.bss
0xffffffffa0273d40
启动调试
(gdb) add-symbol-file test.ko 0xffffffffa022a000 -s .data 0xffffffffa026e000 -s .bss 0xffffffffa0273d40
add symbol table from file "test.ko" at
.text_addr = 0xffffffffa001b040
.data_addr = 0xffffffffa026e000
.bss_addr = 0xffffffffa0273d40
(y or n) y
Reading symbols from test.ko...
(gdb)
动态库有分离dbg symbol信息的添加symbol信息。
info share xxx.so
add-symbol-file path(dbg symbol文件路径) 0x0xx(info首列地址)
bt
gdb 查看所有线程的所有线程中打印堆栈追踪信息 thread apply all bt
gdb 常用方法
命令 | 含义 |
---|---|
dri path | gdb 添加源码方法,如 dir /home/mas/mpv |
info sharedlib | 查看调用的动态库 |
layout | 用于分割窗口,可以一边查看代码,一边测试 |
layout src | 显示源代码窗口 |
layout asm | 显示反汇编窗口 |
layout regs | 显示源代码/反汇编和CPU寄存器窗口 |
layout split | 显示源代码和反汇编窗口 |
Ctrl + L | 刷新窗口 |
Ctrl + x,再按1 | 单窗口模式,显示一个窗口 |
Ctrl + x,再按2 | 双窗口模式,显示两个窗口 |
Ctrl + x,再按a | 回到传统模式,即退出layout,回到执行layout之前的调试窗口 |
reverse-step | 反向运行程序到上一次被执行的源代码行 |
return nn | 退出函数(返回值) |
jump +n(行号) | 跳过函数,跳转到n 行执行,不会停止,需要打断点停止 |
until X | 执行代码并在第 X 行停止 |
set var | 变量=值 设置变量 |
run(简写r) | 运行程序,当遇到断点后,程序会在断点处停止运行,等待用户输入下一步的命令 |
continue(简写c) | 继续执行,到下一个断点处(或运行结束) |
next(简写n) | 单步跟踪程序,当遇到函数调用时,直接调用,不进入此函数体 |
step(简写s) | 单步调试如果有函数调用,则进入函数;与命令n不同,n是不进入调用的函数的 |
until | 运行程序直到退出循环体 |
until+行号 | 运行至某行 |
finish | 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息 |
call 函数(参数) | 调用“函数”,并传递“参数”,如:call gdb_test(55) |
quit 简记为 q | 退出gdb |
break n(简写b n) | 在第n行处设置断点 ;可以带上代码路径和代码名称: b OAGUPDATE.cpp:578) |
break func | 在函数func()的入口处设置断点,如:break cb_button |
delete 断点号n | 删除第n个断点 |
disable 断点号n | 暂停第n个断点 |
enable 断点号n | 开启第n个断点 |
clear 行号n | 清除第n行的断点 |
info breakpoints(简写info b) | 显示当前程序的断点设置情况 |
refresh | 刷新界面 |