zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    68
    56
    Django manager 命令笔记
    Django 执行 manage 命令方式
    Django 连接 Mysql (8.0.16) 失败
    Python django 安装 mysqlclient 失败
    H.264 SODB RBSP EBSP的区别
    FFmpeg—— Bitstream Filters 作用
    MySQL 远程连接问题 (Windows Server)
    MySQL 笔记
  • 原文地址:https://www.cnblogs.com/liusiluandzhangkun/p/8678355.html
Copyright © 2011-2022 走看看