反匯編調試內核驅動 Oops提示【轉】


以下部分內容轉自:https://blog.csdn.net/jiatingqiang/article/details/7481497

反匯編調試內核驅動

arm-none-linux-gnueabi-objdump -S kmod-demo1.o  > a.txt

 

什么是Oops?從語言學的角度說,Oops應該是一個擬聲詞。當出了點小事故,或者做了比較尷尬的事之后,你可以說"Oops",翻譯成中國話就叫做“哎呦”。“哎呦,對不起,對不起,我真不是故意打碎您的杯子的”。看,Oops就是這個意思。

在Linux內核開發中的Oops是什么呢?其實,它和上面的解釋也沒什么本質的差別,只不過說話的主角變成了Linux。當某些比較致命的問題出現時,我們的Linux內核也會抱歉的對我們說:“哎呦(Oops),對不起,我把事情搞砸了”。Linux內核在發生kernel panic時會打印出Oops信息,把目前的寄存器狀態、堆棧內容、以及完整的Call trace都show給我們看,這樣就可以幫助我們定位錯誤。

下面,我們來看一個實例。為了突出本文的主角--Oops,這個例子唯一的作用就是造一個空指針引用錯誤。

#include <linux/kernel.h>
#include <linux/module.h>
 
static  int  __init hello_init( void )
{
     int  *p = 0;
     
     *p = 1;
     return  0;
}
 
static  void  __exit hello_exit( void )
{
     return ;
}
 
module_init(hello_init);
module_exit(hello_exit);
 
MODULE_LICENSE( "GPL" );

很明顯,錯誤的地方就是第8行。

接下來,我們把這個模塊編譯出來,再用insmod來插入到內核空間,正如我們預期的那樣,Oops出現了。

[  100.243737] BUG: unable to handle kernel NULL pointer dereference at (null)

[  100.244985] IP: [<f82d2005>] hello_init+0x5/0x11 [hello]

[  100.262266] *pde = 00000000 

[  100.288395] Oops: 0002 [#1] SMP 

[  100.305468] last sysfs file: /sys/devices/virtual/sound/timer/uevent

[  100.325955] Modules linked in: hello(+) vmblock vsock vmmemctl vmhgfs acpiphp snd_ens1371 gameport snd_ac97_codec ac97_bus snd_pcm_oss snd_mixer_oss snd_pcm snd_seq_dummy snd_seq_oss snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq snd_timer snd_seq_device ppdev psmouse serio_raw fbcon tileblit font bitblit softcursor snd parport_pc soundcore snd_page_alloc vmci i2c_piix4 vga16fb vgastate intel_agp agpgart shpchp lp parport floppy pcnet32 mii mptspi mptscsih mptbase scsi_transport_spi vmxnet

[  100.472178] [  100.494931] Pid: 1586, comm: insmod Not tainted (2.6.32-21-generic #32-Ubuntu) VMware Virtual Platform

[  100.540018] EIP: 0060:[<f82d2005>] EFLAGS: 00010246 CPU: 0

[  100.562844] EIP is at hello_init+0x5/0x11 [hello]

[  100.584351] EAX: 00000000 EBX: fffffffc ECX: f82cf040 EDX: 00000001

[  100.609358] ESI: f82cf040 EDI: 00000000 EBP: f1b9ff5c ESP: f1b9ff5c

[  100.631467]  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068

[  100.657664] Process insmod (pid: 1586, ti=f1b9e000 task=f137b340 task.ti=f1b9e000)

[  100.706083] Stack:

[  100.731783]  f1b9ff88 c0101131 f82cf040 c076d240 fffffffc f82cf040 0072cff4 f82d2000

[  100.759324] <0> fffffffc f82cf040 0072cff4 f1b9ffac c0182340 f19638f8 f137b340 f19638c0

[  100.811396] <0> 00000004 09cc9018 09cc9018 00020000 f1b9e000 c01033ec 09cc9018 00015324

[  100.891922] Call Trace:

[  100.916257]  [<c0101131>] ? do_one_initcall+0x31/0x190

[  100.943670]  [<f82d2000>] ? hello_init+0x0/0x11 [hello]

[  100.970905]  [<c0182340>] ? sys_init_module+0xb0/0x210

[  100.995542]  [<c01033ec>] ? syscall_call+0x7/0xb

[  101.024087] Code: <c7> 05 00 00 00 00 01 00 00 00 5d c3 00 00 00 00 00 00 00 00 00 00 

[  101.079592] EIP: [<f82d2005>] hello_init+0x5/0x11 [hello] SS:ESP 0068:f1b9ff5c

[  101.134682] CR2: 0000000000000000

[  101.158929] ---[ end trace e294b69a66d752cb ]---

Oops首先描述了這是一個什么樣的bug,然后指出了發生bug的位置,即“IP: [<f82d2005>] hello_init+0x5/0x11 [hello]”。

在這里,我們需要用到一個輔助工具objdump來幫助分析問題。objdump可以用來反匯編,命令格式如下:

objdump -S  hello.o

下面是hello.o反匯編的結果,而且是和C代碼混排的,非常的直觀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
hello.o:     file format elf32-i386
 
 
Disassembly of section .init.text:
 
00000000 <init_module>:
#include <linux/kernel.h>
#include <linux/module.h>
 
static  int  __init hello_init( void )
{
    0:   55                      push   %ebp
     int  *p = 0;
     
     *p = 1;
     
     return  0;
}
    1:   31 c0                   xor    %eax,%eax
#include <linux/kernel.h>
#include <linux/module.h>
 
static  int  __init hello_init( void )
{
    3:   89 e5                   mov    %esp,%ebp
     int  *p = 0;
     
     *p = 1;
    5:   c7 05 00 00 00 00 01    movl   $0x1,0x0
    c:   00 00 00
     
     return  0;
}
    f:   5d                      pop    %ebp
   10:   c3                      ret   
 
Disassembly of section . exit .text:
 
00000000 <cleanup_module>:
 
static  void  __exit hello_exit( void )
{
    0:   55                      push   %ebp
    1:   89 e5                   mov    %esp,%ebp
    3:   e8 fc ff ff ff          call   4 <cleanup_module+0x4>
     return ;
}
    8:   5d                      pop    %ebp
    9:   c3                      ret   

對照Oops的提示,我們可以很清楚的看到,出錯的位置hello_init+0x5的匯編代碼是:

1
5:c7 05 00 00 00 00 01 movl   $0x1,0x0

這句代碼的作用是把數值1存入0這個地址,這個操作當然是非法的。

我們還能看到它對應的c代碼是:

1
*p = 1;

Bingo!在Oops的幫助下我們很快就解決了問題。

 

我們再回過頭來檢查一下上面的Oops,看看Linux內核還有沒有給我們留下其他的有用信息。

Oops: 0002 [#1]

這里面,0002表示Oops的錯誤代碼(寫錯誤,發生在內核空間),#1表示這個錯誤發生一次。

Oops的錯誤代碼根據錯誤的原因會有不同的定義,本文中的例子可以參考下面的定義(如果發現自己遇到的Oops和下面無法對應的話,最好去內核代碼里查找):

 * error_code:
 *      bit 0 == 0 means no page found, 1 means protection fault
 *      bit 1 == 0 means read, 1 means write
 *      bit 2 == 0 means kernel, 1 means user-mode
 *      bit 3 == 0 means data, 1 means instruction

有時候,Oops還會打印出Tainted信息。這個信息用來指出內核是因何種原因被tainted(直譯為“玷污”)。具體的定義如下:

  1: 'G' if all modules loaded have a GPL or compatible license, 'P' if any proprietary module has been loaded.  Modules without a MODULE_LICENSE or with a MODULE_LICENSE that is not recognised by insmod as GPL compatible are assumed to be proprietary.
  2: 'F' if any module was force loaded by "insmod -f", ' ' if all modules were loaded normally.
  3: 'S' if the oops occurred on an SMP kernel running on hardware that hasn't been certified as safe to run multiprocessor. Currently this occurs only on various Athlons that are not SMP capable.
  4: 'R' if a module was force unloaded by "rmmod -f", ' ' if all modules were unloaded normally.
  5: 'M' if any processor has reported a Machine Check Exception, ' ' if no Machine Check Exceptions have occurred.
  6: 'B' if a page-release function has found a bad page reference or some unexpected page flags.
  7: 'U' if a user or user application specifically requested that the Tainted flag be set, ' ' otherwise.
  8: 'D' if the kernel has died recently, i.e. there was an OOPS or BUG.
  9: 'A' if the ACPI table has been overridden.
 10: 'W' if a warning has previously been issued by the kernel. (Though some warnings may set more specific taint flags.)
 11: 'C' if a staging driver has been loaded.
 12: 'I' if the kernel is working around a severe bug in the platform firmware (BIOS or similar).

基本上,這個Tainted信息是留給內核開發者看的。用戶在使用Linux的過程中如果遇到Oops,可以把Oops的內容發送給內核開發者去debug,內核開發者根據這個Tainted信息大概可以判斷出kernel panic時內核運行的環境。如果我們只是debug自己的驅動,這個信息就沒什么意義了。

 

本文的這個例子非常簡單,Oops發生以后沒有造成宕機,這樣我們就可以從dmesg中查看到完整的信息。但更多的情況是Oops發生的同時系統也會宕機,此時這些出錯信息是來不及存入文件中的,關掉電源后就無法再看到了。我們只能通過其他的方式來記錄:手抄或者拍照。

還有更壞的情況,如果Oops信息過多的話,一頁屏幕顯示不全,我們怎么來查看完整的內容呢?第一種方法,在grub里用vga參數指定更高的分辨率以使屏幕可以顯示更多的內容。很明顯,這個方法其實解決不了太多的問題;第二種方法,使用兩台機器,把調試機的Oops信息通過串口打印到宿主機的屏幕上。但現在大部分的筆記本電腦是沒有串口的,這個解決方法也有很大的局限性;第三種方法,使用內核轉儲工具kdump把發生Oops時的內存和CPU寄存器的內容dump到一個文件里,之后我們再用gdb來分析問題。

 

開發內核驅動的過程中可能遇到的問題是千奇百怪的,調試的方法也是多種多樣,Oops是Linux內核給我們的提示,我們要用好它。

 

#################################

修訂:

2010-11-19: Oops的error code

 

objdump命令的使用

 

objdump命令是Linux下的反匯編目標文件或者可執行文件的命令,它還有其他作用,下面以ELF格式可執行文件test為例詳細介紹:

 

objdump -f test

顯示test的文件頭信息

 

objdump -d test

反匯編test中的需要執行指令的那些section

 

 

 

objdump -D test

與-d類似,但反匯編test中的所有section

 

 

objdump -h test

顯示test的Section Header信息

 

 

 

objdump -x test

顯示test的全部Header信息

 

 

 

objdump -s test

除了顯示test的全部Header信息,還顯示他們對應的十六進制文件代碼

 

 

 

舉例:

將C源代碼和反匯編出來的指令對照:

1.

編譯成目標文件(要加-g選項)

gcc -g -o test.c

 

2.

輸出C源代碼和反匯編出來的指令對照的格式

objdump -S test.o

如下:

 

 

 

 

如何對任意一個二進制文件進行反匯編?

我們可以這樣做:

objdump -D -b binary -m i386 a.bin

-D表示對全部文件進行反匯編,-b表示二進制,-m表示指令集架構,a.bin就是我們要反匯編的二進制文件

 

objdump -m可以查看更多支持的指令集架構,如i386:x86-64,i8086等

 

另外上面的所有objdump命令的參數同樣適用於arm-linux-objdump。

 

同時我們也可以指定big-endian或little-endian(-EB或-EL),我們可以指定從某一個位置開始反匯編等。所以objdump命令是非常強大的!

 

轉自:http://blog.csdn.net/muyuyuzhong/article/details/7755291

arm-linux-gcc -wall -O2 -c -o $@ $<
 
-o 只激活預處理,編譯,和匯編,也就是他只把程序做成obj文件 
-Wall 指定產生全部的警告信息 
-O2 編譯器對程序提供的編譯優化選項,在編譯的時候使用該選項,可以使生成的執行文件的執行效率提高 
-c 表示只要求編譯器進行編譯,而不要進行鏈接,生成以源文件的文件名命名但把其后綴由 .c 或 .cc 變成 .o 的目標文件 
-S 只激活預處理和編譯,就是指把文件編譯成為匯編代碼
 
arm-linux-ld 
直接指定代碼段,數據段,BSS段的起始地址
-Ttest startaddr
-Tdata startaddr
-Tbss startaddr
示例:
Arm-linux-ld –Ttext 0x0000000 –g led.o –o led_elf
 
使用連接腳本設置地址:
Arm-linux-ld –Ttimer.lds –o timer_elf  $^
其中timer.lds 為連接腳本
完整的連接腳本格式:
SECTIONS{

Secname start ALING(aling) (NOLOAD):AT(ldaddr)
{contents} > region:phdr=fill
…..
}
arm-linux-objcopy被用來復制一個目標文件的內容到另一個文件中,可用於不同源文件的之間的格式轉換
示例:
Arm-linux-objcopy –o binary –S elf_file bin_file
常用的選項:
input-file , outflie
輸入和輸出文件,如果沒有outfile,則輸出文件名為輸入文件名
2.-l bfdname或—input-target=bfdname
用來指明源文件的格式,bfdname是BFD庫中描述的標准格式名,如果沒指明,則arm-linux-objcopy自己分析
3.-O bfdname 輸出的格式
4.-F bfdname 同時指明源文件,目的文件的格式
5.-R sectionname 從輸出文件中刪除掉所有名為sectionname的段
6.-S 不從源文件中復制重定位信息和符號信息到目標文件中
7.-g 不從源文件中復制調試符號到目標文件中
 
arm-linux-objdump
查看目標文件(.o文件)和庫文件(.a文件)信息
arm-linux-objdump -D -m arm led_elf > led.dis
-D 顯示文件中所有匯編信息
-m machine
指定反匯編目標文件時使用的架構,當待反匯編文件本身沒有描述架構信息的時候(比如S-records),這個選項很有用。可以用-i選項列出這里能夠指定的架構.

常用選項:

1.-b bfdname 指定目標碼格式
2.—disassemble或者-d 反匯編可執行段
3.—dissassemble-all或者-D 反匯編所有段
4.-EB,-EL指定字節序
5.—file-headers或者-f 顯示文件的整體頭部摘要信息
6.—section-headers,--headers或者-h 顯示目標文件中各個段的頭部摘要信息
7.—info 或者-I 顯示支持的目標文件格式和CPU架構
8.—section=name或者-j name顯示指定section 的信息
9.—architecture=machine或者-m machine 指定反匯編目標文件時使用的架構

1. 修改源代碼的頂層  Makefile
   CC =$(CROSSCOM_PILE)gcc            -->
   CC =$(CROSSCOM_PILE)gcc   -g
   使成生的vmlinux中含有debug信息
2. 所有生成.o的rule中再加一條    
   CC   -E  -dD -C $< > /preprocessing/$(shell pwd)/$<
   生成預處理文件從這個文件里面能很容易找到c源文件的宏定義
3. objdump -h vmlinux > vmlinux.txt
   顯示  linux 內核段信息,如段的開始虛擬地址,段的長度
4. objdump -S -l -z vmlinux > vmlinux.txt
   反匯編vmlinux到vmlinux.txt,vmlinux.txt含有匯編和c源文件的混合代碼,看起來很方便。而且能一步步看linux怎么一步步運行的。
5. objdump -S -l -z -j xxxx(section name) vmlinux > vmlinux.txt
   反匯編linux內核段xxxx到文件vmlinux.txt中。
6. objdump -x vmlinux > x.txt
   vmliux中所有段的頭信息,其中包口vmlinux的入口地址等
7. objdump --debugging vmlinux > debugging.txt
   很多有用的debug信息,如函數名,結構體定義等
   我覺的用根據以上信息,ultraedit看很方便。尤其在vmlinux.txt中選中文件名,
   用ultraedit右鍵的open能馬上打開文件,很方便。

 

 


objdump -j .text -S vmlinux > vmlinux.txt
-S盡可能反匯編出源代碼,尤其當編譯的時候指定了-g這種調試參數時,效果比較明顯。隱含了-d參數。

-l用文件名和行號標注相應的目標代碼,僅僅和-d、-D或者-r一起使用使用-ld和使用-d的區別不是很大,在源碼級調試的時候有用,要求編譯時使用了-g之類的調試編譯選項。

[-l | --line-numbers]
[-S | --source]    混合匯編
[-z | --disassemble-zeroes]
[-j section | --section=section]
[--prefix-addresses]

 

驅動程序的調試
一. 打印: prink, 自制proc文件
UBOOT傳入console=ttySAC0 console=tty1
1. 內核處理UBOOT傳入的參數
console_setup
    add_preferred_console // 我想用名為"ttySAC0"的控制台,先記錄下來

2. 硬件驅動的入口函數里:
    drivers/serial/s3c2410.c
        register_console(&s3c24xx_serial_console);        

3. printk
        vprintk
            /* Emit the output into the temporary buffer */
            // 先把輸出信息放入臨時BUFFER
            vscnprintf
            
            // Copy the output into log_buf.
            // 把臨時BUFFER里的數據稍作處理,再寫入log_buf
            // 比如printk("abc")會得到"<4>abc", 再寫入log_buf
            // 可以用dmesg命令把log_buf里的數據打印出來重現內核的輸出信息
            
            
            // 調用硬件的write函數輸出
            release_console_sem();
                call_console_drivers(_con_start, _log_end);
                    // 從log_buf得到數據,算出打印級別
                    _call_console_drivers(start_print, cur_index, msg_level);            
                        // 如果可以級別夠格打印
                        if ((msg_log_level < console_loglevel
                            __call_console_drivers
                                con->write(con, &LOG_BUF(start), end - start);




二. 根據內核打印的段錯誤信息分析
a. 作為模塊:
1. 根據pc值確定該指令屬於內核還是外加的模塊
pc=0xbf000018 它屬於什么的地址?是內核還是通過insmod加載的驅動程序?
先判斷是否屬於內核的地址: 看System.map確定內核的函數的地址范圍:c0004000~c03265a4

如果不屬於System.map里的范圍,則它屬於insmod加載的驅動程序

2. 假設它是加載的驅動程序引入的錯誤,怎么確定是哪一個驅動程序?
先看看加載的驅動程序的函數的地址范圍
cat /proc/kallsyms  (內核函數、加載的函數的地址)
從這些信息里找到一個相近的地址, 這個地址<=0xbf000018
比如找到了:
bf000000 t first_drv_open    [first_drv]

3. 找到了first_drv.ko
在PC上反匯編它: arm-linux-objdump -D first_drv.ko > frist_drv.dis
在dis文件里找到first_drv_open

    first_drv.dis文件里              insmod后
00000000 <first_drv_open>:       bf000000 t first_drv_open    [first_drv]
00000018                         pc = bf000018
                                 



./firstdrvtest on
Unable to handle kernel paging request at virtual address 56000050
內核使用56000050來訪問時發生了錯誤

pgd = c3eb0000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: first_drv
CPU: 0    Not tainted  (2.6.22.6 #1)
PC is at first_drv_open+0x18(該指令的偏移)/0x3c(該函數的總大小) [first_drv]
PC就是發生錯誤的指令的地址
大多時候,PC值只會給出一個地址,不到指示說是在哪個函數里

LR is at chrdev_open+0x14c/0x164
LR寄存器的值

pc = 0xbf000018

pc : [<bf000018>]    lr : [<c008d888>]    psr: a0000013
sp : c3c7be88  ip : c3c7be98  fp : c3c7be94
r10: 00000000  r9 : c3c7a000  r8 : c049abc0
r7 : 00000000  r6 : 00000000  r5 : c3e740c0  r4 : c06d41e0
r3 : bf000000  r2 : 56000050  r1 : bf000964  r0 : 00000000
執行這條導致錯誤的指令時各個寄存器的值

Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 33eb0000  DAC: 00000015
Process firstdrvtest (pid: 777, stack limit = 0xc3c7a258)
發生錯誤時當前進程的名稱是firstdrvtest

棧
Stack: (0xc3c7be88 to 0xc3c7c000)
be80:                   c3c7bebc c3c7be98 c008d888 bf000010 00000000 c049abc0 
bea0: c3e740c0 c008d73c c0474e20 c3e766a8 c3c7bee4 c3c7bec0 c0089e48 c008d74c 
bec0: c049abc0 c3c7bf04 00000003 ffffff9c c002c044 c3d10000 c3c7befc c3c7bee8 
bee0: c0089f64 c0089d58 00000000 00000002 c3c7bf68 c3c7bf00 c0089fb8 c0089f40 
bf00: c3c7bf04 c3e766a8 c0474e20 00000000 00000000 c3eb1000 00000101 00000001 
bf20: 00000000 c3c7a000 c04a7468 c04a7460 ffffffe8 c3d10000 c3c7bf68 c3c7bf48 
bf40: c008a16c c009fc70 00000003 00000000 c049abc0 00000002 bec1fee0 c3c7bf94 
bf60: c3c7bf6c c008a2f4 c0089f88 00008520 bec1fed4 0000860c 00008670 00000005 
bf80: c002c044 4013365c c3c7bfa4 c3c7bf98 c008a3a8 c008a2b0 00000000 c3c7bfa8 
bfa0: c002bea0 c008a394 bec1fed4 0000860c 00008720 00000002 bec1fee0 00000001 
bfc0: bec1fed4 0000860c 00008670 00000002 00008520 00000000 4013365c bec1fea8 
bfe0: 00000000 bec1fe84 0000266c 400c98e0 60000010 00008720 00000000 00000000 

Backtrace: (回溯)
[<bf000000>] (first_drv_open+0x0/0x3c [first_drv]) from [<c008d888>] (chrdev_open+0x14c/0x164)
[<c008d73c>] (chrdev_open+0x0/0x164) from [<c0089e48>] (__dentry_open+0x100/0x1e8)
 r8:c3e766a8 r7:c0474e20 r6:c008d73c r5:c3e740c0 r4:c049abc0
[<c0089d48>] (__dentry_open+0x0/0x1e8) from [<c0089f64>] (nameidata_to_filp+0x34/0x48)
[<c0089f30>] (nameidata_to_filp+0x0/0x48) from [<c0089fb8>] (do_filp_open+0x40/0x48)
 r4:00000002
[<c0089f78>] (do_filp_open+0x0/0x48) from [<c008a2f4>] (do_sys_open+0x54/0xe4)
 r5:bec1fee0 r4:00000002
[<c008a2a0>] (do_sys_open+0x0/0xe4) from [<c008a3a8>] (sys_open+0x24/0x28)
[<c008a384>] (sys_open+0x0/0x28) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
Code: e24cb004 e59f1024 e3a00000 e5912000 (e5923000) 
Segmentation fault
# 

b. 編入內核
Modules linked in:
CPU: 0    Not tainted  (2.6.22.6 #2)
PC is at first_drv_open+0x18/0x3c
LR is at chrdev_open+0x14c/0x164
pc : [<c014e6c0>]    lr : [<c008638c>]    psr: a0000013
sp : c3a03e88  ip : c3a03e98  fp : c3a03e94
r10: 00000000  r9 : c3a02000  r8 : c03f3c60
r7 : 00000000  r6 : 00000000  r5 : c38a0c50  r4 : c3c1e780
r3 : c014e6a8  r2 : 56000050  r1 : c031a47c  r0 : 00000000
Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 339f0000  DAC: 00000015
Process firstdrvtest (pid: 750, stack limit = 0xc3a02258)

1. 根據pc值確定該指令屬於內核還是外加的模塊
pc=c014e6c0 屬於內核(看System.map)

2. 反匯編內核: arm-linux-objdump -D vmlinux > vmlinux.dis
在dis文件里搜c014e6c0
c014e6a8 <first_drv_open>:
c014e6a8:       e1a0c00d        mov     ip, sp
c014e6ac:       e92dd800        stmdb   sp!, {fp, ip, lr, pc}
c014e6b0:       e24cb004        sub     fp, ip, #4      ; 0x4
c014e6b4:       e59f1024        ldr     r1, [pc, #36]   ; c014e6e0 <.text+0x1276e0>
c014e6b8:       e3a00000        mov     r0, #0  ; 0x0
c014e6bc:       e5912000        ldr     r2, [r1]
c014e6c0:       e5923000        ldr     r3, [r2] // 在此出錯 r2=56000050



3. 根據棧信息分析函數調用過程
# ./firstdrvtest on
Unable to handle kernel paging request at virtual address 56000050
pgd = c3e78000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: first_drv
CPU: 0    Not tainted  (2.6.22.6 #48)
PC is at first_drv_open+0x18/0x3c [first_drv]
LR is at chrdev_open+0x14c/0x164
pc : [<bf000018>]    lr : [<c008c888>]    psr: a0000013
3.1 根據PC確定出錯位置
bf000018 屬於 insmod的模塊
bf000000 t first_drv_open       [first_drv]

3.2 確定它屬於哪個函數
反匯編first_drv.ko






sp : c3e69e88  ip : c3e69e98  fp : c3e69e94
r10: 00000000  r9 : c3e68000  r8 : c0490620
r7 : 00000000  r6 : 00000000  r5 : c3e320a0  r4 : c06a8300
r3 : bf000000  r2 : 56000050  r1 : bf000964  r0 : 00000000
Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 33e78000  DAC: 00000015
Process firstdrvtest (pid: 752, stack limit = 0xc3e68258)
Stack: (0xc3e69e88 to 0xc3e6a000)
9e80:                   c3e69ebc c3e69e98 c008c888 bf000010 00000000 c0490620 
                        first_drv_open'sp    lr             chrdev_open'sp

9ea0: c3e320a0 c008c73c c0465e20 c3e36cb4 c3e69ee4 c3e69ec0 c0088e48 c008c74c 
                                                            lr    
                                                                                 
9ec0: c0490620 c3e69f04 00000003 ffffff9c c002b044 c06e0000 c3e69efc c3e69ee8 
      __dentry_open'sp

9ee0: c0088f64 c0088d58 00000000 00000002 c3e69f68 c3e69f00 c0088fb8 c0088f40 
      lr                nameidata_to_filp'sp                lr
      
9f00: c3e69f04 c3e36cb4 c0465e20 00000000 00000000 c3e79000 00000101 00000001 
      do_filp_open'sp

9f20: 00000000 c3e68000 c04c1468 c04c1460 ffffffe8 c06e0000 c3e69f68 c3e69f48 
9f40: c008916c c009ec70 00000003 00000000 c0490620 00000002 be94eee0 c3e69f94 
9f60: c3e69f6c c00892f4 c0088f88 00008520 be94eed4 0000860c 00008670 00000005 
               lr                do_sys_open'sp

9f80: c002b044 4013365c c3e69fa4 c3e69f98 c00893a8 c00892b0 00000000 c3e69fa8 
                                          lr                sys_open'sp

9fa0: c002aea0 c0089394 be94eed4 0000860c 00008720 00000002 be94eee0 00000001 
      lr                ret_fast_syscall'sp
                        
9fc0: be94eed4 0000860c 00008670 00000002 00008520 00000000 4013365c be94eea8 
9fe0: 00000000 be94ee84 0000266c 400c98e0 60000010 00008720 00000000 00000000 


三. 自制工具
寄存器編輯器


四. 修改內核來定位系統僵死問題
./firstdrvtest on 
asm_do_IRQ => s3c2410_timer_interrupt : pid = 752, task name = firstdrvtest
pc = bf000084
asm_do_IRQ => s3c2410_timer_interrupt : pid = 752, task name = firstdrvtest
pc = bf000084   // 對於中斷, pc-4才是發生中斷瞬間的地址
/proc/kallsyms                                
first_drv.dis
00000000 <first_drv_open>:                     bf000000 t first_drv_open    [first_drv]         
0000003c <first_drv_write>:
  3c:    e1a0c00d     mov    ip, sp
  40:    e92dd800     stmdb    sp!, {fp, ip, lr, pc}
  44:    e24cb004     sub    fp, ip, #4    ; 0x4
  48:    e24dd004     sub    sp, sp, #4    ; 0x4
  4c:    e3cd3d7f     bic    r3, sp, #8128    ; 0x1fc0
  50:    e3c3303f     bic    r3, r3, #63    ; 0x3f
  54:    e5933008     ldr    r3, [r3, #8]
  58:    e0910002     adds    r0, r1, r2
  5c:    30d00003     sbcccs    r0, r0, r3
  60:    33a03000     movcc    r3, #0    ; 0x0
  64:    e3530000     cmp    r3, #0    ; 0x0
  68:    e24b0010     sub    r0, fp, #16    ; 0x10
  6c:    1a00001c     bne    e4 <init_module+0x5c>
  70:    ebfffffe     bl    70 <first_drv_write+0x34>
  74:    ea00001f     b    f8 <init_module+0x70>
  78:    e3520000     cmp    r2, #0    ; 0x0
  7c:    11a01002     movne    r1, r2
  80:    1bfffffe     blne    80 <first_drv_write+0x44>       // 卡死的地方
  84:    ea00001f     b    108 <init_module+0x80>

 


免責聲明!

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



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