第21章 Linux设备驱动的调试之Oops

21.6 Oops

    当内核出现类似用户空间的Segmentation Fault(段错误)时(例如内核访问一个并不存在的虚拟地址),Oops会被打印到控制台和写入内核log缓冲区

    在globalmem.c的globalmem_read()函数中加上下面一行代码:

        } else {
                *ppos += count;
                ret = count;
                *(unsigned int *)0 = 1; /* 0地址赋值为1,a kernel panic */ 
                printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);

        }

    假设这个字符设备对应的设备节点是/dev/globalmem,通过cat  /dev/globalmem命令读设备文件,将得到

如下Oops信息:

# cat /dev/globalmem
Unable to handle kernel NULL pointer dereference (解引用)at virtual address 00000000
pgd = 9ec08000
[00000000] *pgd=7f733831, *pte=00000000, *ppte=00000000
Internal error: Oops: 817 [#1] SMP ARM
Modules linked in: globalmem
CPU: 0 PID: 609 Comm: cat Not tainted 3.16.0+ #13
task: 9f7d8000 ti: 9f722000 task.ti: 9f722000
PC is at globalmem_read+0xbc/0xcc [globalmem]
LR is at 0x0
pc : [<7f000200>]    lr : [<00000000>]    psr: 00000013
sp : 9f723f30  ip : 00000000  fp : 00000000
r10: 9f414000  r9 : 00000000  r8 : 00001000
r7 : 00000000  r6 : 00001000  r5 : 00001000  r4 : 00000000
r3 : 00000001  r2 : 00000000  r1 : 00001000  r0 : 7f0003cc
Flags: nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: 10c53c7d  Table: 7ec08059  DAC: 00000015
Process cat (pid: 609, stack limit = 0x9f722240)
Stack: (0x9f723f30 to 0x9f724000)
3f20:                                     7ed5ff91 9f723f80 00000000 9f79ab40
3f40: 00001000 7ed5eb18 9f723f80 00000000 00000000 800cb114 00000020 9f722000
3f60: 9f5e4628 9f79ab40 9f79ab40 00001000 7ed5eb18 00000000 00000000 800cb2ec
3f80: 00001000 00000000 9f7168c0 00001000 7ed5eb18 00000003 00000003 8000e4e4
3fa0: 9f722000 8000e360 00001000 7ed5eb18 00000003 7ed5eb18 00001000 0000002f
3fc0: 00001000 7ed5eb18 00000003 00000003 7ed5eb18 00000001 00000003 00000000
3fe0: 0015c23c 7ed5eb00 0000f718 00008d8c 60000010 00000003 00000000 00000000
[<7f000200>] (globalmem_read [globalmem]) from [<800cb114>] (vfs_read+0x98/0x13c)
[<800cb114>] (vfs_read) from [<800cb2ec>] (SyS_read+0x44/0x84)
[<800cb2ec>] (SyS_read) from [<8000e360>] (ret_fast_syscall+0x0/0x30)

Code: e1a05008 e2a77000 e1c360f0 e3a03001 (e58c3000)
---[ end trace 5a36d6470da50d02 ]---

Segmentation fault

分析:

     上述Oops第一行给出“原因”,即访问了NULL pointer。Oops中的PC is at globalmem_read+0xbc/0xcc这一行代码也比较关键,给出了“事发现场”,即globalmem_read()函数偏移0xbc字节的指令处

    通过反汇编globalmem.o可寻找到globalmem_read()函数开头位置偏移0xbc的指令,反汇编方法如下:

drivers/char/globalmem$ arm-none-linux-gnueabi-objdump -d -S globalmem.o

对应的反汇编代码如下,global_read()开始于0x144,偏移0xbc的位置为0x200:

static ssize_t globalmem_read(struct file *filp, char __user * buf, size_t size,
                 loff_t * ppos)
{
 144:      e92d45f0      push    {r4, r5, r6, r7, r8, sl, lr}
 148:      e24dd00c      sub     sp, sp, #12
    unsigned long p = *ppos;
 14c:      e5934000      ldr     r4, [r3]
    …
           *ppos += count;
 1f4:      e2a77000      adc     r7, r7, #0
 1f8:      e1c360f0      strd    r6, [r3]
           ret = count;
           *(unsigned int *)0 = 1; /* a kernel panic */
 1fc:      e3a03001      mov     r3, #1
 200:      e58c3000      str     r3, [ip]
           printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);
 204:      …
    return ret;
}    

“str r3,[ip]”是引起Oops的指令。

备注:

    这里仅仅给出了一个例子,工程实践中的“事发现场”并不全那么容易找到,但方法都是类似的。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值