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 文件
notion image
并且通过 file 命令查看,能够看到其包含调试信息。

为 qemu 添加启动标志

在启动QEMU时加上“-s -S”参数可以进入等待调试状态,然后可以用gdb进行调试。默认调试监听端口为 1234.

开启 gdb 连接 qemu

加载待调试内核模块

执行完下面这条命令之后就可以看到 gdb 命中了我们设置的 do_init_module 函数断点。
notion image

获取内核模块地址信息

使用 print mod->sect_attrs->attrs[0] 命令查找 .text .rodata .data .bss .symtab 这几个节所在的下标号,在我这边编译出来的内核模块这几个节所在的标号分别是 0,4,8,10,11。记录下来这几个值进行备用。
💡
注意:不同的编译参数会导致不同的标号分布,需要自己手动确认一下。
notion image
根据上面所得到的标号,依次执行下面的命令,获取对应接的加载地址。
根据得到的地址,依次修改下面的命令对应部位的地址:
notion image

对驱动程序模块内部函数进行断点

现在你就可以对驱动程序内部的函数进行断点设置并调试了。
notion image

其他问题

value has been optimized out

开启调试信息并关闭编译优化

经过一些搜索的尝试与努力,终于可以开心地进行驱动程序模块的调试了,但是在调试的过程中遇到了 value has been optimized out 的问题,导致一些变量在编译的时候被优化掉,无法查看对应的变量值,所以需要打开内核模块的调试信息并且关闭编译优化。
为了达到上面的目的,需要我们在 Makefile 文件中添加 EXTRA_CFLAGS = -gccflags-y := -O0 这两行参数。
 
在 Arch Linux 上通过 OneDrive 进行 rime 的用户词库同步使用 OpenWrt SDK 手动编译 alist 并安装