Linux內核死機調試方法總結


使用空指針和緩沖區溢出是產生oops的兩個最常見原因。

1、直接查看oops信息,首先查找源代碼發生oops的位置,通過查看指令寄存器EIP的值,可以找到位置。再查找函數調用棧可以得到更多的信息。從函數調用棧可辨別出局部變量,全局變量和函數參數。較為重要的信息就是指令指針(EIP),即出錯指令的地址。

例如:在函數faulty_read的oops信息的函數調用棧中,棧頂為ffffffff,棧頂值應為一個小於ffffffff的值,為此值,說明再找不回調用函數地址,說明有可能因緩沖區溢出等原因造成指針錯誤。

如果oops信息顯示觸發oops的地址為0xa5a5a5a5,則說明很可能是因為沒有初始化動態內存引起的。

2、使用\prebuilts\gcc\linux-x86\arm\arm-eabi-4.7\bin\arm-eabi-addr2line命令找到地址對應的程序位置,顯示對應的程序文件名和行號。

1.arm-eabi-addr2line 將類似libxxx.so 0x00012345的調用棧16進制值翻譯成文件名和函數名

  arm-eabi-addr2line -e libxxx.so 0x00012345

2.arm-eabi-nm 列出文件的符號信息

  arm-eabi-nm -l -C -n -S libdvm.so > dvm.data

3.arm-eabi-objdump 列出文件的詳細信息

  arm-eabi-objdump -C -d libc.so > libc.s

通過以上工具的分析 ,我們可以得到較完整的調用棧以及調用邏輯的匯編碼。

addr2line -e -f libc.so 0001173c 

 

objdump -S -D libc.so > deassmble_libc.txt

 

  打開這個反匯編過后的重定向文件,在查詢的時候輸入1173c這個偏移地址,你會看到在茫茫人海中

  00011684 <pthread_create>:
   11684:       e92d4ff0        push    {r4, r5, r6, r7, r8, r9, sl, fp, lr}
   11688:       e24dd01c        sub     sp, sp, #28     ; 0x1c
   1168c:       e1a06001        mov     r6, r1
   11690:       e1a08002        mov     r8, r2
   11694:       e1a09003        mov     r9, r3
   11698:       e3a04001        mov     r4, #1  ; 0x1
   1169c:       e59f521c        ldr     r5, [pc, #540]  ; 118c0 <pthread_create+0x23c>
   116a0:       e58d000c        str     r0, [sp, #12]
   116a4:       eb009a35        bl      37f80 <strncmp+0x20>
   116a8:       e59f2214        ldr     r2, [pc, #532]  ; 118c4 <pthread_create+0x240>
   116ac:       e1a03000        mov     r3, r0
   116b0:       e1a01004        mov     r1, r4
   116b4:       e593c000        ldr     ip, [r3]
   116b8:       e3a0003c        mov     r0, #60 ; 0x3c
   116bc:       e08f3005        add     r3, pc, r5
   116c0:       e7933002        ldr     r3, [r3, r2]
   116c4:       e5834000        str     r4, [r3]
   116c8:       e58dc010        str     ip, [sp, #16]
   116cc:       eb009a3b        bl      37fc0 <strncmp+0x60>
   ...

 

   1173c:       ebffec2b        bl      c7f0 <__pthread_clone>-->就是他了,對你成功了。

 

   ...

 

3、在分析轉儲映像之前,用戶應重啟動進入一個穩定的內核。用戶可以用GDB對拷貝出的轉儲進行有限分析。編譯vmlinux時應加上-g選項,才能生成調試用的符號,然后,用下面的命令調試vmlinux:

gdb vmlinux <dump-file>
首先 objdump -D vmlinx 反匯編你的內核
然后 你可以通過以下幾個寄存器來判斷: 
1. epc    掛在哪個函數里
2. ra     函數的返回地址,
3. Cause  通過這個寄存器可以分析是什么類型的異常.

 4、通過cat /proc/modules獲得模塊內核鏈接基地址,用死機地址減去鏈接基地址得到模塊內偏移,再反匯編,找該偏移對應的函數就找到了死機函數。

用objdump -d test.ko > test.asm的話。可以將模塊文件反匯編。但是因為模塊是目標文件,所以會看到,text段的地址是從0開始的。而該模塊在內核在運行的時候,該模塊的代碼段顯然不是在0地址。另外,該代碼段中你會看到函數間調用,以及函數內部跳轉都是用的b開頭的分支指令(僅限於mips和arm的體系結構的討論),分支指令跳轉是以pc為基准值前后跳轉的。除非,跳轉符號不屬於這個模塊,則需要32位的跳轉。實際上,模塊鏈接到內核的時候,模塊的代碼段是作為一個整體鏈接到內核,所以我們只需要知道模塊鏈接到的基地址,再用死機的地址減去這個基地址這就得到了,該地址在模塊中代碼段的偏移,再通過剛才的反匯編文件就找到了是死在那個函數中。而要得到各個模塊的鏈接地址就很簡單了,直接cat /proc/modules就得到了。需要注意的是用__init修飾了的模塊初始化函數,是放在單獨的段的,通常叫.init.text 。該段會在模塊初始化完成后內存就釋放掉了

 5、google提供了一個python腳本,可以從 http://code.google.com/p/android-ndk-stacktrace-analyzer/ 下載這個python腳本,然后使用
adb logcat -d > logfile 導出 crash 的log,
使用 arm-eabi-objdump (位於build/prebuilt/linux-x86/arm-eabi-4.2.1/bin下面)把so或exe轉換成匯編代碼,如:
arm-eabi-objdump -S mylib.so > mylib.asm,
然后使用腳本
python parse_stack.py <asm-file> <logcat-file>

 

 

 

http://blog.csdn.net/lickylin/article/details/19172725

如上崩潰信息,可知發生崩潰的函數為rb_init_debugfs崩潰的地址為0x804386f8

 1>linux下,到工程的如下目錄下:kernel/linux,找到文件vmlinux,執行命令gdb vmlinux

 在gdb命令下執行如下命令即可查找到出錯函數所在的文件與行數

 (gdb) b *0x804386f8

 2>如果不確定崩潰的地址是否是0x804386f8,可以在文件System.map中

查找函數rb_init_debugfs獲取該函數的地址,然后加上偏移地址(本例中偏移地址為0x14 rb_init_debugfs+0x14/0x70)即可。

 3>直接函數名加偏移量也可以

 (gdb) b *rb_init_debugfs+0x14

 上面是出錯模塊是編譯進內核的,對於編譯進內核的模塊可以通過gdb vmlinux來確定出錯函數所在的文件與行數。

 那如果出錯模塊是動態加載進內核的該怎么辦呢?

 這就需要使用objdump進行反匯編操作了,使用如下命令,就會將C語言與匯編語言同時顯示(需要加-g命令)

 #objdump -S  **.o -g

 如果使用上面命令,還是只顯示匯編,而沒有c語言的話,不用擔心,在你編譯驅動模塊的Makefile中,編譯成.o文件時,增加-g選項,

對於新生成的.o文件再使用上面的命令就 可以看到匯編與c語言共存了。然后根據kernel panic提示中顯示的函數名加上偏移量就能找到出錯行了。

 http://blog.csdn.net/wuruixn/article/details/38320643

1. 為了測試GDB操作,故意在kernel/linux/fs/ioctl.c文件的do_vfs_ioctl方法中加入空指針操作代碼,然后編譯image燒入單板,啟動單板,內核crash,部分log如下:
 
CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == 800a73b8, ra == 800a793c
Oops[#1]:
Cpu 0
$ 0   : 00000000 10008d00 00000000 ffffffea
$ 4   : fffffdfd 10008d01 00000001 00000000
$ 8   : 00000000 7fed2e40 00001cb2 00000b3b
$12   : 00031c7f 2ab5ead7 2aaac7c9 15010000
$16   : 7fed2e18 878ca5a0 00000000 00000001
$20   : 2ab84980 00000000 00000007 00000000
$24   : 00000000 2ab62090                 
$28   : 8782c000 8782fe98 7fed2fc8 800a793c
Hi    : 0000002a
Lo    : 000311fc
epc   : 800a73b8 do_vfs_ioctl+0x88/0x5c8
    Not tainted
ra    : 800a793c sys_ioctl+0x44/0x98
Status: 10008d03    KERNEL EXL IE
Cause : 00000008
BadVA : 00000000
PrId  : 0002a080 (Broadcom4350)
Modules linked in:

Process init (pid: 1, threadinfo=8782c000, task=8782bb68, tls=00000000)
Stack : 878ca1a0 00000004 00000000 10008d00 00000603 2aabcfff 87b0bea8 00000001
        87beeaf0 2aabc000 2aabd000 87aa9cb0 2aabd000 2aabd000 8782ff08 fffffff8
        00000001 7fed3258 00000007 00000000 878ca5a0 0000540d 00000000 00000001
        2ab84980 800a793c 08100871 00000001 87bea41c 0000fff2 00000000 2abbdff0
        7fed2e18 00000001 7fed2e60 2abbdff0 00000120 8001ba7c 00000000 00000000
        ...
Call Trace:(--Raw--
[<800a793c>] sys_ioctl+0x44/0x98
[<8001ba7c>] stack_done+0x20/0x3c

Call Trace:
[<800a73b8>] do_vfs_ioctl+0x88/0x5c8
[<800a793c>] sys_ioctl+0x44/0x98
[<8001ba7c>] stack_done+0x20/0x3c

Code: 0c029c9f  02003021  8fbf0064 <8c020000> 8fb40060  8fb3005c  8fb20058  8fb10054  8fb00050
Disabling lock debugging due to kernel taint
Kernel panic - not syncing: Attempted to kill init!
Rebooting in 1 seconds..<6>
stopping CPU 1
kerSysMipsSoftReset: called on cpu 0
    

2. 啟動GDB, 直接在主機(開發機)控制台運行gdb工具(或./mips-linux-uclibc-gdb),然后敲入 file  ...../vmlinux啟動帶調試信息的內核,注意此時要配置內核debug開關重新編譯內核生成文件vmlinux(比之前的文件大10倍左右,50M以上),配置內核debug開關如下,

Kernel hacking  --->
    [*] Kernel debugging
    [*] Compile the kernel with debug info
編譯內核命令:make kernelbuild,要在project的根目錄下運行該命令進行編譯。
啟動GDB命令如下:
 
3. GDB調試定位
Checked call trace log, the most important part in log is “epc” (exception program counter), it is where the crash happened. In this example, the “epc” is 0xc0d1d488. For X1000 and X3500, the address like 0x8XXXXXXXX is in kernel, and the address like 0xcXXXXXXX is in some module.
地址0x800a73b8就是對應於地址(do_vfs_ioctl+0x88),后者表示位於函數do_vfs_ioctl的偏移地址0x88處。
調試過程如下:
 
(gdb) list *(0x800a73b8)
0x800a73b8 is in do_vfs_ioctl (fs/ioctl.c:569).
564                             error = vfs_ioctl(filp, cmd, arg);
565                     break;
566             }
567             error = *test;
568             return error;
569     }
570
571     SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
572     {
573             struct file *filp;
(gdb) list*(do_vfs_ioctl+0x88)
0x800a73b8 is in do_vfs_ioctl (fs/ioctl.c:569).
564                             error = vfs_ioctl(filp, cmd, arg);
565                     break;
566             }
567             error = *test;
568             return error;
569     }
570
571     SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
572     {
573             struct file *filp;
(gdb) list*(sys_ioctl+0x44)
 
 
可以分析該函數附近代碼發現錯誤位置在line567,test為空指針(故意在前面賦值NULL)。

 

 

注:如果list*(0x80xxxxxx)命令提示如下信息:No source file for address 0x80xxxxxx. 原因是沒有在make menuconfig中打開對應的調試開關進行編譯。

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM