linux驱动调试--段错误之oops信息分析
来源:互联网 发布:windows xp 反复重启 编辑:程序博客网 时间:2024/05/16 05:28
发生段错误原因就是访问了不该访问的地址,例如访问了不存在的内存地址、访问了系统保护的内存地址、访问了只读的内存地址等。
一、根据Oops信息来分析一下段错误
在first_drv驱动中取消地址映射
驱动程序中不能够直接使用物理地址,需要通过ioremap进行映射
./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: (回溯) sys_open->do_sys_open->do_filp_open->
[<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
#
配置内核以用于输出回溯信息,查看配置文件.config,搜索frame
在make menuconfig后搜索frame_pointer,prompt信息表示编译内核时用帧指针,编译内核时会加上一些选项填入这个帧指针,就可以通过帧指针当发生错误时就可以打印回溯过程,有时候为了减少内核的映像文件,这个回溯选项有可能没有配置上去,就没有回溯信息,就需要自己分析栈,把这些调用关系弄出来
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)
二. 根据内核打印的段错误信息分析
a. 作为模块:1. 根据pc值确定该指令属于内核还是外加的模块
pc=0xbf000018 它属于什么的地址?是内核还是通过insmod加载的驱动程序?
先判断是否属于内核的地址: 看System.map确定内核的函数的地址范围:c0004000~c03265a4
如果不属于System.map里的范围,则它属于insmod加载的驱动程序
2. 假设它是加载的驱动程序引入的错误,怎么确定是哪一个驱动程序?
先看看加载的驱动程序的函数的地址范围
cat /proc/kallsyms (内核函数、加载的函数的地址)(k代表内核,all代表所有,syms代表符号(函数))
把输出信息放到一个文件里面去,好分析
里面的t代表静态函数,T代表全局函数
比如找到了:
bf000000 t first_drv_open [first_drv] //驱动first_drv里面函数first_drv_open的地址是bf000000,偏移0x18就是我们的PC值
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
在反汇编文件里,出错地方为蓝色标志
去r2所指的地方取值,把这个值传给r3,
这个r2值看之前例子的打印信息
r2 : 56000050 ,这个地址出错是因为非法的,不能访问的,所以要修改这个地址
#############################################################
##############################################################
00000000 <first_drv_open>:
0: e1a0c00d mov ip, sp //把栈的地址存到IP
4: e92dd800 stmdb sp!, {fp, ip, lr, pc}//把寄存器的值写到栈中
8: e24cb004 sub fp, ip, #4; 0x4//fp=ip-4,在配置内核时,有1个帧缓冲frame_pointer,就是fp,回溯时根据fp寄存器找到他的栈,从他的栈里面找到lr值,lr是返回值,把返回值打印出来就会知道调用者是谁
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] //r2指向的是56000050 ,对应的正是gpfcon的值,下面函数是将这个值读出来,与其他做与运算,然后重新赋给它,所以有1个读的过程 //先把r2的值读出来。
1c: e3c33c3f bic r3, r3, #16128; 0x3f00 //清位操作r3&=~(0x3f00)
20: e5823000 str r3, [r2] //再把r3的值存回到r2中
###########################################################
#########################################################
b. 编入内核
把有问题的驱动程序拷贝到内核的driver/char目录里面去并修改Makefile
加上1句
然后重新编译内核make uImage,重启开发板,使用有问题的内核,先下载
再用bootm 32000000启动
启动驱动
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
- linux驱动调试--段错误之oops信息分析
- linux驱动调试--段错误之oops信息分析
- linux驱动调试--段错误之oops信息分析
- linux驱动调试--段错误之oops信息分析
- linux驱动调试--段错误之栈信息分析
- linux驱动调试--段错误之栈信息分析
- linux驱动调试之段错误分析-根据栈信息分析函数调用过程
- linux驱动调试--oops信息
- linux驱动调试之Oops信息---针对arm处理器
- 驱动调试之段错误分析_根据栈信息确定函数调用过程
- Linux内核调试之Oops信息
- Linux内核调试之Oops信息
- linux驱动调试之段错误分析_根据pc值确定出错的代码位置
- linux kernel Oops调试信息分析 异常调试
- Linux oops信息的分析
- Linux oops信息的分析
- Linux oops信息的分析 .
- Linux oops信息的分析
- 重读Paul Graham
- 中超赛程分析(5)--德比战
- 树莓派第一次使用(笔记本+网线)
- 常用的正则表达式
- web开发,读取txt文件(程序或本地硬盘)
- linux驱动调试--段错误之oops信息分析
- SRM579 Div1Medium TravellingPurchasingMan
- 463. Island Perimeter(C语言)
- GDT、LDT详解
- 顺序表
- Opencv中convertTo函数
- Android中的Service 与 Thread 的区别
- UI适配兼容性方案
- Spring MyBatis 学习笔记