type
status
slug
summary
tags
category
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 大法,慢慢就变得有一些力不从心了。突然想到了以前使用 gdb 调试操作系统的做法,所以准备尝试一下使用 gdb + qemu 的方法进行内核驱动模块的调试。
但是与原来调试操作系统不一样的是,因为Linux驱动程序是一种模块化的 .ko 文件,不是直接写入到操作系统内核代码中的程序,而是在需要的时候手动进行加载并且与设备树相互配合,所以没有办法直接进行调试。
于是就查询了相关资料,发现在执行
insmod
指令进行驱动程序加载的时候,统一入口就是 do_init_module
函数,通过在 do_init_module
函数处打断点,就可以得到内核驱动模块加载后在内核中的对应地址,然后通过 gdb 的 add-symbol-file
指令,将对应的驱动模块加载至 gdb 中,补全相关的符号表,即可进行驱动程序模块的调试。查询内核模块加载到的地址
编译开启调试信息的内核文件
确保在 Linux 内核源码路径中的 .config 文件中,开启了内核调试的相关配置。
最终在内核源码路径中可以看到
vmlinux
文件并且通过 file 命令查看,能够看到其包含调试信息。
为 qemu 添加启动标志
在启动QEMU时加上“-s -S”参数可以进入等待调试状态,然后可以用gdb进行调试。默认调试监听端口为
1234
.开启 gdb 连接 qemu
加载待调试内核模块
执行完下面这条命令之后就可以看到 gdb 命中了我们设置的
do_init_module
函数断点。获取内核模块地址信息
使用
print mod->sect_attrs->attrs[0]
命令查找 .text .rodata .data .bss .symtab
这几个节所在的下标号,在我这边编译出来的内核模块这几个节所在的标号分别是 0,4,8,10,11
。记录下来这几个值进行备用。注意:不同的编译参数会导致不同的标号分布,需要自己手动确认一下。
根据上面所得到的标号,依次执行下面的命令,获取对应接的加载地址。
根据得到的地址,依次修改下面的命令对应部位的地址:
对驱动程序模块内部函数进行断点
现在你就可以对驱动程序内部的函数进行断点设置并调试了。
其他问题
value has been optimized out
开启调试信息并关闭编译优化
经过一些搜索的尝试与努力,终于可以开心地进行驱动程序模块的调试了,但是在调试的过程中遇到了
value has been optimized out
的问题,导致一些变量在编译的时候被优化掉,无法查看对应的变量值,所以需要打开内核模块的调试信息并且关闭编译优化。为了达到上面的目的,需要我们在 Makefile 文件中添加
EXTRA_CFLAGS = -g
与 ccflags-y := -O0
这两行参数。- 作者:tangcuyu
- 链接:https://expoli.tech/articles/2023/12/29/Driver%20debugging%20using%20gdb%20%2B%20qemu
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章