• Linux驱动调试之段错误分析_根据栈信息确定函数调用过程


    目录

    1 函数调用过程

     2 根据栈信息分析函数调用过程

    2.1 根据PC确定出错位置

    2.2 确定它属于哪个函数


    1 函数调用过程

    当我们的驱动程序报错时,如果我们编译内核的时候配置了CONFIG_FRAME_POINTER,那么报错后回答引出backTrace回溯信息,能够看到函数的调用关系,但是如果我们的内核没有配置CONFIG_FRAME_POINTER,那么报错时不会打印函数调用关系,这时候我们可以根据栈信息自己分析函数调用过程。

     2 根据栈信息分析函数调用过程

    # ./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 : []    lr : []    psr: a0000013

    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)

    2.1 根据PC确定出错位置

    pc= bf000018,那么根据内核的System.map可以看到这个地址不属于内核,那么这个pc属于 insmod的模块。

    然后看加载的驱动程序的函数的地址范围,cat /proc/kallsyms > kallsyms.txt  (内核函数、加载的函数的地址)kallsyms:k指内核,all所有的,syms符号。
    从这些信息里找到一个相近的地址, 这个地址<=0xbf000018, 比如找到了:
    bf000000 t first_drv_open    [first_drv]

     那么这个bf000018应该是属于first_drv这个模块。那么bf000018属于哪个函数呢。

    2.2 确定它属于哪个函数

    反汇编first_drv.ko,得到

    1. first_drv.ko: file format elf32-littlearm
    2. Disassembly of section .text:
    3. 00000000 :
    4. 0: e1a0c00d mov ip, sp
    5. 4: e92dd800 stmdb sp!, {fp, ip, lr, pc}
    6. 8: e24cb004 sub fp, ip, #4 ; 0x4
    7. c: e59f1024 ldr r1, [pc, #36] ; 38 <__mod_vermagic5>
    8. 10: e3a00000 mov r0, #0 ; 0x0
    9. 14: e5912000 ldr r2, [r1]
    10. 18: e5923000 ldr r3, [r2] // 在这里出错 r2=56000050
    11. 1c: e3c33c3f bic r3, r3, #16128 ; 0x3f00
    12. 20: e5823000 str r3, [r2]
    13. 24: e5912000 ldr r2, [r1]
    14. 28: e5923000 ldr r3, [r2]
    15. 2c: e3833c15 orr r3, r3, #5376 ; 0x1500
    16. 30: e5823000 str r3, [r2]
    17. 34: e89da800 ldmia sp, {fp, sp, pc}
    18. 38: 00000000 andeq r0, r0, r0
    19. 0000003c :
    20. 3c: e1a0c00d mov ip, sp

    出错的时候就把栈信息打印出来,我们先从反汇编文件看一下栈信息,从

    4:    e92dd800     stmdb    sp!, {fp, ip, lr, pc}

    可以看到这是往栈sp里面存了四个值(注意stmdb意思是地址先减然后完成操作),那么fp ip lr pc这四个保存顺序是怎么样的,我们之前知道pc是r15寄存器的别名,lr是r14寄出器的别名,ip是r12寄存器的别名,fp是r11寄存器的别名,那么先存哪一个呢,先存pc,因为高地址保存高标号,所以先存r15,然后sp地址减4字节之后再存r14/lr,那么从下面打印的栈信息可以看到c008c888就是返回地址,这个地址就是first_drv_open调用者的地址,first_drv_open执行完之后就返回这里。那么c008c888是哪一个函数呢,这个是属于内核的函数,这时候要去反汇编内核。然后在反汇编文件中搜索这个地址,发现属于chrdev_open函数,然后看到chrdev_open的栈,在这里面首先存了9个值,然后sp又减了4,所以一共10个值,而第8个是lr=c0088e48,这个就是调用者的地址,

     然后同样看一下c0088e48 在哪里,发现属于__dentry_open函数,所以调用者是__dentry_open

     然后这个__dentry_open函数栈占用了10*4个字节,然后lr寄存器是第9个,lr=c0088f64 ,这个值属于

     然后这个nameidata_to_filp的栈占据6个位置,然后找到lr=c0088fb8 ,这就是调用者的地址,继续找,发现这个c0088fb8 属于

     然后do_filp_open的栈占据(6+84/4)=27个位置,继续找到lr=c00892f4 ,这个地址属于

     然后这个do_sys_open又占据11个位置,从来继续找到 lr= c00893a8,继续找到调用者是

     然后sys_open的栈占4个位置,继续找到 lr = c002aea0 ,

    1. Stack: (0xc3e69e88 to 0xc3e6a000)
    2. 9e80: c3e69ebc c3e69e98 c008c888 bf000010 00000000 c0490620
    3. first_drv_open'sp lr chrdev_open'sp
    4. 9ea0: c3e320a0 c008c73c c0465e20 c3e36cb4 c3e69ee4 c3e69ec0 c0088e48 c008c74c
    5. lr
    6. 9ec0: c0490620 c3e69f04 00000003 ffffff9c c002b044 c06e0000 c3e69efc c3e69ee8
    7. __dentry_open'sp
    8. 9ee0: c0088f64 c0088d58 00000000 00000002 c3e69f68 c3e69f00 c0088fb8 c0088f40
    9. lr nameidata_to_filp'sp lr
    10. 9f00: c3e69f04 c3e36cb4 c0465e20 00000000 00000000 c3e79000 00000101 00000001
    11. do_filp_open'sp
    12. 9f20: 00000000 c3e68000 c04c1468 c04c1460 ffffffe8 c06e0000 c3e69f68 c3e69f48
    13. 9f40: c008916c c009ec70 00000003 00000000 c0490620 00000002 be94eee0 c3e69f94
    14. 9f60: c3e69f6c c00892f4 c0088f88 00008520 be94eed4 0000860c 00008670 00000005
    15. lr do_sys_open'sp
    16. 9f80: c002b044 4013365c c3e69fa4 c3e69f98 c00893a8 c00892b0 00000000 c3e69fa8
    17. lr sys_open'sp
    18. 9fa0: c002aea0 c0089394 be94eed4 0000860c 00008720 00000002 be94eee0 00000001
    19. lr ret_fast_syscall'sp
    20. 9fc0: be94eed4 0000860c 00008670 00000002 00008520 00000000 4013365c be94eea8
    21. 9fe0: 00000000 be94ee84 0000266c 400c98e0 60000010 00008720 00000000 00000000
  • 相关阅读:
    一篇文章学会C#的正则表达式
    CSDN课程推荐:《【专题】SecureBoot精讲》系列课程上线
    使用JMeter进行接口压力测试
    FOC学习笔记-坐标变换以及仿真验证
    C语言字符串函数简单介绍
    Postman+Newman生成测试报告
    Win10电脑重装系统更新关闭了还自动打开怎么解决?
    iOS实时监控与报警器
    第10章 PCA降维技术
    【Odoo】Odoo16-性能优化提升
  • 原文地址:https://blog.csdn.net/u013171226/article/details/126279550