通過gdb定位核心宕機
核心宕機不要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 條件暫存器,它分成8個4位欄位,cr0-cr7,它反映了某個演算法操作的結果並且提供條件分支的機制。 暫存器r1、r14-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也就是十進位制的464,dump資訊裡的R31暫存器是0,即dev是NULL,這也就是這條指令導致產生訪問非法地址0x000001d0的原因:
Unable to handle kernel paging request for data at address 0x000001d0