標籤:
使用null 指標和緩衝區溢位是產生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方法中加入null 指標作業碼,然後編譯image燒入單板,啟動單板,核心crash,部分log如下: CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == 800a73b8, ra == 800a793cOops[#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中開啟對應的調試開關進行編譯。
Linux核心死機調試方法總結