26、驱动调试之根据oops信息和堆栈确定出错的代码
a.驱动作为模块:
1. 根据pc值确定该指令属于内核还是外加的模块
pc=0xbf000018 它属于什么的地址?是内核还是通过insmod加载的驱动程序?
先判断是否属于内核的地址: 看System.map确定内核的函数的地址范围:c0004000~c03265a4
<System.map文件在内核make uImage后产生,在内核顶层目录下>
如果不属于System.map里的范围,则它属于insmod加载的驱动程序
2. 假设它是加载的驱动程序引入的错误,怎么确定是哪一个驱动程序?
先看看加载的驱动程序的函数的地址范围
cat /proc/kallsyms > /kallsyms.txt (内核函数、加载的函数的地址)
从这些信息里找到一个相近的地址, 这个地址<=0xbf000018
比如找到了:
bf000000 t first_drv_open [first_drv]
3. 找到了first_drv.ko
在PC上反汇编它: arm-linux-objdump -D first_drv.ko > frist_drv.dis
在dis文件里找到first_drv_open
first_drv.dis文件里 insmod后
00000000 <first_drv_open> : bf000000 t first_drv_open [first_drv]
00000018 pc = bf000018
(18: ldr r3, [r2] 将r2地址的值给r3,根据oops信息找到出错时r2的值,根据发送错误时的打印信息查看r2的值)
./firstdrvtest on
Unable to handle kernel paging request at virtual address 56000050
内核使用56000050来访问时发生了错误
pgd = c3eb0000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: first_drv
CPU: 0 Not tainted (2.6.22.6 #1)
PC is at first_drv_open+0x18(该指令的偏移)/0x3c(该函数的总大小) [first_drv]
PC就是发生错误的指令的地址
大多时候,PC值只会给出一个地址,不到指示说是在哪个函数里
LR is at chrdev_open+0x14c/0x164
LR寄存器的值
pc = 0xbf000018
pc : [<bf000018>] lr : [<c008d888>] psr: a0000013
sp : c3c7be88 ip : c3c7be98 fp : c3c7be94
r10: 00000000 r9 : c3c7a000 r8 : c049abc0
r7 : 00000000 r6 : 00000000 r5 : c3e740c0 r4 : c06d41e0
r3 : bf000000 r2 : 56000050 r1 : bf000964 r0 : 00000000
执行这条导致错误的指令时各个寄存器的值
Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user
Control: c000717f Table: 33eb0000 DAC: 00000015
Process firstdrvtest (pid: 777, stack limit = 0xc3c7a258)
发生错误时当前进程的名称是firstdrvtest
栈
Stack: (0xc3c7be88 to 0xc3c7c000)
be80: c3c7bebc c3c7be98 c008d888 bf000010 00000000 c049abc0
bea0: c3e740c0 c008d73c c0474e20 c3e766a8 c3c7bee4 c3c7bec0 c0089e48 c008d74c
bec0: c049abc0 c3c7bf04 00000003 ffffff9c c002c044 c3d10000 c3c7befc c3c7bee8
bee0: c0089f64 c0089d58 00000000 00000002 c3c7bf68 c3c7bf00 c0089fb8 c0089f40
bf00: c3c7bf04 c3e766a8 c0474e20 00000000 00000000 c3eb1000 00000101 00000001
bf20: 00000000 c3c7a000 c04a7468 c04a7460 ffffffe8 c3d10000 c3c7bf68 c3c7bf48
bf40: c008a16c c009fc70 00000003 00000000 c049abc0 00000002 bec1fee0 c3c7bf94
bf60: c3c7bf6c c008a2f4 c0089f88 00008520 bec1fed4 0000860c 00008670 00000005
bf80: c002c044 4013365c c3c7bfa4 c3c7bf98 c008a3a8 c008a2b0 00000000 c3c7bfa8
bfa0: c002bea0 c008a394 bec1fed4 0000860c 00008720 00000002 bec1fee0 00000001
bfc0: bec1fed4 0000860c 00008670 00000002 00008520 00000000 4013365c bec1fea8
bfe0: 00000000 bec1fe84 0000266c 400c98e0 60000010 00008720 00000000 00000000
Backtrace: (回溯)(需要make menuconfig配置内核支持回溯
kernel hacking
<*> kernel debugging
)
(回溯的原理是在执行函数的时候把sp指针赋给fp寄存器,段错误时根据fp来回溯,可以看汇编代码,主要根据sp保存的lr来一步步回推,lr是调用者的地址)
[<bf000000>] (first_drv_open+0x0/0x3c [first_drv]) from [<c008d888>] (chrdev_open+0x14c/0x164)
[<c008d73c>] (chrdev_open+0x0/0x164) from [<c0089e48>] (__dentry_open+0x100/0x1e8)
r8:c3e766a8 r7:c0474e20 r6:c008d73c r5:c3e740c0 r4:c049abc0
[<c0089d48>] (__dentry_open+0x0/0x1e8) from [<c0089f64>] (nameidata_to_filp+0x34/0x48)
[<c0089f30>] (nameidata_to_filp+0x0/0x48) from [<c0089fb8>] (do_filp_open+0x40/0x48)
r4:00000002
[<c0089f78>] (do_filp_open+0x0/0x48) from [<c008a2f4>] (do_sys_open+0x54/0xe4)
r5:bec1fee0 r4:00000002
[<c008a2a0>] (do_sys_open+0x0/0xe4) from [<c008a3a8>] (sys_open+0x24/0x28)
[<c008a384>] (sys_open+0x0/0x28) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
Code: e24cb004 e59f1024 e3a00000 e5912000 (e5923000)
Segmentation fault
#
b. 编入内核
Modules linked in:
CPU: 0 Not tainted (2.6.22.6 #2)
PC is at first_drv_open+0x18/0x3c
LR is at chrdev_open+0x14c/0x164
pc : [<c014e6c0>] lr : [<c008638c>] psr: a0000013
sp : c3a03e88 ip : c3a03e98 fp : c3a03e94
r10: 00000000 r9 : c3a02000 r8 : c03f3c60
r7 : 00000000 r6 : 00000000 r5 : c38a0c50 r4 : c3c1e780
r3 : c014e6a8 r2 : 56000050 r1 : c031a47c r0 : 00000000
Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user
Control: c000717f Table: 339f0000 DAC: 00000015
Process firstdrvtest (pid: 750, stack limit = 0xc3a02258)
1. 根据pc值确定该指令属于内核还是外加的模块
pc=c014e6c0 属于内核(看System.map)
2. 反汇编内核: arm-linux-objdump -D vmlinux > vmlinux.dis
在dis文件里搜c014e6c0
c014e6a8 <first_drv_open>:
c014e6a8: e1a0c00d mov ip, sp
c014e6ac: e92dd800 stmdb sp!, {fp, ip, lr, pc}
c014e6b0: e24cb004 sub fp, ip, #4 ; 0x4
c014e6b4: e59f1024 ldr r1, [pc, #36] ; c014e6e0 <.text+0x1276e0>
c014e6b8: e3a00000 mov r0, #0 ; 0x0
c014e6bc: e5912000 ldr r2, [r1]
c014e6c0: e5923000 ldr r3, [r2] // 在此出错 r2=56000050
3. 根据栈信息分析函数调用过程
# ./firstdrvtest on
Unable to handle kernel paging request at virtual address 56000050
pgd = c3e78000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: first_drv
CPU: 0 Not tainted (2.6.22.6 #48)
PC is at first_drv_open+0x18/0x3c [first_drv]
LR is at chrdev_open+0x14c/0x164
pc : [<bf000018>] lr : [<c008c888>] psr: a0000013
3.1 根据PC确定出错位置
bf000018 属于 insmod的模块
bf000000 t first_drv_open [first_drv]
3.2 确定它属于哪个函数
反汇编first_drv.ko
通过查看反汇编代码可以看到进入first_drv_open 函数后执行stmdb sp!,{fp,ip,lr,pc}保存了四个寄存器信息,fp就是r15,lr就是r14,ip就是r12,fp就是r11,SP制作是往低位地址移动,高位先保存pc,接着lr、ip、fp,每个寄存器32位,lr就是返回地址即调用函数的地址。
举例:下面打印的栈信息中第一个栈的lr是c008c888 ,根据前面分析可以得到是内核地址,查看反汇编确认调用函数,分析调用函数来确认其占用了多大的栈,在栈信息中,first_drv_open 栈用4*4字节的栈,后面就是调用者的栈,根据前面确认其栈大小,重复前面的步骤
sp : c3e69e88 ip : c3e69e98 fp : c3e69e94
r10: 00000000 r9 : c3e68000 r8 : c0490620
r7 : 00000000 r6 : 00000000 r5 : c3e320a0 r4 : c06a8300
r3 : bf000000 r2 : 56000050 r1 : bf000964 r0 : 00000000
Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user
Control: c000717f Table: 33e78000 DAC: 00000015
Process firstdrvtest (pid: 752, stack limit = 0xc3e68258)
Stack: (0xc3e69e88 to 0xc3e6a000)
9e80: c3e69ebc c3e69e98 c008c888 bf000010 00000000 c0490620
first_drv_open'sp lr chrdev_open'sp
9ea0: c3e320a0 c008c73c c0465e20 c3e36cb4 c3e69ee4 c3e69ec0 c0088e48 c008c74c
lr
9ec0: c0490620 c3e69f04 00000003 ffffff9c c002b044 c06e0000 c3e69efc c3e69ee8
__dentry_open'sp
9ee0: c0088f64 c0088d58 00000000 00000002 c3e69f68 c3e69f00 c0088fb8 c0088f40
lr nameidata_to_filp'sp lr
9f00: c3e69f04 c3e36cb4 c0465e20 00000000 00000000 c3e79000 00000101 00000001
do_filp_open'sp
9f20: 00000000 c3e68000 c04c1468 c04c1460 ffffffe8 c06e0000 c3e69f68 c3e69f48
9f40: c008916c c009ec70 00000003 00000000 c0490620 00000002 be94eee0 c3e69f94
9f60: c3e69f6c c00892f4 c0088f88 00008520 be94eed4 0000860c 00008670 00000005
lr do_sys_open'sp
9f80: c002b044 4013365c c3e69fa4 c3e69f98 c00893a8 c00892b0 00000000 c3e69fa8
lr sys_open'sp
9fa0: c002aea0 c0089394 be94eed4 0000860c 00008720 00000002 be94eee0 00000001
lr ret_fast_syscall'sp
9fc0: be94eed4 0000860c 00008670 00000002 00008520 00000000 4013365c be94eea8
9fe0: 00000000 be94ee84 0000266c 400c98e0 60000010 00008720 00000000 00000000
26、驱动调试之根据oops信息和堆栈确定出错的代码的更多相关文章
- linux驱动调试--段错误之oops信息分析
linux驱动调试--段错误之oops信息分析 http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29401328&id= ...
- linux设备驱动第四篇:从如何定位oops的代码行谈驱动调试方法
上一篇我们大概聊了如何写一个简单的字符设备驱动,我们不是神,写代码肯定会出现问题,我们需要在编写代码的过程中不断调试.在普通的c应用程序中,我们经常使用printf来输出信息,或者使用gdb来调试程序 ...
- 驱动调试(四)oops确定调用树
目录 驱动调试(四)oops确定调用树 内核开启调用树 栈指针分析 原理 寄存器别名 基础解释 例子分析 找到PC地址的位置 栈分析 附录:原文的excel title: 驱动调试(四)oops确定调 ...
- 驱动调试(三)oops确定函数PC
目录 驱动调试(三)oops确定函数PC 什么是oops 流程简述 代码仓库 模块例子分析 找到PC值 判断是否属于模块 查看符号表 找到模块 反汇编模块 内核例子分析 找到PC值 判断是否属于模块 ...
- linux中oops信息的调试及栈回溯【转】
本文转载自:http://blog.csdn.net/kangear/article/details/8217329 ========================================= ...
- 驱动调试-根据oops定位错误代码行
1.当驱动有误时,比如,访问的内存地址是非法的,便会打印一大串的oops出来 1.1以LED驱动为例 将open()函数里的ioremap()屏蔽掉,直接使用物理地址的GPIOF,如下图所示: 1.2 ...
- Linux中oops信息调试【转】
1.Oops 信息来源及格式 Oops 这个单词含义为“惊讶”,当内核出错时(比如访问非法地址)打印出来的信息被称为 Oops 信息. 2.Oops 信息包含以下几部分内容 2.1 一段文本描述信息. ...
- 36.Linux驱动调试-根据oops定位错误代码行
1.当驱动有误时,比如,访问的内存地址是非法的,便会打印一大串的oops出来 1.1以LED驱动为例 将open()函数里的ioremap()屏蔽掉,直接使用物理地址的GPIOF,如下图所示: 1.2 ...
- linux中Oops信息的调试及栈回溯
Oops 信息来源及格式 Oops 这个单词含义为“惊讶” ,当内核出错时(比如访问非法地址)打印出来的信息被 称为 Oops 信息. Oops 信息包含以下几部分内容. 1 一段文本描述信息. 比如 ...
随机推荐
- C/C++(数据结构链表的实现)
链表 List 链表实现了内存零碎片的有效组织. 静态链表 链表中有两个成员,数据域和指针域 数据域:我们存储的数据. 指针域:指针指向下一个具体的节点,代表了下一个节点的类型是链表类型. 所谓的指针 ...
- 趣题: 按二进制中1的个数枚举1~2^n (位运算技巧)
; ; k <= n; k++){ << k)-,u = << n; s < u;){ ;i < n;i++) printf(-i)&); print ...
- 【Swing】一点基础操作
之前实训的老师不推荐swing就没有学...然而学校老师又是另一种态度...加上学长作比赛用swing...学一下吧 1.将窗体放在中间 jdk1.4之后setLocationRelativeTo(o ...
- windows查看硬件信息系统信息
rem 查看cpu wmic cpu list briefrem 查看物理内存wmic memphysical list briefrem 查看逻辑内存wmic memlogical list bri ...
- 【t038】&&【u214】金字塔
Time Limit: 1 second Memory Limit: 128 MB [问题描述] 小X来到一个雄奇的金字塔挖宝,但是这是一座被诅咒的金字塔,小X必须马上逃离这里,否则小X就会被埋在金字 ...
- android关键组件service服务(一)
一. Service简单介绍 Service是android 系统中的四大组件之中的一个(Activity.Service.BroadcastReceiver.ContentProvider),它跟A ...
- BZOJ 1696 [Usaco2007 Feb]Building A New Barn新牛舍 数学
题意:链接 方法:数学+模拟 解析: 首先这类问题不是第一次见了,所以直接知道拿x的中位数.y的中位数. 这题就是讨论情况很的烦. 题中有个限制,给出待求和的点不能选取. 所以假设奇数个点,求出x中位 ...
- Android仿Win8界面的button点击
今天没事的时候,感觉Win8的扁平化的button还是挺好看的,就研究了下怎样在安卓界面实现Win8的扁平化button点击效果. 发现了一个自己定义的View能够实现扁平化button效果,话不多说 ...
- funuiTitle-居中问题修改
今天遇到了一个问题,在一个actionbar上,title居中了,现在想要的方式是,让actionbar上显示返回按钮,后面紧跟着title.当时自己一直尝试要找到activity,然后在theme中 ...
- android图像处理系列之七--图片涂鸦,水印-图片叠加
图片涂鸦和水印其实是一个功能,实现的方式是一样的,就是一张大图片和一张小点图片叠加即可.前面在android图像处理系列之六--给图片添加边框(下)-图片叠加中也讲到了图片叠加,里面实现的原理是直接操 ...