qemu 单步调试linux driver

一、背景

qemu单步调试arm64 linux kernel-CSDN博客介绍了如何单步调试kernel, 但是我们经常写一些测试driver, driver的部分如何调试?

二、环境准备

调试driver 就需要准备一个简单的driver, 这里用最简单的hello world来演示如何调试,程序非常简单,生成一个字符设备,并且在cat的时候打印变量,还加了一个全局变量,用来gdb调试查看变量使用

#include <linux/module.h>     /* Needed by all modules */
#include <linux/kernel.h>     /* Needed for KERN_INFO */
#include <linux/init.h>       /* Needed for the macros */
#include <linux/miscdevice.h> /* Needed for misc device */int global_hello_value = 996;#define    DEVICE_NAME     "Helloworld"void test_for_debug(void)
{   printk(KERN_INFO "just for debug the text section %d", global_hello_value);
}static int hello_open(struct inode *inode, struct file *file){test_for_debug();return 0;
}static struct file_operations hello_fops = { .owner  =   THIS_MODULE,.open   =   hello_open,     
};static struct miscdevice hello_misc = { .minor  = MISC_DYNAMIC_MINOR,.name   = "Helloworld",.fops   = &hello_fops,
};static int __init hello_start(void) 
{int ret;ret = misc_register(&hello_misc);if (ret < 0) {printk(KERN_EMERG " %s register failed %d.\n", DEVICE_NAME, ret);return ret;}   printk(KERN_INFO "Hello world\n"); return 0;  
}static void __exit hello_end(void) 
{ test_for_debug();misc_deregister(&hello_misc);printk(KERN_INFO "hello_end Goodbye\n"); 
} MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("geek");
MODULE_DESCRIPTION("A simple Hello world misc driver!");
MODULE_VERSION("0.1");module_init(hello_start);
module_exit(hello_end);

编译ko需要使用 make modules指令

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- modules -j8

编译完成后,会在hello driver的源码目录生成对应的ko文件;

将ko 放在我们的rootfs 中,参考前一篇 无人知晓:qemu搭建arm64 linux kernel调试环境 rootfs image制作部分,挂载rootfs.img, 拷贝ko, 然后umount img

sudo mount rootfs.img rootfs
sudo mkdir rootfs/driver
sudo cp ../hello_driver.ko rootfs/driver/
sudo umount rootfs

三、加载ko

insmod 加载打印hello world, 同时生成/dev/Helloworld节点

~ # insmod driver/hello_driver.ko 
[   11.203785] Hello world
~ # cd /dev/
/dev # ls
Helloworld       ptypc            tty36            ttyp0

gdb 连接后,尝试看下之前ko的global_hello_value变量,无法显示,test_for_debug函数也是无法找到的

(gdb) p global_hello_value
No symbol "global_hello_value" in current context.
(gdb) b test_for_debug
Function "test_for_debug" not defined.
Make breakpoint pending on future shared library load? (y or [n]) n

四、调试ko

调试ko的步骤也是分三步:先加载ko, 确认ko加载的地址;然后加载ko symbols,加载地址需要和实际加载一致 ; 最后一步就是加断点,然后启动调试即可

方法1: insmod ko之后根据节点/sys/module/XXXX/sections/ 确认ko 加载的地址

/driver # cat /sys/module/hello_driver/sections/.text
0xffff80007a860000

加载ko symbols

gdb 中执行
(gdb) add-symbol-file drivers/my_driver/hello_driver.ko -s .text 0xffff80007a860000
add symbol table from file "drivers/my_driver/hello_driver.ko" at.text_addr = 0xffff80007a860000
(y or n) y
Reading symbols from drivers/my_driver/hello_driver.ko...

然后再设置断点

(gdb) b test_for_debug
Breakpoint 1 at 0x4: test_for_debug. (3 locations)
(gdb) c
Continuing.

continue 后 qemu中cat节点即可触发中断

/dev # cat Helloworld

gdb窗口

(gdb) bt
#0  0xffff80007a860004 in test_for_debug () at drivers/my_driver/hello_driver.c:12
#1  hello_open (inode=0xffff000002f11610, file=0xffff00000381bb00) at drivers/my_driver/hello_driver.c:16
#2  0xffff8000808623d8 in misc_open (inode=0xffff000002f11610, file=0xffff00000381bb00) at drivers/char/misc.c:165
#3  0xffff8000802c8250 in chrdev_open (inode=0xffff000002f11610, filp=0xffff00000381bb00) at fs/char_dev.c:414
#4  0xffff8000802bcfcc in do_dentry_open (f=0xffff00000381bb00, inode=0xffff000002f11610, open=0xffff8000802c8194 <chrdev_open>) at fs/open.c:929
#5  0xffff8000802beda0 in vfs_open (path=0x2 <hello_end+2>, file=0xffff000002f11610) at fs/open.c:1063
#6  0xffff8000802d6350 in do_open (op=<optimized out>, file=<optimized out>, nd=<optimized out>) at fs/namei.c:3640
#7  path_openat (nd=0xffff800082adbc40, op=0x4 <hello_end+4>, flags=2055602176) at fs/namei.c:3797
#8  0xffff8000802d706c in do_filp_open (dfd=-100, pathname=0xffff000002e40000, op=0xffff800082adbd74) at fs/namei.c:3824
#9  0xffff8000802bf048 in do_sys_openat2 (dfd=-100, filename=0x646c <error: Cannot access memory at address 0x646c>, how=0x3 <hello_end+3>) at fs/open.c:1422
#10 0xffff8000802bf388 in do_sys_open (mode=<optimized out>, flags=<optimized out>, filename=<optimized out>, dfd=<optimized out>) at fs/open.c:1437
#11 __do_sys_openat (mode=<optimized out>, flags=<optimized out>, filename=<optimized out>, dfd=<optimized out>) at fs/open.c:1453
#12 __se_sys_openat (mode=<optimized out>, flags=<optimized out>, filename=<optimized out>, dfd=<optimized out>) at fs/open.c:1448
#13 __arm64_sys_openat (regs=0xffff80007a860000 <hello_open>) at fs/open.c:1448
#14 0xffff800080027738 in __invoke_syscall (syscall_fn=<optimized out>, regs=<optimized out>) at arch/arm64/kernel/syscall.c:37
#15 invoke_syscall (regs=0xffff800082adbeb0, scno=58833664, sc_nr=2055602176, syscall_table=0x2 <hello_end+2>) at arch/arm64/kernel/syscall.c:51
#16 0xffff800080027840 in el0_svc_common (regs=0xffff800082adbeb0, scno=1, syscall_table=0x2 <hello_end+2>, sc_nr=<optimized out>) at arch/arm64/kernel/syscall.c:136
#17 0xffff8000800278fc in do_el0_svc (regs=0xffff000002f11610) at arch/arm64/kernel/syscall.c:155
#18 0xffff800081016224 in el0_svc (regs=0xffff800082adbeb0) at arch/arm64/kernel/entry-common.c:678
#19 0xffff800081016688 in el0t_64_sync_handler (regs=0xffff80007a860000 <hello_open>) at arch/arm64/kernel/entry-common.c:696
#20 0xffff800080011d4c in el0t_64_sync () at arch/arm64/kernel/entry.S:59

但是还有一个问题,查看global_hello_value怎么失败了?

(gdb) p global_hello_value
Cannot access memory at address 0x288

这是因为,前面我加载symbols时没有指定.data段,只指定了.text段

退出gdb,重新修改ko 加载symbols 指令为(.text, .data 等段信息均在/sys/module/hello_driver/sections/ 下)

(gdb) add-symbol-file drivers/my_driver/hello_driver.ko -s .text 0xffff80007a860000 -s .data 0xffff80007a862000
add symbol table from file "drivers/my_driver/hello_driver.ko" at.text_addr = 0xffff80007a860000.data_addr = 0xffff80007a862000
(y or n) y
Reading symbols from drivers/my_driver/hello_driver.ko...
(gdb) p global_hello_value
$1 = 996

方法2:增加断点,在load_module中停住,这样可以debug 初始化的部分,比如module_init中的函数(如果我们驱动在这里有bug,根本没机会生成/sys/module下的driver 段信息)

ko加载的可以参考这篇 linux ko模块动态加载源码分析

核心就是在do_init_module 设置断点,然后从结构体struct module 提取module加载信息

注意:module_init函数在 .init.text 段,add-symbol-file 需要加入这个 .init.text ,才能在module init设置断点

(gdb)b do_init_module
设置好断点后继续,然后加载ko,触发断点后,将struct module的段信息及地址信息用gdb 显示出来
(gdb) n
2523		freeinit->init_text = mod->mem[MOD_INIT_TEXT].base;
(gdb) p *mod->sect_attrs->attrs@20

在hello_start 设置断点

(gdb) add-symbol-file drivers/my_driver/hello_driver.ko -s .text 18446603338276798464 -s .init.text 18446603338276823040 -s .data 18446603338276806656
add symbol table from file "drivers/my_driver/hello_driver.ko" at.text_addr = 0xffff80007a860000.init.text_addr = 0xffff80007a866000.data_addr = 0xffff80007a862000
(y or n) y
Reading symbols from drivers/my_driver/hello_driver.ko...(gdb) b hello_start
Breakpoint 2 at 0xffff80007a866000: file drivers/my_driver/hello_driver.c, line 34.Thread 2 hit Breakpoint 2, hello_start () at drivers/my_driver/hello_driver.c:34
warning: Source file is more recent than executable.
34	    ret = misc_register(&hello_misc);
(gdb) bt
#0  hello_start () at drivers/my_driver/hello_driver.c:34
#1  0xffff800080014dbc in do_one_initcall (fn=0xffff80007a866000 <hello_start>) at init/main.c:1232
#2  0xffff800080120d20 in do_init_module (mod=0xffff80007a862180) at kernel/module/main.c:2530
#3  0xffff800080122dfc in load_module (info=0xffff800082af3ac8, uargs=0xffff0000035e9d80 "\004", flags=0) at kernel/module/main.c:2981
#4  0xffff800080123020 in __do_sys_init_module (umod=0x23756d60, len=38752, uargs=0x5bdf21 "") at kernel/module/main.c:3058
#5  0xffff800080123140 in __se_sys_init_module (uargs=<optimized out>, len=<optimized out>, umod=<optimized out>) at kernel/module/main.c:3038
#6  __arm64_sys_init_module (regs=0x0 <hello_end>) at kernel/module/main.c:3038
#7  0xffff800080027738 in __invoke_syscall (syscall_fn=<optimized out>, regs=<optimized out>) at arch/arm64/kernel/syscall.c:37
#8  invoke_syscall (regs=0xffff800082af3eb0, scno=56532352, sc_nr=0, syscall_table=0x0 <hello_end>) at arch/arm64/kernel/syscall.c:51
#9  0xffff800080027840 in el0_svc_common (regs=0xffff800082af3eb0, scno=-48, syscall_table=0x0 <hello_end>, sc_nr=<optimized out>) at arch/arm64/kernel/syscall.c:136
#10 0xffff8000800278fc in do_el0_svc (regs=0x0 <hello_end>) at arch/arm64/kernel/syscall.c:155
#11 0xffff800081016224 in el0_svc (regs=0xffff800082af3eb0) at arch/arm64/kernel/entry-common.c:678
#12 0xffff800081016688 in el0t_64_sync_handler (regs=0x0 <hello_end>) at arch/arm64/kernel/entry-common.c:696
#13 0xffff800080011d4c in el0t_64_sync () at arch/arm64/kernel/entry.S:595
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

方法3:利用vmlinux-gdb.py脚本中的lx-symbols辅助命令

利用gdb脚本加载, 也是三步,先构建gdb script环境; 去掉gdb 脚本执行限制 ; 调用lx-symbols获取ko加载地址,当让script中除了获取lx-symbols外还有很多其他脚本辅助我们调试;

参考:https://www.codenong.com/cs105354913/

构建脚本环境

 make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- scripts_gdb

根目录生成vmlinux-gdb.py表示成功

去掉gdb脚本加载限制,在~/.config/gdb/gdbinit 中添加set auto-load safe-path / 环境变量;

此时在linux source加载vmlinux也会提示这个限制错误及解决方法

添加环境变量后重新启动gdb,出现了加载vmlinux-gdb.py脚本的错误

上面的报错原因是因为编译kernel版本中打开了config CONFIG_DEBUG_INFO_REDUCED, 这个会影响gdb script的完整功能,在arch/arm64/ configs/defconfig中去掉这个config,重新编辑后加载vmlinux;

编译kernel之后记得还需要重新生成下脚本

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- scripts_gdb

(gdb) lx-symbols
loading vmlinux
scanning for modules in /home/geek/workspace/linux/linux-6.6.1
loading @0xffff80007a860000: /home/geek/workspace/linux/linux-6.6.1/drivers/my_driver/hello_driver.ko

这个指令会自动加载ko symbols, 直接加断点即可调试,免去了前面通过 add-symbol-file 设置ko section的繁琐步骤;(但是如果你是调试驱动 init的部分,还是得使用上面的方法2)

方法3虽然配置有些麻烦,但是vmlinux-gdb.py 提供了非常多的调试功能 apropos lx 可以查看,这些调试功能能帮助我们提升调试的效率

(gdb) apropos lx
function lx_clk_core_lookup -- Find struct clk_core by name
function lx_current -- Return current task.
function lx_dentry_name -- Return string of the full path of a dentry.
function lx_device_find_by_bus_name -- Find struct device by bus and name (both strings)
function lx_device_find_by_class_name -- Find struct device by class and name (both strings)
function lx_i_dentry -- Return dentry pointer for inode.
function lx_module -- Find module by name and return the module variable.
function lx_per_cpu -- Return per-cpu variable.
function lx_radix_tree_lookup --  Lookup and return a node from a RadixTree.
function lx_rb_first -- Lookup and return a node from an RBTree
function lx_rb_last -- Lookup and return a node from an RBTree.
function lx_rb_next -- Lookup and return a node from an RBTree.
function lx_rb_prev -- Lookup and return a node from an RBTree.
function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable.
function lx_thread_info -- Calculate Linux thread_info from task variable.
function lx_thread_info_by_pid -- Calculate Linux thread_info from task variable found by pid
lx-clk-summary -- Print clk tree summary
lx-cmdline --  Report the Linux Commandline used in the current kernel.
lx-configdump -- Output kernel config to the filename specified as the command
lx-cpus -- List CPU status arrays
lx-device-list-bus -- Print devices on a bus (or all buses if not specified)
lx-device-list-class -- Print devices in a class (or all classes if not specified)
lx-device-list-tree -- Print a device and its children recursively
lx-dmesg -- Print Linux kernel log buffer.
lx-dump-page-owner -- Dump page owner
lx-fdtdump -- Output Flattened Device Tree header and dump FDT blob to the filename
lx-genpd-summary -- Print genpd summary
lx-getmod-by-textaddr -- Look up loaded kernel module by text address.
lx-interruptlist -- Print /proc/interrupts
lx-iomem -- Identify the IO memory resource locations defined by the kernel
lx-ioports -- Identify the IO port resource locations defined by the kernel
lx-list-check -- Verify a list consistency
lx-lsmod -- List currently loaded modules.
lx-mounts -- Report the VFS mounts of the current process namespace.
lx-page_address -- struct page to linear mapping address
lx-page_to_pfn -- struct page to PFN
lx-page_to_phys -- struct page to physical address
lx-pfn_to_kaddr -- PFN to kernel address
lx-pfn_to_page -- PFN to struct page
lx-ps -- Dump Linux tasks.
lx-slabinfo -- Show slabinfo
lx-slabtrace -- Show specific cache slabtrace
lx-sym_to_pfn -- symbol address to PFN
lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules.
lx-timerlist -- Print /proc/timer_list
lx-version --  Report the Linux Version of the current kernel.
lx-virt_to_page -- virtual address to struct page
lx-virt_to_phys -- virtual address to physical address
lx-vmallocinfo -- Show vmallocinfo

比如我想要查看所有task的信息,执行lx-ps即可,如果要看task_struct的详细信息,将显示的地址转换成struct task_struct *指针即可显示

比如查看一个进程的进程名
(gdb) p ((struct task_struct*)0xffff000002d68ec0)->comm
$6 = "watchdogd\000\000\000\000\000\000"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/653274.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java日志框架总结(三 、Log4j日志框架)

一、简介 Log4j ( Logger For Java ) , Java 日志的记录包。 官方网站 。Log4j 是 Apache 的一个开源项目&#xff0c; 为Java提供了日志记录功能。能够让程序员非常方便的记录日志&#xff0c; 并且提供了多种适配方式&#xff0c;能满足各种需求。 使用Log4j 只需要导入一个…

【设计模式】腾讯面经:原型模式怎么理解?

什么是原型模式&#xff1f; 设计模式是编程世界的基石&#xff0c;其中原型模式无疑是一种常用而又高效的创建对象的手段。那么&#xff0c;什么是原型模式呢&#xff1f;又该如何去实现它&#xff1f; 在软件工程中&#xff0c;原型模式是一种创建型设计模式。我们可以这样…

2024獬豸杯完整Writeup

文章目录 手机手机基本信息- 1、IOS手机备份包是什么时候开始备份的。&#xff08;标准格式&#xff1a;2024-01-20.12:12:12)手机基本信息- 2、请分析&#xff0c;该手机共下载了几款即时通讯工具。&#xff08;标准格式&#xff1a;阿拉伯数字&#xff09;手机基本信息- 3、手…

Zerosync:构建基于STARK的Bitcoin证明系统

1. 引言 前序博客&#xff1a; BitcoinSTARK: ZeroSync & Khepri Robin Linus、Tino Steffens、Lukas George 等人成立了一个名为 ZeroSync 协会&#xff08;ZeroSync Association&#xff09;的瑞士非营利组织&#xff0c;该组织将牵头开发比特币证明系统。ZeroSync 于…

共享自助空间打破传统束缚,创新消费体验

共享自助空间是指将传统的办公空间、工作空间、社交空间等资源进行共享&#xff0c;为个体或小型团体提供灵活的使用服务和自主管理的空间。这种模式使得个人可以在一个共享的环境中独立办公、工作或社交&#xff0c;并能享受到共享资源和服务的便利&#xff0c;比如共享茶室、…

超温报警器电路设计方案汇总

超温报警器电路设计方案&#xff08;一&#xff09; 该超温报警电路由温度采集电路、继电器控制电路、延时电路、秒脉冲信号发生器、计数译码电路、数显电路、报警电路共同构成。下面来详细介绍一下各部分电路的功能。 温度采集电路 温度采集电路由负温度系数的热敏电阻RW、R…

PDF标准详解(一)——PDF文档结构

已经很久没有写博客记录自己学到的一些东西了。但是在过去一年的时间中自己确实又学到了一些东西。一直攒着没有系统化成一篇篇的文章&#xff0c;所以今年的博客打算也是以去年学到的一系列内容为主。通过之前Vim系列教程的启发&#xff0c;我发现还是写一些系列文章对自己的帮…

Day01-变量和数据类型课后练习-参考答案

文章目录 1、输出你最想说的一句话&#xff01;2、定义所有基本数据类型的变量和字符串变量3、用合适类型的变量存储个人信息并输出4、定义圆周率PI5、简答题 1、输出你最想说的一句话&#xff01; 编写步骤&#xff1a; 定义类 Homework1&#xff0c;例如&#xff1a;Homewo…

测试C#调用OpenCvSharp和IronOcr从摄像头中识别文字

学习了基于OpenCvSharp获取摄像头数据&#xff0c;同时学习了基于IronOcr的文字识别用法&#xff0c;将这两者结合即是从摄像头中识别文字。本文测试C#调用OpenCvSharp和IronOcr从摄像头中识别文字的基本用法、。   新版Winform项目&#xff0c;在Nuget包管理器中添加以下程序…

案例分析技巧-软件工程

一、考试情况 需求分析&#xff08;※※※※&#xff09;面向对象设计&#xff08;※※&#xff09; 二、结构化需求分析 数据流图 数据流图的平衡原则 数据流图的答题技巧 利用数据平衡原则&#xff0c;比如顶层图的输入输出应与0层图一致补充实体 人物角色&#xff1a;客户、…

告别繁琐!轻松创建旧版Spring Boot项目!

推荐文章 给软件行业带来了春天——揭秘Spring究竟是何方神圣&#xff08;一&#xff09; 给软件行业带来了春天——揭秘Spring究竟是何方神圣&#xff08;二&#xff09; 给软件行业带来了春天——揭秘Spring究竟是何方神圣&#xff08;三&#xff09; 给软件行业带来了春天—…

[Python图像处理] 使用OpenCV创建深度图

使用OpenCV创建深度图 双目视觉创建深度图相关链接双目视觉 在传统的立体视觉中,两个摄像机彼此水平移动,用于获得场景上的两个不同视图(作为立体图像),就像人类的双目视觉系统: 通过比较这两个图像,可以以视差的形式获得相对深度信息,该视差编码对应图像点的水平坐标的…

基于Python 网络爬虫和可视化的房源信息的设计与实现

摘 要 一般来说&#xff0c;在房地产行业&#xff0c;房源信息采集&#xff0c;对企业来说至关重要&#xff0c;通过人工采集数据的方式进行数据收集&#xff0c;既耗时又费力&#xff0c;影响工作效率&#xff0c;还导致信息时效性变差&#xff0c;可靠性偏低&#xff0c;不利…

QWT开源库使用

源代码地址&#xff1a;Qwt Users Guide: Qwt - Qt Widgets for Technical Applications Qwt库包含GUI组件和实用程序类&#xff0c;它们主要用于具有技术背景的程序。除了2D图的框架外&#xff0c;它还提供刻度&#xff0c;滑块&#xff0c;刻度盘&#xff0c;指南针&#xf…

matlab appdesigner系列-仪器仪表4-旋钮(离散)

旋钮&#xff08;离散&#xff09;&#xff0c;或叫分档旋钮&#xff0c;跟旋钮的连续性相区别&#xff0c;呈分档性。 示例&#xff1a;模拟空调档位切换 操作步骤&#xff1a; 1&#xff09;将旋钮&#xff08;离散&#xff09;、信号灯、标签拖拽到画布上&#xff0c;并设…

CSS 星空按钮

<template><button class="btn" type="button"><strong>星空按钮</strong><div id="container-stars"><div id="stars"></div></div><div id="glow"><div class=…

Kafka-服务端-GroupMetadataManager

GroupMetadataManager是GroupCoordinator中负责管理Consumer Group元数据以及其对应offset信息的组件。 GroupMetadataManager底层使用Offsets Topic,以消息的形式存储Consumer Group的GroupMetadata信息以及其消费的每个分区的offset,如图所示。 consumer_offsets的某Partiti…

每日一题——LeetCode1365.有多少小于当前数字的数字

方法一 暴力循环 对于数组里的没一个元素都遍历一遍看有多少元素小于当前元素 var smallerNumbersThanCurrent function(nums) {let n nums.length;let ret [];for (let i 0; i < n; i) {let count 0;for (let j 0; j < n; j) {if (nums[j] < nums[i]) {count…

菜谱的未来:SpringBoot, Vue与MySQL的智能推荐系统设计

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

算法学习记录:动态规划

前言&#xff1a; 算法学习记录不是算法介绍&#xff0c;本文记录的是从零开始的学习过程&#xff08;见到的例题&#xff0c;代码的理解……&#xff09;&#xff0c;所有内容按学习顺序更新&#xff0c;而且不保证正确&#xff0c;如有错误&#xff0c;请帮助指出。 学习工具…