通过gdb定位内核宕机

2013年9月30日 4点热度 0条评论 来源: turkeyzhou

内核宕机不要panic,我们有gdb,通过dump出来信息可以快速定位出出错的地方。下面就以一个实际遇到的例子描述一下怎么通过gdb找到实际出错的代码行。

  Unable to handle kernel paging request for data at address 0x000001d0

  Faulting instruction address: 0xc0220820

  Oops: Kernel access of bad area, sig: 11 [#1]

  MPC85xx CDS

  Modules linked in: hsl linux_bcm_diag_full(P) linux_uk_proxy(P) linux_kernel_bde(P) wdtDrv pciDrv i2cDrv flashDrv cpuDrv

  NIP: c0220820 LR: c0220818 CTR: c02209a4

  REGS: dde67b20 TRAP: 0300   Tainted: P           (2.6.27.21)

  MSR: 00029000 <EE,ME>  CR: 24000824  XER: 00000000

  DEAR: 000001d0, ESR: 00000000

  TASK = decea500[940] 'nsm' THREAD: dde66000

  GPR00: 00000001 dde67bd0 decea500 00000001 00000001 decea538 00000078 00000000

  GPR08: deceae80 00000000 83d0da00 c02209a4 e8669fbb 10219d44 00000290 ffffffff

  GPR16: 00000001 00000000 100d4464 100e7db4 00000000 00000008 dde67ddc dde67dfc

  GPR24: dde67ddc dde67dfc dde67dc0 e3970000 00000001 00000000 ddac8018 00000000

  NIP [c0220820] rollback_registered+0x2c/0x130

  LR [c0220818] rollback_registered+0x24/0x130

  Call Trace:

  [dde67bd0] [00000008] 0x8 (unreliable)

  [dde67be0] [c022094c] unregister_netdevice+0x28/0x80

  [dde67bf0] [c02209c4] unregister_netdev+0x20/0x38

  [dde67c10] [e393661c] hsl_eth_drv_destroy_netdevice+0x10/0x24 [hsl]

  [dde67c20] [e3938490] hsl_os_l3_if_unconfigure+0x20/0x34 [hsl]

  [dde67c30] [e393aa94] hsl_ifmgr_L3_delete2+0xf0/0x2b4 [hsl]

  [dde67c50] [e394103c] hsl_ifmgr_L3_unregister+0x60/0xfc [hsl]

  [dde67c70] [e3926004] hsl_msg_recv_if_delete_svi+0x54/0x15c [hsl]

  [dde67c90] [e391feb8] hsl_sock_process_msg+0x260/0x7a8 [hsl]

  [dde67ca0] [e392046c] _hsl_sock_sendmsg+0x6c/0xac [hsl]

  [dde67cc0] [c0211fc0] sock_sendmsg+0xac/0xe4

  [dde67db0] [c02121cc] sys_sendmsg+0x1d4/0x284

  [dde67f00] [c0212d40] sys_socketcall+0xe8/0x200

  [dde67f40] [c000df5c] ret_from_syscall+0x0/0x3c

  Instruction dump:

  4bffff48 9421fff0 7c0802a6 3d20c039 93e1000c 7c7f1b78 90010014 800954d8

  0f000000 4800a101 2f830000 419e00e4 <813f01d0> 2f890000 409e0034 3c60c033

  ---[ end trace fc1818f8d9535ae2 ]---

首先介绍一下ppc(powerpc)的寄存器:

通用寄存器:
  r0   在函数开始(function prologs)时使用。
  r1   堆栈指针,相当于ia32架构中的esp寄存器,idapro把这个寄存器反汇编标识为sp
  r2   内容表(toc)指针,idapro把这个寄存器反汇编标识为rtoc。系统调用时,它包含系统调用号。
  r3   作为第一个参数和返回地址。
  r4-r10 函数或系统调用开始的参数。
  r11   用在指针的调用和当作一些语言的环境指针。
  r12   它用在异常处理和glink(动态连接器)代码。
  r13   保留作为系统线程ID
  r14-r31 作为本地变量,非易失性。
专用寄存器:
  lr   链接寄存器,它用来存放函数调用结束处的返回地址。
  ctr   计数寄存器,它用来当作循环计数器,会随特定转移操作而递减。
  xer   定点异常寄存器,存放整数运算操作的进位以及溢出信息。
  msr   机器状态寄存器,用来配置微处理器的设定。
  cr   条件寄存器,它分成84位字段,cr0-cr7,它反映了某个算法操作的结果并且提供条件分支的机制。
  寄存器r1r14-r31是非易失性的,这意味着它们的值在函数调用过程保持不变。寄存器r2也算非易失性,但是只有在调用函数在调用后必须恢复它的值时才被处理。

ppc使用了LR寄存器(Link Register)来完成:在bl指令跳转前,下条指令的地址会被保存到LR,函数返回时候调用 blr,系统会跳转到LR所表示的地址,完成返回。

------------------------------分割线--------------------------------------------------

我们开始通过gdb查找宕机的地方:

1. 首先进入内核的编译目录,保证代码与出错的内核一直,没有编译的话要先编译,编译完之后,在当前目录就会有vmlinux,这个是未压缩过的内核bin文件,用gdb运行它(这里是powerpc平台):

 gdb4ppc ./vmlinux

2. NIP显示的下一条指令地址(0xc0220820),使用list 指令显示出对应的函数:

(gdb) list *0xc0220820

0xc0220820 is in rollback_registered (net/core/dev.c:4051).

4046    {

4047            BUG_ON(dev_boot_phase);

4048            ASSERT_RTNL();

4049

4050            /* Some devices call without registering for initialization unwind. */

4051            if (dev->reg_state == NETREG_UNINITIALIZED) {

4052                    printk(KERN_DEBUG "unregister_netdevice: device %s/%p never "

4053                                      "was registered\n", dev->name, dev);

4054

4055                    WARN_ON(1);

3. 反汇编这个函数 (disassemble /r表示原始的16进制格式的汇编)

(gdb) disassemble /r rollback_registered

Dump of assembler code for function rollback_registered:

   0xc02207f4 <+0>:      94 21 ff f0    stwu    r1,-16(r1)

   0xc02207f8 <+4>:      7c 08 02 a6    mflr    r0

   0xc02207fc <+8>:      3d 20 c0 39    lis     r9,-16327

   0xc0220800 <+12>:     93 e1 00 0c    stw     r31,12(r1)

   0xc0220804 <+16>:     7c 7f 1b 78    mr      r31,r3

   0xc0220808 <+20>:     90 01 00 14    stw     r0,20(r1)

   0xc022080c <+24>:     80 09 54 d8    lwz     r0,21720(r9)

   0xc0220810 <+28>:     0f 00 00 00    twnei   r0,0

   0xc0220814 <+32>:     48 00 a1 01    bl      0xc022a914 <rtnl_is_locked>

   0xc0220818 <+36>:     2f 83 00 00    cmpwi   cr7,r3,0

   0xc022081c <+40>:        41 9e 00 e4    beq     cr7,0xc0220900 <rollback_registered+268>

   0xc0220820 <+44>:     81 3f 01 d0    lwz     r9,464(r31)

   0xc0220824 <+48>:     2f 89 00 00    cmpwi   cr7,r9,0

   0xc0220828 <+52>:     40 9e 00 34    bne     cr7,0xc022085c <rollback_registered+104>

 可以看到汇编与dump信息里面的Instruction dump完全对得上,地址0xc022082

对应汇编 81 3f 01 d0  dump信息里面特意用尖括号标识出来了,看来就死在这一条指令了。

4. 找到对应的代码行(disassemble /m 将源码嵌入到汇编里面),分析原因。

(gdb) disassemble /m rollback_registered

Dump of assembler code for function rollback_registered:

4046    {

   0xc02207f4 <+0>:     stwu    r1,-16(r1)

   0xc02207f8 <+4>:     mflr    r0

   0xc0220800 <+12>:    stw     r31,12(r1)

   0xc0220804 <+16>:    mr      r31,r3

   0xc0220808 <+20>:    stw     r0,20(r1) 

4047            BUG_ON(dev_boot_phase);

   0xc02207fc <+8>:     lis     r9,-16327

   0xc022080c <+24>:    lwz     r0,21720(r9)

   0xc0220810 <+28>:    twnei   r0,0 

4048            ASSERT_RTNL();

   0xc0220814 <+32>:    bl      0xc022a914 <rtnl_is_locked>

   0xc0220818 <+36>:    cmpwi   cr7,r3,0

   0xc022081c <+40>:    beq     cr7,0xc0220900 <rollback_registered+268>

   0xc0220900 <+268>:   lis     r4,-16333

   0xc0220904 <+272>:   lis     r3,-16334

   0xc0220908 <+276>:   addi    r4,r4,-16240

   0xc022090c <+280>:   li      r5,4048

   0xc0220910 <+284>:   addi    r3,r3,-3816

   0xc0220914 <+288>:   crclr   4*cr1+eq

   0xc0220918 <+292>:   bl      0xc0027ca0 <printk>

   0xc022091c <+296>:   bl      0xc0006cfc <dump_stack>

   0xc0220920 <+300>:   b       0xc0220820 <rollback_registered+44> 

4049

4050            /* Some devices call without registering for initialization unwind. */

4051            if (dev->reg_state == NETREG_UNINITIALIZED) {

   0xc0220820 <+44>:    lwz     r9,464(r31)

   0xc0220824 <+48>:    cmpwi   cr7,r9,0

   0xc0220828 <+52>:    bne     cr7,0xc022085c <rollback_registered+104>

这样我们就找到对应的代码语句是 rollback_registered

if (dev->reg_state == NETREG_UNINITIALIZED) {

这一行,指令是lwz r9,464(r31)R31存放的是dev指针,我们先确认一下偏移464的地方就是

dev->reg_state

(gdb)    p &((struct net_device *)0)->reg_state

$1 = (enum {...} *) 0x1d0

0x1d0也就是十进制的464dump信息里的R31寄存器是0,即devNULL,这也就是这条指令导致产生访问非法地址0x000001d0的原因:

Unable to handle kernel paging request for data at address 0x000001d0

    原文作者:turkeyzhou
    原文地址: https://blog.csdn.net/turkeyzhou/article/details/12187427
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。