使用kgdb调试内核驱动

1. 目的:

在ubuntu真实环境,使用gdb对一个PCIe设备的内核驱动代码(后面用X表示该内核驱动和设备)进行单步调试。

User Mode程序可直接在单机环境下调试。

2. 调试环境:

要调试真实环境中X内核驱动(不是仿真环境,不是虚拟环境),目标机必须是带X设备的物理机。目标机通过串口与调试机(或者称为开发机)相连,即通过kgdboc(kgdb on console)方式调试。另外还有kgdboe(kgdb on enthernet)方式,简单尝试过,会导致目标机hang死。

我用到的硬件环境:

1, 目标机为x86_64台式机,有X设备,主板上有COM口(见下图“目标机侧COM口和连线图”,非标准pin脚排布,且pin未引出)。

2, 调试机为另一台x86_64的物理机。

3, 使用USB转RS232串口线(链接如下)连接目标机和调试机。串口线COM口与目标机主板上COM口相连(目标机上设备为/dev/ttyS0),另一端与调试机的USB口相连(调试机上设备为/dev/ttyUSB0)。

绿联(UGREEN)USB转RS232串口线 USB转DB9针公头转接线 支持考勤机收银机标签打印机com口调试线

目标机侧COM口和连线图

问:如果目标机和调试机都用USB转串口(USB转TTL)的方式连接呢,能支持kgdb调试吗?

答:不行,缺省的USB转串口驱动不支持。要支持kgdboc,底层串口驱动需要支持“polling hooks”,即支持.poll_get_char和.poll_put_char接口;通常,驱动中用#ifdef CONFIG_CONSOLE_POLL包含这部分代码。添加上述功能后,可以支持。

3. 环境约定

为了减少拷贝,随时修改驱动,进行持续调试,我进行了如下约定(部分非必须):

1, 目标机和调试机运行的内核来自同一份内核源码,且保证内核版本信息一致,避免调试机编译的X驱动无法被目标机加载。

2, 目标机上运行X内核驱动。

3, 调试机上运行gdb和编译X驱动,调试机上有X驱动源码、驱动编译生成的ko文件、目标机内核文件(还有内核源码),gdb会用到上述文件。每次编译后,将X.ko拷贝到目标机上运行。

4. 具体执行步骤:

4.1. 目标机上内核编译和安装

假设已经有一份内核的源代码包,编译的内核需要支持kgdb调试。

依次执行如下:

执行cd /usr/src/linux-source-5.4.0/到源代码目录。

执行make clean; make mrproper净化源代码。

执行cp -v /boot/config-5.4.0-42-generic .config基于当前内核的配置(此步可省略)。

执行make menuconfig

选中kernel hacking->Compile-time checks * ->compile kernel with debug info(默认已选);

修改kernel hacking->Compile-time checks * ->Warn for stack frames larger than *,从1024改为2048(Optional);

选中kernel hacking->Compile-time checks * ->enable full section mismatch analysis(可选项,防止内联);

选中kernel hacking->compile the kernel with frame pointers(可能没有这一项);

选中kernel hacking->Magic Sysrq key;

选中kernel hacking->Kernel debugging;

选中KGDB: kernel debugging with remote gdb –>kgdb:use kgdb over the serial console;

执行nproc查看cpu核数。

执行make –j 16进行编译(16为cpu核数),或者执行make bzImage进行编译。

编译生成的vmlinux是未压缩的内核文件,gdb加载这个文件并读取symbol符号信息。.arch/x86_64/boot/bzImage(.arch/x86/boot/bzImage)是压缩后的镜像文件。

将编译生成的vmlinux拷贝到调试机上。

编译成功后,执行make INSTALL_MOD_STRIP=1 modules_install && make install分别安装内核模块和内核。

修改/etc/default/grub使启动内核选项都显示出来。

修改目标机的内核启动参数添加如下信息:rw console=ttyS0,115200 kgdbwait kgdboc=ttyS0,115200 nokaslr (带有kgdbwait,目标机在启动时就会等待调试机的gdb连接,由于我们可以等待目标机启动后再手动加载内核驱动,故去掉kgdbwait)。

4.2. 调试机上内核编译和安装

调试机的新内核,不需要支持kgdb。

我使用与目标机相同内核和版本信息,是为了确保调试机编译的X驱动能被目标机加载。

详细步骤略。

4.3. 调试机上内核驱动编译

调试机上,基于新安装的内核编译X内核驱动。

把新编译生成的X.ko拷贝到目标机/usr/lib/modules/5.4.210/extra/目录。

把编译生成的X驱动拷贝到本地目录:/usr/src/kgdb_dir/(此目录用于存放调试用到的文件)。

4.4. 调试机上串口虚拟化(optional)

获取agent-proxy: kernel/kgdb/agent-proxy.git - agent-proxy for kgdb

执行cd /home/xxx/agent-proxy/

执行./agent-proxy  5550^5551 0 /dev/ttyUSB0,115200

通过串口虚拟化把目标机的控制台重定向到HOST:5550;把目标机到HOST的kgdb重定向到HOST:5551。

调试机执行telnet localhost 5550监听目标机控制台输出。

4.5. 调试机其他配置:

调试机上编写脚本~/.gdbinit,内容如下:

add-auto-load-safe-path /usr/src/linux-source-5.4.0(调试机上的目标机内核源码路径)

source ./vmlinux-gdb.py

4.6. 调试内核驱动X

目标机启动,如果带kgdbwait启动参数,目标机会进入等待连接状态;如果不带kgdbwait启动参数,正常进入系统,需要在目标机上手动执行echo g > /proc/sysrq-trigger使目标机处于等待gdb连接的状态。此时,在调试机console 5550的监听窗口,会停在kdb>提示符,输入kgdb后,目标机等待gdb连接。

调试机执行cd /usr/src/kgdb_dir/(此目录下有X驱动文件)。

调试机执行gdb ../linux-source-5.4.0/vmlinux(调试机上的目标机内核文件)。

调试机执行(gdb)target remote localhost:5551进行gdb连接。

此时目标机上X驱动未加载,设置断点(gdb)b do_init_module后让目标机正常执行。

目标机执行modprobe X加载X驱动模块。此时gdb会中断在do_init_module,运行(gdb)lx-symbols在gdb中加载X驱动模块对应的符号,加载后,就可以设置X驱动模块中的断点。

注:这里使用lx-symbols脚本自动加载对应的符号,与前面的拷贝X驱动模块到当前目录对应。手动加载方法是:目标机上执行cat /sys/module/X/sections/.text获取地址值,再调试机执行gdb>add-symbol-file X.ko 0xaddr。

注意:上图中函数的局部变量可能被优化掉,无法在gdb中查看。

针对整个内核,不能取消优化,不能完全关掉-O2,用-O0是无法完全编译通过的。

实测在源代码文件中添加以下方式是有效的。

#pragma GCC push_options

#pragma GCC optimize("O0")

<source code>

#pragma GCC pop_options

估计下述方法也应该有效:

Void __attribute__((optimize(“O0”))) foo(char data)

{}

<end>

  • 26
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值