驱动从PC指针分析段错误
我们将驱动程序故意改一句话 我们都知道寄存器要ioremap之后才能用。在驱动程序里面不能使用物理地址 或者对那种没有初始化指针的时候你操作也会出错。我现在故意引入这个错误 我们修改以前的LED代码用来说明
我们将以前的驱动程序改成下面这种
然后加载并运行测试程序
我们现在就根据这一大堆错我来找问题
[ 181.105729] Unable to handle kernel paging request at virtual address e0200280
第一句话 无法处理对内核的也请求在虚拟地址e0200280,驱动程序是把所有的地址当中虚拟地址访问的,它这里无法识别或者处理你这个虚拟地址,因为根本 没有这个虚拟地址,因为你还没有建立映射,
[ 181.105801] pgd = cf4fc000
[ 181.105827] [e0200280] *pgd=00000000
[ 181.105865] Internal error: Oops: 805 [#1] PREEMPT
Internal error: Oops: 805 [#1] PREEMPT 内部错误OOPS
[ 181.105907] last sysfs file: /sys/devices/virtual/led_class/led_driver/dev
[ 181.108524] Modules linked in: led humidity ov9650 timer_irq second buzzer_drv adc_drv snd_soc_gec210_wm8960 snd_soc_wm8960
Modules linked in: led 表示是在led模块发生了这个错误
[ 181.119619] CPU: 0 Not tainted (2.6.35.7-GEC210 #1)
[ 181.124824] PC is at led_open+0x30/0x50 [led]
PC就是发送错误的指令的地址,我执行了某条机器码发送了错误,PC就是这条指令码的地址
[ 181.129159] LR is at chrdev_open+0x18c/0x1b8
LR是LR寄存器而已
[ 181.133398] pc : [<bf0380fc>] lr : [<c0120154>] psr: a0000013
[ 181.133403] sp : cf4f7dc8 ip : 00001111 fp : cf4f7ddc
[ 181.144834] r10: cf4ba700 r9 : c011ffc8 r8 : 00000001
[ 181.150034] r7 : 00000000 r6 : bf0383c4 r5 : cf4c6d80 r4 : bf0383b8
[ 181.156533] r3 : e0200280 r2 : 00000000 r1 : 00000004 r0 : e0200284
执行这条导致错误的执行时,各个寄存器的值
[ 181.163034] Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
[ 181.170139] Control: 10c5387d Table: 3f4fc019 DAC: 00000015
[ 1317.590588] Process led_test (pid: 127, stack limit = 0xcf4f62f0)
Process表示发送错误时当前进程的名称是led_test
[ 1317.596654] Stack: (0xcf4f7dc8 to 0xcf4f8000)
[ 1317.600991] 7dc0: bf0380cc cf4ba700 cf4f7e0c cf4f7de0 c0120154 bf0380d8
[ 1317.609137] 7de0: c05726f4 c02349d8 cf4f7e0c cff49980 cf9be090 c0577ee0 00000000 cf4c6840
[ 1317.617283] 7e00: cf4f7e3c cf4f7e10 c011b230 c011ffd4 00000000 cf4c6840 00000000 00000666
[ 1317.625429] 7e20: 00000000 0000002e beea5c4c cf4c6840 cf4f7e54 cf4f7e40 c011c114 c011b0c0
[ 1317.633574] 7e40: 00002000 cf4f7ec8 cf4f7e8c cf4f7e58 c0128388 c011c0f0 c08603a0 00000000
[ 1317.641720] 7e60: cf4f7e8c cf4f7ec8 00000666 cf4f7f28 00000000 cf4f6000 00000000 0000002e
[ 1317.649866] 7e80: cf4f7f5c cf4f7e90 c0128644 c0127f30 beea5c4c c05726ac 00000071 cf4f7fb0
[ 1317.658012] 7ea0: cfec9180 400ef420 ffffff9c cf46e000 cf4f7edc beea5c4c cf4f7efc 00000000
[ 1317.666158] 7ec0: 00000667 c00d2c00 cff49980 cf9be090 f764bd4e 0000000a cf46e005 cff49480
[ 1317.674303] 7ee0: cf804240 00000301 00000000 00000000 cf4f7fac 00000000 cf4c6cc0 cf4c6ce4
[ 1317.682449] 7f00: 00000000 00000666 00000000 cf4c6cc8 cf4f7f2c 00000667 beea5c4c cf4c6840
[ 1317.690595] 7f20: cf4f7f5c cf4f7f30 cff49980 cf9be090 beea5c4c 00000000 00000003 00000666
[ 1317.698741] 7f40: beea5c4c cf46e000 cf4f6000 ffffff9c cf4f7f94 cf4f7f60 c011c18c c01284e0
[ 1317.706886] 7f60: 00000000 cf4f7f70 c01123e0 00000000 00000000 00000000 00000005 c008f168
[ 1317.715032] 7f80: cf4f6000 00000000 cf4f7fa4 cf4f7f98 c011c26c c011c134 00000000 cf4f7fa8
[ 1317.723178] 7fa0: c008efc0 c011c250 00000000 00000000 0000860c 00000666 beea5c4c 000084ac
[ 1317.731324] 7fc0: 00000000 00000000 00000000 00000005 00000000 00000000 40025000 beea5af4
[ 1317.739470] 7fe0: 00000000 beea5ae0 000084cc 400ef43c 60000010 0000860c 30a97021 30a97421
栈,这个非常有用,我们以后可以根据这个推出函数的调用关系
[ 1317.747610] Backtrace:
[ 1317.750048] [<bf0380cc>] (led_open+0x0/0x50 [led]) from [<c0120154>] (chrdev_open+0x18c/0x1b8)
[ 1317.758617] r4:cf4ba700 r3:bf0380cc
[ 1317.762177] [<c011ffc8>] (chrdev_open+0x0/0x1b8) from [<c011b230>] (__dentry_open.clone.13+0x17c/0x294)
[ 1317.771528] r8:cf4c6840 r7:00000000 r6:c0577ee0 r5:cf9be090 r4:cff49980
[ 1317.778207] [<c011b0b4>] (__dentry_open.clone.13+0x0/0x294) from [<c011c114>] (nameidata_to_filp+0x30/0x44)
[ 1317.787916] [<c011c0e4>] (nameidata_to_filp+0x0/0x44) from [<c0128388>] (do_last.clone.29+0x464/0x5b0)
[ 1317.797179] r4:cf4f7ec8 r3:00002000
[ 1317.800738] [<c0127f24>] (do_last.clone.29+0x0/0x5b0) from [<c0128644>] (do_filp_open+0x170/0x4d4)
[ 1317.809664] [<c01284d4>] (do_filp_open+0x0/0x4d4) from [<c011c18c>] (do_sys_open+0x64/0x11c)
[ 1317.818069] [<c011c128>] (do_sys_open+0x0/0x11c) from [<c011c26c>] (sys_open+0x28/0x2c)
[ 1317.826045] [<c011c244>] (sys_open+0x0/0x2c) from [<c008efc0>] (ret_fast_syscall+0x0/0x30)
[ 1317.834274] Code: e301c111 e3a01004 e3a02000 e59f0018 (e583c000)
[ 1317.843379] ---[ end trace 8b8c2943c58e57b7 ]---
Segmentation fault
Backtarce表示回溯。回溯是什么意思呢,你可以倒过来看,sys_open函数调用do_sys_open函数调用do_filp_ope,这么一层一层调用
你想要这个回溯信息的话,必须要把内核中的一个开关打开
在内核里面叫做 FRAME_POINT
这里就会把这个回溯打印上去,有时候为了减少内核的体积,这里就没有打印上去,
我们先来看PC值,
[ 1316.984165] PC is at led_open+0x30/0x50 [led]
这里是说PC在led_open+0x30的地方发送了错误,0x30表示该指令的偏移 0x50表示该函数指令的总大小
大多时候PC只会给出一个地址,不会指示他在那个地址里面
[ 1316.992739] pc : [<bf0380fc>] lr : [<c0120154>] psr: a0000013
我们根据上面这个可以知道PC=bf0380fc。它属于什么地址。是内核还是通过insmod加载的驱动程序
先判断是否属于内核的地址,去看Sysmap确定内核的函数的地址范围,如果sysmap中包含这个bf0380fc,表示当前发生错误的指令是属于内核函数
否则,如果不属于Sysmap里的函数,则它属于insmod驱动程序,
假设它是加载的驱动程序引入的错误,怎么确定是哪一个驱动程序
[ 1316.978094] CPU: 0 Tainted: G D (2.6.35.7-GEC210 #1)
[ 1316.984165] PC is at led_open+0x30/0x50 [led]
[ 1316.988500] LR is at chrdev_open+0x18c/0x1b8
如果有这些信息就可以看出,如果没有的话,只得到一个PC值,怎么办
我们下面来说根据PC值去找到是那个驱动程序
进入内核的目录
我们来看看Sysmap
内核是从C0004006开始,到最后一行44020 c087f648 A _end C087f648结束
你前面的PC=bf0380f不属于这个范围,所以肯定是外面加进来的驱动程序,
既然知道是外面加进来的驱动程序发生了错误,怎么知道是哪一个呢,先看看加载的驱动程序的函数的地址范围,
怎么看,使用下面这条指令
K代表内核,all所有的,symsfuhao 内核的函数,加载函数的地址,所有的东西都有,变量也有
我们把这个放到一个文件里面去 cat /proc/kallsyms > 1.txt
从这些信息里找到一个相近的地址 这个地址<=bf0380fc这个值
这里有个接近的
这里led_open表示驱动程序里面有个open函数,led表示驱动程序,这个led_open是属于这个驱动程序的,这个led_open函数的地址是0xbf0380cc
这个地址就属于PC is at led_open+0x30的地方 算出来就是0xbf0380cc+30刚好等于我们的0xbf0380fc
找到了这个驱动,然后反汇编它
在PC上使用反汇编命令arm-none-linux-gnueabi-objdump -D led.ko > led.dis
在反汇编文件里面找到led_open这个东西
然后我们PC就等于000000CC+30= 000000fc
000000cc <led_open>:cc: e1a0c00d mov ip, sp
d0: e92dd818 push {r3, r4, fp, ip, lr, pc}
d4 e24cb004 sub fp, ip, #4
d8: e59f4030 ldr r4, [pc, #48] ; 110 <led_open+0x44>
dc: e59f3030 ldr r3, [pc, #48] ; 114 <led_open+0x48>
e0: e5843048 str r3, [r4, #72] ; 0x48
e4: f57ff04f dsb sy
e8: e5943048 ldr r3, [r4, #72] ; 0x48
ec: e301c111 movw ip, #4369 ; 0x1111
f0: e3a01004 mov r1, #4
f4: e3a02000 mov r2, #0
f8: e59f0018 ldr r0, [pc, #24] ; 118 <led_open+0x4c>fc: e583c000 str ip, [r3]
就是这一句出错
这计划的意思是将IP的值压入【R3】寄存器 对应C语言中的写的过程
R3寄存器根据我们内核的输出信息可以看到
r3 : e0200280,所以是这里出错,这个地址是非法的
这是外加的模块
如果你是直接编译进内核的话
你需要去System.map的地址有没有包括你的
然后反汇编内核
arm-linux-objdump -D vmlinux > vmlinux.dis
反汇编之后查找