zoukankan      html  css  js  c++  java
  • 驱动调试(三)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` 中

    https://www.imooc.com/article/26837?block_id=tuijian_wz

    我们的板子可以使用dmesg来重新查看

    流程简述

    mark

    代码仓库

    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
    
  • 相关阅读:
    nginx+tomcat配置websocket反向代理
    jmter 参数化
    postman 自动化
    python yaml文件读写
    python redis 操作
    pycharm 连接github操作
    校验身份证号
    python faker 造数据
    pandas处理日期相关的操作
    python的time几种用法strptime、strftime、localtime、mktime
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10269156.html
Copyright © 2011-2022 走看看