linux驱动调试--段错误之oops信息分析
原文地址
http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29401328&id=4923447
发生段错误原因就是访问了不该访问的地址,例如访问了不存在的内存地址、访问了系统保护的内存地址、访问了只读的内存地址等。
下面根据Oops信息来分析一下段错误
first_drv.c
#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/io.h>#include <linux/device.h>static struct class *segment_class;static struct device *segment_class_dev;unsigned char *c = NULL;int major;static int segment_test_open(struct inode *inode, struct file *file){*c = 0x34;//printk("segment_test_open success!\n");return 0;}static struct file_operations segment_test_fops = {.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */.open = segment_test_open,};static int segment_drv_init(void){major = register_chrdev(0, "segment_test", &segment_test_fops); // 注册, 告诉内核segment_class = class_create(THIS_MODULE, "segment_test");segment_class_dev = device_create(segment_class, NULL, MKDEV(major, 0), NULL, "segment");c = (unsigned char *)0x48000000;printk("segment_drv_init success!\n");return 0;}static void segment_drv_exit(void){ device_destroy(segment_class, MKDEV(major,0));class_destroy(segment_class);unregister_chrdev(major, "segment_test");}module_init(segment_drv_init);module_exit(segment_drv_exit);MODULE_LICENSE("GPL");
firstdrvtest.c
#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>int main(int argc, char **argv){int fd;int val = 1;fd = open("/dev/segment", O_RDWR);if (fd < 0){printf("can't open!\n");return -1;}close(fd);return 0;}
# insmod first_drv.ko
# ./firstdrvtest
Unable to handle kernel paging request at virtual address 48000000 // 内核使用48000000来访问时发生了错误
pgd = c3b4c000
[48000000] *pgd=00000000
Internal error: Oops: 805 [#1]
Modules linked in: first_drv rt5370sta zd1211rw mac80211
CPU: 0 Not tainted (2.6.30.4-EmbedSky #1)
PC is at segment_test_open+0x1c/0x28 [first_drv]// PC值
LR is at chrdev_open+0xcc/0x170
pc : []lr : [] psr: a0000013 // 发生错误时各寄存器的值(下面五行)
sp : c3a61e30 ip : c3a61e40 fp : c3a61e3c
r10: c394bc80 r9 : 00000002 r8 : c34b7600
r7 : c3b46100 r6 : c3ab84b0 r5 : c3a62180 r4 : 00000000
r3 : 00000034 r2 : 48000000 r1 : c3b46100 r0 : 00000000
Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
Control: c000717f Table: 33b4c000 DAC: 00000015
Process firstdrvtest (pid: 637, stack limit = 0xc3a60268) // 发生错误时当前进程的名称是firstdrvtest
Stack: (0xc3a61e30 to 0xc3a62000) // 栈
1e20: c3a61e64 c3a61e40 c00a8580 bf0d7010
1e40: c00adba8 00000000 00000000 c3b46100 c3ab84b0 c00a84b4 c3a61e8c c3a61e68
1e60: c00a3a7c c00a84c4 c3b46100 c2c0ae40 00000003 c3af0000 00000026 c3a61ed8
1e80: c3a61eac c3a61e90 c00a3d14 c00a39bc 00000000 c2c0ae40 00000000 00000000
1ea0: c3a61f64 c3a61eb0 c00b0c80 c00a3cc0 c3a61f7c c3a61ec0 c004b714 c006f8b8
1ec0: c3a61efc beb5ad9c 00000000 00000000 c3a63000 c048070c c394bc80 c34b7600
1ee0: c048077c c3a61fb0 00000000 00000101 00000001 00000000 c00441e0 c004b548
1f00: 08100875 c39568a0 c3a7ec00 0000001c 00000000 00001000 00000003 00000003
1f20: 00000000 c3b46100 00000000 c3a60000 c3a61f64 c3a61f40 c00b99b8 00000003
1f40: c3af0000 00000002 beb5ad9c ffffff9c c3a60000 00000000 c3a61f94 c3a61f68
1f60: c00a38d8 c00b0aa0 00000000 40025000 c3a61f9c 0000850c 00000000 000083e0
1f80: 00000005 c0045008 c3a61fa4 c3a61f98 c00a3988 c00a3878 00000000 c3a61fa8
1fa0: c0044e60 c00a3974 0000850c 00000000 00008590 00000002 beb5ad9c 00000001
1fc0: 0000850c 00000000 000083e0 00000005 00000000 00000000 40025000 beb5ac44
1fe0: 00000000 beb5ac28 000084b8 400efd9c 60000010 00008590 00000000 00000000
Backtrace: // 回溯信息
[] (segment_test_open+0x0/0x28 [first_drv]) from [] (chrdev_open+0xcc/0x170)
[] (chrdev_open+0x0/0x170) from [] (__dentry_open+0xd0/0x270)
r7:c00a84b4 r6:c3ab84b0 r5:c3b46100 r4:00000000
[] (__dentry_open+0x0/0x270) from [] (nameidata_to_filp+0x64/0x6c)
[] (nameidata_to_filp+0x0/0x6c) from [] (do_filp_open+0x1f0/0x7e8)
r5:00000000 r4:00000000
[] (do_filp_open+0x0/0x7e8) from [] (do_sys_open+0x70/0xe8)
[] (do_sys_open+0x0/0xe8) from [] (sys_open+0x24/0x28)
r8:c0045008 r7:00000005 r6:000083e0 r5:00000000 r4:0000850c
[] (sys_open+0x0/0x28) from [] (ret_fast_syscall+0x0/0x2c)
Code: e59f3010 e3a00000 e5932000 e3a03034 (e5c23000)
---[ end trace d31b8aee70b25c9c ]---
Segmentation fault
上面的这些调试信息包含了很多内容,我们可以根据其中的一部分就可以定位出问题,下面逐一介绍一下。
一、直接确定发生错误的函数
看到这句 “PC is at segment_test_open+0x1c/0x28 [first_drv]”,出现错误时我们最关注的就是PC值,因为它就是发生错误
的指令的地址,这里我们可以看到错误发生在函数 segment_test_open 的0x1c处,0x28代表这个函数的总长度(汇编代码)
二、根据PC值确定发生错误的函数
有时候不会直接告诉你发生在哪个函数,而是只把PC值告诉你:
pc : []
这时你要根据PC值自己找到发生错误的地方,怎么找呢?
现在我们知道发生错误时 PC = 0xbf0d701c,我们首先要确定发生的错误位置是在内核中还是在外面的模块里,
然后根据PC值找出发生的函数及指令。怎么确定?
1. 进入到我们内核源码的根目录下,找到System.map,这个文件指示了所有的内核函数的地址范围,
我们可以观察,发生错误时PC值是不是在这个文件的地址范围内,例如我的这个文件的地址范围是:
c0004000 A swapper_pg_dir ~~~ c04ec044 B _end
如果不属于System.map里的范围,则它属于insmod加载的驱动程序,这里可以看到bf0d701c属于模块地址
2.知道错误在模块里了,那么怎么确定是哪一个驱动程序?
在开发板上查看:
# cat /proc/kallsyms // 内核函数、加载的函数的地址,t是静态函数,T是全局函数
从这些信息里找到一个与PC值相近的地址
比如找到了:
00000000 a first_drv.c [first_drv]
bf0d7000 t $a [first_drv]
bf0d7000 t segment_test_open [first_drv]
bf0d7024 t $d [first_drv]
bf0d7028 t $a [first_drv]
bf0d7028 t segment_drv_exit [first_drv]
这里可以看出来,PC=bf0d701c 是属于segment_test_open函数
其实,我们只通过“cat /proc/kallsyms”就可以知道是哪个函数发生了错误,步骤1只是让我们知道这个函数是属于内核的还是模块的
三、通过回溯信息确定发生错误的函数
Backtrace:
[] (segment_test_open+0x0/0x28 [first_drv]) from [] (chrdev_open+0xcc/0x170)
省略好几行
这部分是回溯信息,从最后调用的发生错误的函数层层打印出函数的调用关系,上一行的函数被下一行的调用。
注意:在配置内核时,需要选择 FRAME_POINTER = y 才会有回溯信息,如果没有,可以根据栈信息分析
四、定位发生错误的代码(需要汇编阅读能力)
上面几种方法都只定位了发生错误的函数,怎么定位到是哪一句代码发生了错误呢?
1. 如果发生的错误函数是属于模块的,如我们的这个实例
segment_test_open+0x1c/0x28
这里的0x1c是指汇编代码的地址,所以我们要把这个模块反汇编,然后定位。
# arm-none-linux-gnueabi-objdump -D first_drv.ko > first_drv.dis
打开first_drv.dis有下面这一段:
00000000 :
0: e1a0c00d mov ip, sp
4: e92dd800 push {fp, ip, lr, pc}
8: e24cb004 sub fp, ip, #4 ; 0x4
c: e59f3010 ldr r3, [pc, #16] ; 24
10: e3a00000 mov r0, #0 ; 0x0
14: e5932000 ldr r2, [r3]
18: e3a03034 mov r3, #52 ; 0x34
1c: e5c23000 strb r3, [r2]
20: e89da800 ldm sp, {fp, sp, pc}
24: 00000000 .word 0x00000000
这里代码的实际地址都要加上偏移地址 bf0d7000,发生错误的那句代码是:
1c: e5c23000 strb r3, [r2]
根据我们的C语言代码可以看出这里是把0x34赋给变量时产生错误,产生错误的原因是加载模块初始化时赋给的一个地址非法:
c = (unsigned char *)0x48000000;
我们这里的程序比较短,可以一眼看出来,如果代码很长,就可以根据发生错误的位置,大概确定代码的位置,
然后再去看代码和汇编,这里要求比较高的汇编阅读能力
2. 如果发生的错误函数是属于内核的
这个时候和发生在模块里类似,不过这里要反汇编整个内核:
# arm-none-linux-gnueabi-objdump -D vmlinux > vmlinux.dis
打开vmlinux.dis,然后直接查找地址bf0d7000,接下来像上面一样分析代码
关于上面的打印信息,还有栈那一段没有讲,放到下一篇博文说
linux驱动调试--段错误之oops信息分析相关推荐
- Linux驱动段错误,linux驱动调试--段错误之oops信息分析
下面根据Oops信息来分析一下段错误 first_drv.c 点击(此处)折叠或打开 #include #include #include #include #include #include #in ...
- linux驱动调试--段错误之栈信息分析
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29401328&id=4923529 接着上一篇来分析一下Oop ...
- linux驱动获取函数失败信息,linux驱动调试--段错误之栈信息分析
接着上一篇来分析一下Oops的栈 s3c2440平台 关于调试源码和整个Oops信息请参考上一篇博文,这里只再次贴出关于栈的信息 Stack: (0xc3a61e30 to 0xc3a62000) 1 ...
- linux 捕获sigsegv信息如何生成core文件,[转]Linux下的段错误产生的原因及调试方法Core Dump...
简而言之,产生段错误就是访问了错误的内存段,一般是你没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址. 一般来说,段错误就是指访问的内存超出了系统所给这个程序的内存空间,通常这个值是由 ...
- Linux环境下段错误的产生原因及调试方法小结
From:http://www.cnblogs.com/panfeng412/archive/2011/11/06/2237857.html 最近在Linux环境下做C语言项目,由于是在一个原有项目基 ...
- [转]Linux环境下段错误的产生原因及调试方法小结
最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的"段错误"(Segme ...
- [转] Linux C语言 段错误bug的调试
原来看过一次,后来当自己有段错误的时候,才想起来这个很有用.如果不用他的方法,段错误很恶心的,不好找出来的. 下面就是原文,不过 最初出处不得而知 ======================= 复制 ...
- Linux下的段错误产生的原因及调试方法-转
分类: Linux--Ubuntu入门级 重学C/C++2011-10-19 22:13 332人阅读 评论(0) 收藏 举报 因为你调用了glibc的fputs 检查你传进去的char* +++++ ...
- linux程序运行段错误,Linux下的段错误产生的原因及调试方法
Linux下的段错误产生的原因及调试方法 简而言之,产生段错误就是访问了错误的内存段,一般是你没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址. 一般来 说,段错误就是指访问的内存超出 ...
最新文章
- 使用Word2010灵活掌握文档结构
- 刘政:别着急颠覆传统数据分析人才培养,先将落脚点放在实处
- ORA-600 [kddummy_blkchk] [18038] 一例
- java swing 表格不显示_JAVA SWING 表头不显示问题
- 使用字符串解析的方式完成计算器的设计思路
- 获取窗口上指定控件集合 2012-08-22 16:14 498人阅读 评论(0) 收藏...
- java中的foreach
- centos6架设dhcp服务器提供两个网段的IP地址分配
- Javascript数据类型,类型转换
- Java程序员如何写好一份个人求职简历
- (Rock, Paper, Scissors, Lizard and Spock)sheldon谢耳朵独创的剪刀,布,石头,蜥蜴,史波克 用代码实现
- vscode缩放代码_Visual Studio Code 缩放设置
- 计算机基础应用在线免费答题,计算机应用基础简答题附答案.doc
- 如何打造智能世界的数据底座?深耕华为云大数据,畅享价值最大化
- set、env、export的区别
- 深圳/河南/天津/山东产品经理国际认证NPDP招生简章
- win10 家庭中文版内存占用过高
- 开源一个后台基于bmob云端的社交app
- 地图上道路编号中的G S X Y
- python可视化词云图WordCloud
热门文章
- 最新CSGO国服能取回皮肤的国内开箱网站推荐大全
- java g1 详解_JAVA垃圾收集算法总结以及CMS、G1算法详解
- 论文笔记《Are You Talking to Me? Reasoned Visual Dialog Generation through Adversarial Learning》
- 汉语是思维广阔敏捷的语言
- Google Maps Android API
- Win11设置鼠标箭头图案的方法教程
- HAProxy配置详解
- Hive中各种日期格式转换方法总结
- 低级程序员和高级程序员的区别在于?
- java script实训心得_javascript实训报告总结.docx