驱动调试(三)oops确定函数PC
title: 驱动调试(三)oops确定函数PC
date: 2019/1/14 17:30:32
toc: true
驱动调试(三)oops确定函数PC
什么是oops
oops是 Linux 内核的一部分出现了偏差行为,你有做错了什么吗?可能没有。它实际上就是“哎呀” (oops),就像你刚掉下一杯酒或踩在你的猫身上。哎呀! “oops” 的复数是 “oopses”。
有些会存储在``/var/log/dmesg或/var/log/kern.log` 中
我们的板子可以使用dmesg来重新查看
流程简述

代码仓库
https://gitee.com/layty/Jz2440/tree/master/Driver/code/32th-oops/1th-通过PC确定函数位置
模块例子分析
原因: 取消寄存器设置中的ioremap,直接设置该地址
gpfcon = (volatile unsigned long *)0x56000050; //(volatile unsigned long *)ioremap(0x56000050, 16);
gpfcon = (volatile unsigned long *)0x56000050; //(volatile unsigned long *)ioremap(0x56000050, 16);
编译模块后,加载模块,调用应用程序,显示oops如下
# ./test on
Unable to handle kernel paging request at virtual address 56000050
pgd = c2c8c000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in:
CPU: 0 Not tainted (2.6.22.6 #3)
PC is at first_drv_open+0x18/0x3c
LR is at chrdev_open+0x14c/0x164
pc : [<c019df5c>] lr : [<c008d888>] psr: a0000013
sp : c2c71e88 ip : c2c71e98 fp : c2c71e94
r10: 00000000 r9 : c2c70000 r8 : c3e99cc0
r7 : 00000000 r6 : 00000000 r5 : c3e7c594 r4 : c04b8180
r3 : c019df44 r2 : 56000050 r1 : c03c3d5c r0 : 00000000
Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user
Control: c000717f Table: 32c8c000 DAC: 00000015
Process test (pid: 793, stack limit = 0xc2c70258)
Stack: (0xc2c71e88 to 0xc2c72000)
1e80: c2c71ebc c2c71e98 c008d888 c019df54 00000000 c3e99cc0
1ea0: c3e7c594 c008d73c c0474d20 c3e7d5b0 c2c71ee4 c2c71ec0 c0089e48 c008d74c
1ec0: c3e99cc0 c2c71f04 00000003 ffffff9c c002c044 c04ad000 c2c71efc c2c71ee8
1ee0: c0089f64 c0089d58 00000000 00000002 c2c71f68 c2c71f00 c0089fb8 c0089f40
1f00: c2c71f04 c3e7d5b0 c0474d20 00000000 00000000 c2c8d000 00000101 00000001
1f20: 00000000 c2c70000 c046dd48 c046dd40 ffffffe8 c04ad000 c2c71f68 c2c71f48
1f40: c008a16c c009fc70 00000003 00000000 c3e99cc0 00000002 beaf6ee0 c2c71f94
1f60: c2c71f6c c008a2f4 c0089f88 00008520 beaf6ed4 0000860c 00008670 00000005
1f80: c002c044 4013365c c2c71fa4 c2c71f98 c008a3a8 c008a2b0 00000000 c2c71fa8
1fa0: c002bea0 c008a394 beaf6ed4 0000860c 00008720 00000002 beaf6ee0 00000001
1fc0: beaf6ed4 0000860c 00008670 00000002 00008520 00000000 4013365c beaf6ea8
1fe0: 00000000 beaf6e84 0000266c 400c98e0 60000010 00008720 00000000 00000000
Backtrace:
[<c019df44>] (first_drv_open+0x0/0x3c) from [<c008d888>] (chrdev_open+0x14c/0x164)
[<c008d73c>] (chrdev_open+0x0/0x164) from [<c0089e48>] (__dentry_open+0x100/0x1e8)
r8:c3e7d5b0 r7:c0474d20 r6:c008d73c r5:c3e7c594 r4:c3e99cc0
[<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:beaf6ee0 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
找到PC值
PC is at first_drv_open+0x18/0x3c
pc : [<c019df5c>] lr : [<c008d888>] psr: a0000013
sp : c2c71e88 ip : c2c71e98 fp : c2c71e94
r10: 00000000 r9 : c2c70000 r8 : c3e99cc0
r7 : 00000000 r6 : 00000000 r5 : c3e7c594 r4 : c04b8180
r3 : c019df44 r2 : 56000050 r1 : c03c3d5c r0 : 00000000
Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user
判断是否属于模块
查看编译源码下的 System.map文件,可以看到内核的空间在 c0004000---c03cebf4
c0004000 A swapper_pg_dir
..
...
c03cebf4 B _end
也就是说 PC地址是 pc : [<bf000018>] 不属于内核自身,属于模块
查看符号表
查看下模块的符号表地址 cat /proc/kallsyms > a.txt ,这个文件包含了这个文件包含内核自身的函数,可以看到这个文件和 System.map 有很多同样的地方.然后再这个文件里面寻找一个地址比 pc : [<bf000018>]小一点的地址,这个应该写个脚本去搜索,x是本文中最大的一个 < pc 的值逻辑应该是这样的
x是本文中最大的一个 < pc 的值
tmp=0
while (文件结束)
if lin[xxx] < pc
if lin[xxx] >tmp
tmp=lin[xxx]
这里先直接搜索一下好了,可以看到是
---- 最接近了
Line 19861: bf000000 t first_drv_open [first_drv]
Line 19862: bf000000 t $a [first_drv]
----
Line 19854: bf0000c0 t first_drv_init [first_drv]
Line 19855: bf00016c t first_drv_exit [first_drv]
Line 19857: bf000960 b $d [first_drv]
Line 19858: bf000770 d first_drv_fops [first_drv]
Line 19859: bf000770 d $d [first_drv]
Line 19860: bf00003c t first_drv_write [first_drv]
Line 19861: bf000000 t first_drv_open [first_drv]
Line 19862: bf000000 t $a [first_drv]
Line 19863: bf000038 t $d [first_drv]
Line 19864: bf00003c t $a [first_drv]
Line 19865: bf0000bc t $d [first_drv]
Line 19866: bf0000c0 t $a [first_drv]
Line 19867: bf000140 t $d [first_drv]
Line 19868: bf00096c b firstdrv_class [first_drv]
Line 19869: bf000970 b firstdrv_class_dev [first_drv]
Line 19870: bf00016c t $a [first_drv]
Line 19871: bf0001b0 t $d [first_drv]
Line 19874: bf0008cc d $d [first_drv]
Line 19879: bf000968 b major [first_drv]
Line 19880: bf000964 b gpfcon [first_drv]
Line 19883: bf0007e0 d __this_module [first_drv]
Line 19884: bf0000c0 t init_module [first_drv]
Line 19886: bf00016c t cleanup_module [first_drv]
Line 19888: bf000960 b gpfdat [first_drv]
找到模块
我们找到了Line 19861: bf000000 t first_drv_open [first_drv] 与pc地址最接近也就是这个pc属于这个 first_drv模块 的 first_drv_open函数.
注意:
tips 在这个例子中
刚开始描述的是 - 0x18 偏移地址 PC is at first_drv_open+0x18/0x3c [first_drv]
我们找到了 first_drv_open函数 的地址是 bf000000
也就是和我们所找到后的实际地址 bf000000+0x18 也就是寄存器描述的 pc : [<bf000018>]是一致的
也就是说我们能够反算出函数的偏移地址,我们是需要这个偏移地址的,去查找出错的位置
1. pc地址=bf000018
2. 最接近的函数地址是bf000000
3.偏移地址是0x18
first_drv.dis文件里 insmod后
00000000 <first_drv_open>: bf000000 t first_drv_open [first_drv]
00000018 pc = bf000018
反汇编模块
反汇编找到的模块函数吧 这里是用-S 选项的反汇编能看到一些C信息,但是会没有-D 产生的debug信息,文件大小也是-D的大
arm-linux-objdump -D first_drv.ko >first_drv.dis
查看反汇编,分析汇编文件
#######先找到这个函数
00000000 <first_drv_open>:
0: e1a0c00d mov ip, sp
4: e92dd800 stmdb sp!, {fp, ip, lr, pc}
8: e24cb004 sub fp, ip, #4 ; 0x4
c: e59f1024 ldr r1, [pc, #36] ; 38 <__mod_vermagic5>
10: e3a00000 mov r0, #0 ; 0x0
14: e5912000 ldr r2, [r1]
18: e5923000 ldr r3, [r2] ####在这里出错了
### 来查看下原来的寄存器的值 ###########################################
### LR is at chrdev_open+0x14c/0x164
### #LR寄存器的地址
###
### pc : [<bf000018>] lr : [<c008d888>] psr: a0000013
### sp : c2cebe88 ip : c2cebe98 fp : c2cebe94
### r10: 00000000 r9 : c2cea000 r8 : c3e9b700
### r7 : 00000000 r6 : 00000000 r5 : c04b2e5c r4 : c06dc300
### r3 : bf000000 r2 : 56000050 r1 : bf000964 r0 : 00000000
### Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user
### Control: c000717f Table: 32c78000 DAC: 00000015
#########################################################################
也就是 将 56000050地址的值取出来放到 r3 bf000000中
也就是我们的C语言的 读修改写的读步骤
#// *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)))
我们在驱动初始化的时候已经设置了指针了
#//gpfcon = (volatile unsigned long *)0x56000050; //(volatile unsigned long *)ioremap(0x56000050, 16);
# 下面这个bic 就是清那些位的操作
1c: e3c33c3f bic r3, r3, #16128 ; 0x3f00
# 然后重新赋值
20: e5823000 str r3, [r2]
24: e5912000 ldr r2, [r1]
28: e5923000 ldr r3, [r2]
2c: e3833c15 orr r3, r3, #5376 ; 0x1500
30: e5823000 str r3, [r2]
34: e89da800 ldmia sp, {fp, sp, pc}
38: 00000000 andeq r0, r0, r0
内核例子分析
先将错误代码放到内核中
cp first_drv.c ~/stu/kernel/linux-2.6.22.6/drivers/char/
cd ~/stu/kernel/linux-2.6.22.6/
vi drivers/char/Makefile
输入 obj-y += first_drv.o
make uImage
找到PC值
错误信息如下
##############################################################################
Unable to handle kernel paging request at virtual address 56000050
pgd = c2c8c000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in:
CPU: 0 Not tainted (2.6.22.6 #3)
PC is at first_drv_open+0x18/0x3c
LR is at chrdev_open+0x14c/0x164
pc : [<c019df5c>] lr : [<c008d888>] psr: a0000013
sp : c2c71e88 ip : c2c71e98 fp : c2c71e94
r10: 00000000 r9 : c2c70000 r8 : c3e99cc0
r7 : 00000000 r6 : 00000000 r5 : c3e7c594 r4 : c04b8180
r3 : c019df44 r2 : 56000050 r1 : c03c3d5c r0 : 00000000
Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user
################################################################################
判断是否属于模块
查看 System.map,可以看到内核地址是 如下,可以确定是内核地址了
c0004000 A swapper_pg_dir
...
c03cec74 B _end
反汇编内核
这个反汇编文件比较大,345M... ,window下vscode还是能扛住的,notepad挂了搜索的时候,后来试了不要点太快能扛住搜索 c019df5c 可以看到函数了
arm-linux-objdump -D vmlinux >vmlinux.dis
分析汇编
c019df44 <first_drv_open>:
c019df44: e1a0c00d mov ip, sp
c019df48: e92dd800 stmdb sp!, {fp, ip, lr, pc}
c019df4c: e24cb004 sub fp, ip, #4 ; 0x4
c019df50: e59f1024 ldr r1, [pc, #36] ; c019df7c <.text+0x172f7c>
c019df54: e3a00000 mov r0, #0 ; 0x0
c019df58: e5912000 ldr r2, [r1]
c019df5c: e5923000 ldr r3, [r2] ######在这里出错了
#################################################################
r10: 00000000 r9 : c2c70000 r8 : c3e99cc0
r7 : 00000000 r6 : 00000000 r5 : c3e7c594 r4 : c04b8180
r3 : c019df44 r2 : 56000050 r1 : c03c3d5c r0 : 00000000
########################################################
c019df60: e3c33c3f bic r3, r3, #16128 ; 0x3f00
c019df64: e5823000 str r3, [r2]
c019df68: e5912000 ldr r2, [r1]
c019df6c: e5923000 ldr r3, [r2]
c019df70: e3833c15 orr r3, r3, #5376 ; 0x1500
c019df74: e5823000 str r3, [r2]
c019df78: e89da800 ldmia sp, {fp, sp, pc}
c019df7c: c03c3d5c eorgts r3, ip, ip, asr sp
驱动调试(三)oops确定函数PC的更多相关文章
- 应用调试(三)oops
目录 应用调试(三)oops 引入 配置内核打开用户oops CONFIG_DEBUG_USER user_debug 设置启动参数测试 打印用户堆栈 分析栈 main的调用 title: 应用调试( ...
- 驱动调试(四)oops确定调用树
目录 驱动调试(四)oops确定调用树 内核开启调用树 栈指针分析 原理 寄存器别名 基础解释 例子分析 找到PC地址的位置 栈分析 附录:原文的excel title: 驱动调试(四)oops确定调 ...
- 驱动调试-根据oops定位错误代码行
1.当驱动有误时,比如,访问的内存地址是非法的,便会打印一大串的oops出来 1.1以LED驱动为例 将open()函数里的ioremap()屏蔽掉,直接使用物理地址的GPIOF,如下图所示: 1.2 ...
- 36.Linux驱动调试-根据oops定位错误代码行
1.当驱动有误时,比如,访问的内存地址是非法的,便会打印一大串的oops出来 1.1以LED驱动为例 将open()函数里的ioremap()屏蔽掉,直接使用物理地址的GPIOF,如下图所示: 1.2 ...
- Linux驱动调试-根据oops的栈信息,确定函数调用过程
上章链接入口: http://www.cnblogs.com/lifexy/p/8006748.html 在上章里,我们分析了oops的PC值在哪个函数出错的,那如何通过栈信息来查看出错函数的整个调用 ...
- 37.Linux驱动调试-根据oops的栈信息,确定函数调用过程
上章链接入口: http://www.cnblogs.com/lifexy/p/8006748.html 在上章里,我们分析了oops的PC值在哪个函数出错的 本章便通过栈信息来分析函数调用过程 1. ...
- linux驱动调试--段错误之oops信息分析
linux驱动调试--段错误之oops信息分析 http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29401328&id= ...
- 【转】android 电容屏(三):驱动调试之驱动程序分析篇
关键词:android 电容屏 tp 工作队列 中断 坐点计算 电容屏主要参数平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310( ...
- 26、驱动调试之根据oops信息和堆栈确定出错的代码
a.驱动作为模块:1. 根据pc值确定该指令属于内核还是外加的模块pc=0xbf000018 它属于什么的地址?是内核还是通过insmod加载的驱动程序?先判断是否属于内核的地址: 看System.m ...
随机推荐
- 一文把samba相关的都说清楚
1.前言 samba源码都一样,配置也也一样,各个不同linux版本,唯一不同的是对服务的启动方式不同.下面以ubuntu14.4为例,说明. 2. 安装samba samba的安装,可以源码安装,大 ...
- (转)聊聊Greenplum的那些事
开卷有益——作者的话 有时候真的感叹人生岁月匆匆,特别是当一个IT人沉浸于某个技术领域十来年后,蓦然回首,总有说不出的万千感慨. 笔者有幸从04年就开始从事大规模数据计算的相关工作,08年作为Gree ...
- Linux AIDE(文件完整性检测)
一.AIDE的概念 AIDE:Advanced Intrusion Detection Environment,是一款入侵检测工具,主要用途是检查文档的完整性.AIDE在本地构造了一个基准的数据库,一 ...
- 关于opencv模板匹配功能的项目测试记录
模板匹配功能介绍的很好的一篇博客:https://www.cnblogs.com/XJT2018/p/9934139.html 就如上述博客所言:“若原图像中的匹配目标发生旋转或大小变化,该算法无效. ...
- git add 添加多个文件
在使用git add提交多个文件的方式: git add . 后面加一个".",匹配所有的文件 总结下,提交多个文件时,git add后可以有如下参数以及参数的解释: git ...
- web渗透 学习计划(转载)
作者:向生李链接:https://www.zhihu.com/question/21914899/answer/39344435来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...
- Django REST framework基础:认证、权限、限制
认证.权限和限制 身份验证是将传入请求与一组标识凭据(例如请求来自的用户或其签名的令牌)相关联的机制.然后 权限 和 限制 组件决定是否拒绝这个请求. 简单来说就是: 认证确定了你是谁 权限确定你能不 ...
- addq
<template> <el-row id="AddRoom"> <el-col :xs="0" :sm="2" ...
- 【转】Android 增,删,改,查 通讯录中的联系人
一.权限 操作通讯录必须在AndroidManifest.xml中先添加2个权限, <uses-permission android:name="android.permission. ...
- 从PyMongo看MongoDB Read Preference
在CAP理论与MongoDB一致性.可用性的一些思考一文中提到,MongoDB提供了一些选项,如Read Preference.Read Concern.Write Concern,对MongoD ...