type
Post
status
Published
slug
2023/12/29/Driver debugging using gdb + qemu
summary
tags
Linux
驱动
category
Linux
icon
password
new update day
Property
Dec 29, 2023 03:20 AM
created days
Last edited time
Dec 29, 2023 03:20 AM
简单聊聊
因为最近在学习驱动程序的开发,一开始因为写的驱动程序比较简单,所以都是直接使用 printk 大法配合一些宏,在函数关键部位进行输出,掌握驱动程序运行的大致状态,比如下面的这种代码。
printk("%s %s line %d, can not alloc mem\n", __FILE__, __FUNCTION__, __LINE__);
但是随着驱动程序涉及到的东西的增加,逻辑的逐渐复杂化,原本的 printk 大法,慢慢就变得有一些力不从心了。突然想到了以前使用 gdb 调试操作系统的做法,所以准备尝试一下使用 gdb + qemu 的方法进行内核驱动模块的调试。
但是与原来调试操作系统不一样的是,因为Linux驱动程序是一种模块化的 .ko 文件,不是直接写入到操作系统内核代码中的程序,而是在需要的时候手动进行加载并且与设备树相互配合,所以没有办法直接进行调试。
于是就查询了相关资料,发现在执行
insmod
指令进行驱动程序加载的时候,统一入口就是 do_init_module
函数,通过在 do_init_module
函数处打断点,就可以得到内核驱动模块加载后在内核中的对应地址,然后通过 gdb 的 add-symbol-file
指令,将对应的驱动模块加载至 gdb 中,补全相关的符号表,即可进行驱动程序模块的调试。查询内核模块加载到的地址
编译开启调试信息的内核文件
确保在 Linux 内核源码路径中的 .config 文件中,开启了内核调试的相关配置。
make zImage -jx
最终在内核源码路径中可以看到
vmlinux
文件
并且通过 file 命令查看,能够看到其包含调试信息。
file vmlinux vmlinux: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, BuildID[sha1]=9a8d545bc62f684b6c685b17302696f17281feff, with debug_info, not stripped
为 qemu 添加启动标志
在启动QEMU时加上“-s -S”参数可以进入等待调试状态,然后可以用gdb进行调试。默认调试监听端口为
1234
.开启 gdb 连接 qemu
target remote :1234 b do_init_module c
加载待调试内核模块
执行完下面这条命令之后就可以看到 gdb 命中了我们设置的
do_init_module
函数断点。insmod xxx.ko

获取内核模块地址信息
使用
print mod->sect_attrs->attrs[0]
命令查找 .text .rodata .data .bss .symtab
这几个节所在的下标号,在我这边编译出来的内核模块这几个节所在的标号分别是 0,4,8,10,11
。记录下来这几个值进行备用。注意:不同的编译参数会导致不同的标号分布,需要自己手动确认一下。

根据上面所得到的标号,依次执行下面的命令,获取对应接的加载地址。
print /x mod->sect_attrs->attrs[1]->address print /x mod->sect_attrs->attrs[4]->address print /x mod->sect_attrs->attrs[8]->address print /x mod->sect_attrs->attrs[10]->address print /x mod->sect_attrs->attrs[11]->address
根据得到的地址,依次修改下面的命令对应部位的地址:
add-symbol-file /home/tcy/100ask/src/06_gpio_irq/01_simple/gpio_key_drv.ko 0x7f000000 -s .rodata 0x7f000c40 -s .data 0x7f001058 -s .bss 0x7f0012c0 -s .symtab 0x7f003050

对驱动程序模块内部函数进行断点
现在你就可以对驱动程序内部的函数进行断点设置并调试了。
b gpio_key_probe c

其他问题
value has been optimized out
开启调试信息并关闭编译优化
经过一些搜索的尝试与努力,终于可以开心地进行驱动程序模块的调试了,但是在调试的过程中遇到了
value has been optimized out
的问题,导致一些变量在编译的时候被优化掉,无法查看对应的变量值,所以需要打开内核模块的调试信息并且关闭编译优化。为了达到上面的目的,需要我们在 Makefile 文件中添加
EXTRA_CFLAGS = -g
与 ccflags-y := -O0
这两行参数。KERN_DIR = /home/xxx/100ask/100ask_imx6ull-qemu/linux-4.9.88 # 板子所用内核源码的目录 EXTRA_CFLAGS = -g ccflags-y := -O0 all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m += gpio_key_drv.o
欢迎加入“喵星计算机技术研究院”,原创技术文章第一时间推送。

- 作者:tangcuyu
- 链接:https://expoli.tech/articles/2023/12/29/Driver%20debugging%20using%20gdb%20%2B%20qemu
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章