根據PANIC信息定位內核代碼位置

首先Linux內核需要打開編譯的調試選項CONFIG_DEBUG_INFO,如下:

Kernel hacking --->
    [*] Kernel debugging
    [*] Compile the kernel with debug info

這樣,內核在編譯之後的目標文件帶有調試信息。檢查內核的Makefile文件,打開CONFIG_DEBUG_INFO選項之後,內核爲KBUILD_CFLAGS變量添加了-g的調試選項。

ifdef CONFIG_DEBUG_INFO
KBUILD_CFLAGS   += -g
KBUILD_AFLAGS   += -gdwarf-2
endif

比如有如下的panic信息,指令指針IP的絕對位置在ffffffff8196f70c,相對位置在函數ip_local_deliver_finish開始的位置偏移0x1c,另外0x100爲此函數的結束位置。

[  266.469971] BUG: unable to handle kernel NULL pointer dereference at 00000000000004a0
[  266.470020] IP: [<ffffffff8196f70c>] ip_local_deliver_finish+0x1c/0x100
[  266.470020] PGD ad37a067 PUD ad37b067 PMD 0 
[  266.470020] Oops: 0000 [#1] SMP 
[  266.470020] CPU: 1 PID: 238 Comm: initXXXXXXXXXXX Not tainted 3.10.0 #1
[  266.470020] Hardware name: To Be Filled By O.E.M. To Be Filled By O.E.M./G6LD525 VER2, BIOS 080016  07/24/2015
[  266.470020] task: ffff88007f8b3b80 ti: ffff88009dae4000 task.ti: ffff88009dae4000
[  266.470020] RIP: 0010:[<ffffffff8196f70c>]  [<ffffffff8196f70c>] ip_local_deliver_finish+0x1c/0x100
[  266.470020] RSP: 0018:ffff88009dae7c98  EFLAGS: 00010206

查看內核代碼可知,ip_local_deliver_finish函數位於文件net/ipv4/ip_input.c中。使用objdump工具反編譯此文件的目標文件:

# objdump -DSl net/ipv4/ip_input.o

如下爲反編譯內容,此處使用panic信息中的相對位置定位出問題的代碼位置:

ip_input.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <ip_local_deliver_finish>:
ip_local_deliver_finish():
/linux-3.10.0/net/ipv4/ip_input.c:198
   0:   e8 00 00 00 00          callq  5 <ip_local_deliver_finish+0x5>
   5:   55                      push   %rbp
   6:   48 89 e5                mov    %rsp,%rbp
   9:   41 55                   push   %r13
   b:   41 54                   push   %r12
   d:   49 89 f4                mov    %rsi,%r12
  10:   53                      push   %rbx
/linux-3.10.0/include/linux/netdevice.h:2058
  11:   48 8b 46 20             mov    0x20(%rsi),%rax
skb_network_header_len():
/linux-3.10.0/include/linux/skbuff.h:2095
  15:   0f b7 96 4e 01 00 00    movzwl 0x14e(%rsi),%edx
ip_local_deliver_finish():
/linux-3.10.0/include/linux/netdevice.h:2058
  1c:   4c 8b a8 a0 04 00 00    mov    0x4a0(%rax),%r13
skb_network_header_len():


  fd:   00 00 00

0000000000000100 <ip_rcv_finish>:

函數ip_local_deliver_finish的起始位置爲0000000000000000,加上偏移0x1c,可知出錯位置在文件include/linux/netdevice.h的2058行,而此netdevice.h中的函數有net/ipv4/ip_input.c的198行調用而來,看以下代碼:

net/ipv4/ip_input.c:
196 static int ip_local_deliver_finish(struct sock *sk, struct sk_buff *skb)
197 {
198     struct net *net = dev_net(skb->dev);

include/linux/netdevice.h:
2055 static inline
2056 struct net *dev_net(const struct net_device *dev)
2057 {
2058     return read_pnet(&dev->nd_net);
2059 }

可知,問題出在dev_net函數中,讀取的dev的成員nd_net爲非法值
---------------------------------------------------------------------------------------------------------------------------------------------------------------------

有一個問題時,以上objdump反彙編的代碼沒有插進去c代碼。於是,寫了一個hello world的測試程序,使用gcc -g編譯後,再用objdump反彙編,還是可以看到插入的C代碼的。內核只能看到行號,感覺應該是-O2優化的影響導致,還不確定。


# gcc -g main.c
# objdump -DSl a.out

結果如下:

 320 int main()
 321 {
 322   40052d:   55                      push   %rbp
 323   40052e:   48 89 e5                mov    %rsp,%rbp
 324 main.c:8
 325     printf("hello world!\n");
 326   400531:   bf d4 05 40 00          mov    $0x4005d4,%edi
 327   400536:   e8 d5 fe ff ff          callq  400410 <puts@plt>
 328 /home/kzhang/firewall_tc_2.0.0/root/1ksa0b/linux-3.10.0/main.c:10
 329 }

完。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章