1. 程式人生 > >嵌入式Linux——oops:根據oops資訊,找到錯誤的產生位置以及函式的呼叫關係

嵌入式Linux——oops:根據oops資訊,找到錯誤的產生位置以及函式的呼叫關係

簡介:

    本文主要介紹通過oops資訊找到程式中出錯位置的方法。並結合自己程式碼中的錯誤來講解如何找到出錯位置。同時還會介紹使用棧資訊來推到函式間的呼叫關係。

 Linux核心:linux-2.6.22.6

 所用開發板:JZ2440 V3(S3C2440A)

宣告:

    本文主要是對韋東山老師視訊的總結,同時看了一些網友的博文來對這方面的資訊進行補充。希望通過我的文章讓你對oops資訊有更好的瞭解。

oops資訊介紹:

    我們先以一個例子來介紹oops中都含有什麼資訊。我們看下面這幾部分:

1  一段文字描述資訊,用於描述程式出錯的原因:
Unable to handle kernel paging request at virtual address 56000050

2  Oops 資訊的序號


Internal error: Oops: 5 [#1]
 bit 0 如果第0位被清0,則異常是由一個不存在的頁所引起的;否則是由無效的訪問許可權引起的。
 bit 1 如果第1位被清0,則異常由讀訪問或者執行訪問所引起;否則異常由寫訪問引起。
 bit 2 如果第2位被清0,則異常發生在核心態;否則異常發生在使用者態。
Oops中的 [#1] crash發生次數。
Oops中的 PREEMPT 是指系統支援搶佔模式,有時會還會輸出SMP(多核) ARM/THUMB(指令集)等資訊。

3 核心中載入的模組名稱,也可能沒有,以下面字樣開頭。
Modules linked in: first_drv

4 發生錯誤的 CPU 的序號,對於單處理器的系統,序號為 0,比如:

CPU: 0    Not tainted  (2.6.22.6 #1)

其中Tainted的表示可以從核心中 kernel/panic.c 中找到:

Tainted 描述
‘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.
‘F’ if any module was force loaded by “insmod -f”.
‘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.
‘R’ if a module was force unloaded by “rmmod -f”.
‘M’ if any processor has reported a Machine Check Exception.
‘B’ if a page-release function has found a bad page reference or some unexpected page flags.
‘U’ if a user or user application specifically requested that the Tainted flag be set.
‘D’ if the kernel has died recently, i.e. there was an OOPS or BUG.
‘W’ if a warning has previously been issued by the kernel.
‘C’ if a staging module / driver has been loaded.
‘I’ if the kernel is working around a sever bug in the platform’s firmware (BIOS or similar).

5 發生錯誤時 CPU 的各個暫存器值。
PC is at first_drv_open+0x18/0x3c [first_drv]
LR is at chrdev_open+0x14c/0x164
pc : [<bf000018>]    lr : [<c008d888>]    psr: a0000013
sp : c3ed5e88  ip : c3ed5e98  fp : c3ed5e94
r10: 00000000  r9 : c3ed4000  r8 : c049a300
r7 : 00000000  r6 : 00000000  r5 : c3e700c0  r4 : c06a4540
r3 : bf000000  r2 : 56000050  r1 : bf000964  r0 : 00000000

不同的系統中提示的可能有所不同, 不同架構對 PC/IP 暫存器的叫法不同

PC is at first_drv_open+0x18/0x3c [first_drv]

或者

EIP : first_drv_open+0x18/0x3c [first_drv]
告訴我們核心是執行到first_drv_open+0x18/0x3c [first_drv] 這個地址處出錯的, 那麼我們所需要做的就是找到這個地址對應的程式碼格式為 函式+偏移/長度
first_drv_open指示了在first_drv_open中出現的異常
0x18表示出錯的偏移位置
0x3c 表示first_drv_open函式的大小

6 當前程序的名字及程序 ID,比如:
Process firstdrvtest (pid: 783, stack limit = 0xc3ed4258)
這並不是說發生錯誤的是這個程序,而是表示發生錯誤時,當前程序是它。錯誤可能發生在核心程式碼、驅動程式,也可能就是這個程序的錯誤。

7 棧資訊。
Stack: (0xc3ed5e88 to 0xc3ed6000)
5e80:                   c3ed5ebc c3ed5e98 c008d888 bf000010 00000000 c049a300 
5ea0: c3e700c0 c008d73c c0474f20 c3e79724 c3ed5ee4 c3ed5ec0 c0089e48 c008d74c 
5ec0: c049a300 c3ed5f04 00000003 ffffff9c c002c044 c3cf4000 c3ed5efc c3ed5ee8 
5ee0: c0089f64 c0089d58 00000000 00000002 c3ed5f68 c3ed5f00 c0089fb8 c0089f40
5f00: c3ed5f04 c3e79724 c0474f20 00000000 00000000 c3edd000 00000101 00000001

8 棧回溯資訊,可以從中看出函式呼叫關係,形式如下:
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:c3e79724 r7:c0474f20 r6:c008d73c r5:c3e700c0 r4:c049a300
[<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:bed00edc 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)

要讓核心出錯時能夠列印棧回溯資訊,編譯核心時要增加“-fno-omit-frame-pointer”選項,這可以通過配置 CONFIG_FRAME_POINTER 來實現。檢視核心目錄下的配置檔案.config,確保 CONFIG_FRAME_POINTER 已經被定義,如果沒有,執行“make menuconfig”命令重新配置核心。CONFIG_FRAME_POINTER 有可能被其他配置項自動選上。

9 出錯指令附近的指令的機器碼,比如(出錯指令在小括號裡)

Code: e24cb004 e59f1024 e3a00000 e5912000 (e5923000) 

根據PC/IP值找到出錯的位置:

    我們主要通過PC/IP值來找到出錯的位置。這裡我們還需要其他的資訊輔助。我們先將判斷位置的方法寫出,然後我們使用這個方法從上面舉例的錯誤中找到導致錯誤的出處。

判斷步驟:

一   先通過PC/IP值判斷這個錯誤是核心函式中的錯誤還是使用insmod載入的驅動程式的錯誤:

二   假設是載入驅動程式引起的錯誤

    2.1  是載入驅動程式引起的錯誤,那麼就要確定是哪個驅動程式引起的錯誤。

    2.2  確定是哪個驅動引起的錯誤之後我們就要反彙編這個驅動模組的ko檔案,得到dis檔案。

    2.3  分析反彙編得到的dis檔案,來確定引起錯誤的語句。

    2.4  結合上面各個暫存器的值來確定具體是那條C語言語句引起的錯誤。

三   假設是核心函式引起的錯誤

    3.1   是核心函式引起的錯誤,那麼反彙編核心檔案,得到dis檔案

    3.2   在核心的反彙編檔案中以PC/IP值進行搜尋,得到出錯的函式和出錯的語句。

    3.3    結合上面各個暫存器的值來確定具體是那條C語言語句引起的錯誤。

    好了,有了上面的方法我們現在就以上一個錯誤的oops資訊為例,來找出是哪裡出了錯誤。

# ./firstdrvtest   
Unable to handle kernel paging request at virtual address 56000050
pgd = c3edc000
[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]
LR is at chrdev_open+0x14c/0x164
pc : [<bf000018>]    lr : [<c008d888>]    psr: a0000013
sp : c3ed5e88  ip : c3ed5e98  fp : c3ed5e94
r10: 00000000  r9 : c3ed4000  r8 : c049a300
r7 : 00000000  r6 : 00000000  r5 : c3e700c0  r4 : c06a4540
r3 : bf000000  r2 : 56000050  r1 : bf000964  r0 : 00000000
Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 33edc000  DAC: 00000015
Process firstdrvtest (pid: 783, stack limit = 0xc3ed4258)
Stack: (0xc3ed5e88 to 0xc3ed6000)
5e80:                   c3ed5ebc c3ed5e98 c008d888 bf000010 00000000 c049a300
5ea0: c3e700c0 c008d73c c0474f20 c3e79724 c3ed5ee4 c3ed5ec0 c0089e48 c008d74c
5ec0: c049a300 c3ed5f04 00000003 ffffff9c c002c044 c3cf4000 c3ed5efc c3ed5ee8 
5ee0: c0089f64 c0089d58 00000000 00000002 c3ed5f68 c3ed5f00 c0089fb8 c0089f40
5f00: c3ed5f04 c3e79724 c0474f20 00000000 00000000 c3edd000 00000101 00000001
5f20: 00000000 c3ed4000 c046de08 c046de00 ffffffe8 c3cf4000 c3ed5f68 c3ed5f48 
5f40: c008a16c c009fc70 00000003 00000000 c049a300 00000002 bed00edc c3ed5f94 
5f60: c3ed5f6c c008a2f4 c0089f88 00008520 bed00ed4 0000860c 00008670 00000005 
5f80: c002c044 4013365c c3ed5fa4 c3ed5f98 c008a3a8 c008a2b0 00000000 c3ed5fa8 
5fa0: c002bea0 c008a394 bed00ed4 0000860c 00008720 00000002 bed00edc 00000001 
5fc0: bed00ed4 0000860c 00008670 00000001 00008520 00000000 4013365c bed00ea8 
5fe0: 00000000 bed00e84 0000266c 400c98e0 60000010 00008720 4021a2cc 4021a2dc 
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:c3e79724 r7:c0474f20 r6:c008d73c r5:c3e700c0 r4:c049a300
[<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:bed00edc 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) 

    從中我們發現這裡的pc 為 bf000018,那麼我們如何判斷他是屬於核心還是屬於載入模組引發的錯誤那?

    我們要通過核心目錄下的System.map檔案來確定核心函式的地址範圍。如果上面的PC值在System.map檔案中核心函式的範圍之內,那麼這個錯誤就是有核心函式引起的,否則就是由載入模組引起的。而我的System.map中核心函式的範圍是:

c0004000 A swapper_pg_dir
c0008000 T __init_begin
c0008000 T _sinittext
c0008000 T stext
c0008000 T _stext
·············
c03cdb44 b ratelimit.1
c03cdb48 b registered_mechs_lock
c03cdb48 b rsi_table
c03cdc48 b rsc_table
c03cec48 B krb5_seq_lock
c03cec4c b i.0
c03cec54 B _end

    c0004000~c03cec54,而我的PC值為bf000018,所以不是核心函式引起的錯誤,而是載入模組引起的錯誤,同時在核心模型中一般有:

地址

作用

說明

>=0xc000 0000

核心虛擬儲存器

使用者程式碼不可見區域

<0xc000 0000

Stack(使用者棧)

ESP指向棧頂

空閒記憶體

>=0x4000 0000

檔案對映區

<0x4000 0000

空閒記憶體

Heap(執行時堆)

通過brk/sbrk系統呼叫擴大堆,向上增長。

.data、.bss(讀寫段)

從可執行檔案中載入

>=0x0804 8000

.init、.text、.rodata(只讀段)

從可執行檔案中載入

<0x0804 8000

保留區域

    下一步我們就要確定是哪個驅動模組引起的錯誤,當然我們上面的oops資訊中已經告訴我們:

PC is at first_drv_open+0x18/0x3c [first_drv]

    是first_drv驅動引入的錯誤,那如果我們沒有上面的提示資訊該怎麼辦啊?

   我們要去看核心的/proc/kallsyms 檔案,從中找到與PC值相近的值的所在的函式,最好是比PC值要小一些。我們使用命令:cat /proc/kallsyms > /kallsyms.txt將/proc/kallsyms檔案的內容放到一個TXT檔案中。我們看這個檔案中的資訊:

    從上面紅色邊框中我們看出,這個bf000000與我們的PC值bf000018十分相近。同時我們觀察發現PC值包含在first_drv的函式值中,所以在此可以確定我們出錯的模組為first_drv模組。同時我們從紅色方框中發現bf000000位置對應的是first_drv_open函式。

    下面我們就要進行下一步:反彙編該模組的ko檔案。在這裡我們使用:arm-linux-objdump -D first_drv.ko > first_drv.dis 命令來將first_drv的ko檔案轉化為其反彙編的dis檔案。

    那麼下面我們就要對比PC值與反彙編檔案了:

dis檔案中的函式和地址 iinsmod載入檔案中的函式和地址
00000000 <first_drv_open>: bf000000 t first_drv_open
00000018 (出錯的行) bf000018

    通過上面的分析我們就可以找到出錯的行在哪裡了:

00000000 <first_drv_open>:
   0:	e1a0c00d 	mov	ip, sp
   4:	e92dd800 	stmdb	sp!, {fp, ip, lr, pc}
   8:	e24cb004 	sub	fp, ip, #4	; 0x4
   c:	e59f1024 	ldr	r1, [pc, #36]	; 38 <__mod_vermagic5>
  10:	e3a00000 	mov	r0, #0	; 0x0
  14:	e5912000 	ldr	r2, [r1]
  18:	e5923000 	ldr	r3, [r2]  //這裡出錯了

    不過這裡是彙編語句,我們要想將這個這裡的彙編語句對應到我們的C語句就要藉助於其他的資訊了。例如我們這時各個暫存器中的值:

pc : [<bf000018>]    lr : [<c008d888>]    psr: a0000013
sp : c3ed5e88  ip : c3ed5e98  fp : c3ed5e94
r10: 00000000  r9 : c3ed4000  r8 : c049a300
r7 : 00000000  r6 : 00000000  r5 : c3e700c0  r4 : c06a4540
r3 : bf000000  r2 : 56000050  r1 : bf000964  r0 : 00000000

    通過上面的暫存器值,並結合我們的組合語言知識我們不難將錯誤找出。而我們這裡其實就是在對開發板暫存器操作時,沒有使用ioremap函式引起的。

核心引起錯誤:

    我將上面這個有錯誤的檔案放到核心驅動中的char資料夾下,並修改相應的Makefile檔案 vi /drivers/char/Makefile。在其中加入: obj-y += first_drv.o 。然後我們重新編譯核心得到新的uImage,我們載入新的uImage。這時候這個錯誤就是核心引起的了。

    這時候我們看oops資訊發現這時候的PC值變為了:c014e6c0了。而我們System.map中核心函式的範圍是:c0004000 ~ c03cec54。而我們這個時候要使用命令:arm-linux-objdump -D vmlinux > vmlinux.dis 來得到核心的反彙編檔案。然後我們在檔案中找PC值對應的行,從中我們就可以確定是在哪裡出處,我們結合暫存器相關的資訊就可以找出出錯的位置了。

通過棧資訊確定函式呼叫關係:

    我們以上面驅動模組中出錯的oops資訊來分析函式的呼叫關係。我們知道其實上面已經有了這個函式呼叫關係,那就是

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:c3e79724 r7:c0474f20 r6:c008d73c r5:c3e700c0 r4:c049a300
[<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:bed00edc 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) 

    從上面我們可以看出出錯函式的呼叫關係,但是如果我們的oops中沒有Backtrace怎麼辦啊?這時候我們就要通過棧資訊自己來推出函式呼叫關係了。這裡我們需要核心的反彙編檔案,因為我們是核心中的函式呼叫驅動模組中的函式。

    而在講解如何推出呼叫關係之前,我想先介紹一下我們要用到的暫存器:

1. 暫存器R14被稱為連結暫存器(LR),存放每種模式下,當前子程式的返回地址或者發生異常中斷的時候,將R14設定成異常模式將要返回的地址。

2. 暫存器R13(SP),通常用作堆疊指標,每一種模式都有自己的物理R13,程式初始化R13。當進入該模式時,可以將要使用的暫存器儲存在R13所指的棧中,當退出時,將彈出,從而實現了現場保護。

    我們現在假設有三個函式A函式,B函式,C函式。他們的呼叫關係為C函式呼叫B函式,而B函式呼叫A函式。我們可以以虛擬碼的形式表達為:

C(void){
	B();
	其他程式碼;
}

B(void){
	A();
	其他程式碼;
}

A(void){
	其他程式碼;
}

    而他對應的呼叫關係圖為:

    從上面的圖中我們知道,我們的C函式呼叫B函式,在進入B函式後,B函式會將C函式的LR壓入棧中,當B函式執行完成後,他會呼叫LR的值返回到C函式中。同樣在B函式中呼叫A函式,進入A函式後先將B函式的LR壓入棧中,當A函式完成後,他會呼叫LR的值返回B函式。

    而現在我們知道了A函式,並且知道了A函式棧中B函式的LR值。那麼我們就可以找到B函式。而找到B函式後我們知道B函式棧中LR的值我們就可以找到C函式。我們以此類推就可以找到函式的呼叫關係了。我們就是利用這個原理來從這些棧資訊中找到呼叫關係。

    好了,有了上面的知識我們現在再看我們oops中的棧資訊:

Stack: (0xc3ed5e88 to 0xc3ed6000)
5e80:                   c3ed5ebc c3ed5e98 c008d888 bf000010 00000000 c049a300 
5ea0: c3e700c0 c008d73c c0474f20 c3e79724 c3ed5ee4 c3ed5ec0 c0089e48 c008d74c 
5ec0: c049a300 c3ed5f04 00000003 ffffff9c c002c044 c3cf4000 c3ed5efc c3ed5ee8 
5ee0: c0089f64 c0089d58 00000000 00000002 c3ed5f68 c3ed5f00 c0089fb8 c0089f40 
5f00: c3ed5f04 c3e79724 c0474f20 00000000 00000000 c3edd000 00000101 00000001 
5f20: 00000000 c3ed4000 c046de08 c046de00 ffffffe8 c3cf4000 c3ed5f68 c3ed5f48 
5f40: c008a16c c009fc70 00000003 00000000 c049a300 00000002 bed00edc c3ed5f94 
5f60: c3ed5f6c c008a2f4 c0089f88 00008520 bed00ed4 0000860c 00008670 00000005 
5f80: c002c044 4013365c c3ed5fa4 c3ed5f98 c008a3a8 c008a2b0 00000000 c3ed5fa8 
5fa0: c002bea0 c008a394 bed00ed4 0000860c 00008720 00000002 bed00edc 00000001 
5fc0: bed00ed4 0000860c 00008670 00000001 00008520 00000000 4013365c bed00ea8 
5fe0: 00000000 bed00e84 0000266c 400c98e0 60000010 00008720 4021a2cc 4021a2dc 

    我們通過上面的分析知道我們載入模組的出錯函式為first_drv_open,所以我們看他的反彙編程式碼:

00000000 <first_drv_open>:
   0:	e1a0c00d 	mov	ip, sp
   4:	e92dd800 	stmdb	sp!, {fp, ip, lr, pc}
   8:	e24cb004 	sub	fp, ip, #4	; 0x4
   c:	e59f1024 	ldr	r1, [pc, #36]	; 38 <__mod_vermagic5>
  10:	e3a00000 	mov	r0, #0	; 0x0
  14:	e5912000 	ldr	r2, [r1]
  18:	e5923000 	ldr	r3, [r2]

    從上面的程式碼我們看出對棧的操作只有:e92dd800 stmdb sp!, {fp, ip, lr, pc},而在棧裡面他是高暫存器在高位,所以PC值在最高位,而LR次之,接下來依次為IP和FP。而他佔用四個棧資訊值。而在其中LR為倒數第二個。所以在上面的棧資訊中有:

5e80:                   c3ed5ebc c3ed5e98 c008d888 bf000010 00000000 c049a300 
			<first_drv_open>: lr		    caller'sp

    同時我們知道LR中存放的就是上一個呼叫函式的返回地址,所以我們可以通過first_drv_open的LR值c008d888找到他的呼叫函式。我們在核心的反彙編檔案中搜c008d888這個地址,看他屬於那個函式:

c008d73c <chrdev_open>:
c008d73c:	e1a0c00d 	mov	ip, sp
c008d740:	e92dd9f0 	stmdb	sp!, {r4, r5, r6, r7, r8, fp, ip, lr, pc}
c008d744:	e24cb004 	sub	fp, ip, #4	; 0x4
c008d748:	e24dd004 	sub	sp, sp, #4	; 0x4
c008d74c:	e59040e4 	ldr	r4, [r0, #228]
·······
c008d874:	0a000006 	beq	c008d894 <chrdev_open+0x158>
c008d878:	e1a00005 	mov	r0, r5
c008d87c:	e1a01008 	mov	r1, r8
c008d880:	e1a0e00f 	mov	lr, pc
c008d884:	e1a0f003 	mov	pc, r3
c008d888:	e2507000 	subs	r7, r0, #0	; 0x0
c008d88c:	11a00004 	movne	r0, r4
······

    從上面我們知道first_drv_open的呼叫函式為chrdev_open函式,同時我們還從上面的反彙編程式碼中看出在chrdev_open函式中對棧SP的操作只有:

c008d740:	e92dd9f0 	stmdb	sp!, {r4, r5, r6, r7, r8, fp, ip, lr, pc}
c008d748:	e24dd004 	sub	sp, sp, #4	; 0x4

    這裡面一共移動棧10個位置,其中下面sub sp, sp, #4 ; 0x4 因為這是32位的系統,所以4位元組表示一個棧地址。同時LR資訊同樣在倒數第二位。而對應到上面的棧資訊中:

5e80:                   c3ed5ebc c3ed5e98 c008d888 bf000010 00000000 c049a300 
			<first_drv_open>: lr		    <chrdev_open>

5ea0: c3e700c0 c008d73c c0474f20 c3e79724 c3ed5ee4 c3ed5ec0 c0089e48 c008d74c 
                                                             lr
5ec0: c049a300 c3ed5f04 00000003 ffffff9c c002c044 c3cf4000 c3ed5efc c3ed5ee8 
      caller'sp

    下面我們就要在核心的反彙編檔案中找c0089e48對應的函數了:

c0089d48 <__dentry_open>:
c0089d48:	e1a0c00d 	mov	ip, sp
c0089d4c:	e92dddf0 	stmdb	sp!, {r4, r5, r6, r7, r8, sl, fp, ip, lr, pc}
c0089d50:	e24cb004 	sub	fp, ip, #4	; 0x4
······
c0089e40:	e1a0e00f 	mov	lr, pc
c0089e44:	e1a0f006 	mov	pc, r6
c0089e48:	e250a000 	subs	sl, r0, #0	; 0x0
c0089e4c:	1a000019 	bne	c0089eb8 <__dentry_open+0x170>
c0089e50:	e5943018 	ldr	r3, [r4, #24]
······

    從上面看chrdev_open函式由__dentry_open函式呼叫,同時我們還知道了棧操作資訊:

c0089d4c:	e92dddf0 	stmdb	sp!, {r4, r5, r6, r7, r8, sl, fp, ip, lr, pc}

    那麼通過上面的資訊我們就可以知道__dentry_open的呼叫函數了。這樣以此類推我們就知道核心中對first_drv_open函式的呼叫關係了。這裡我將他們全部的關係貼出:

Stack: (0xc3ed5e88 to 0xc3ed6000)
5e80:                   c3ed5ebc c3ed5e98 c008d888 bf000010 00000000 c049a300 
		        <first_drv_open>: lr		    <chrdev_open>
5ea0: c3e700c0 c008d73c c0474f20 c3e79724 c3ed5ee4 c3ed5ec0 c0089e48 c008d74c 
							     lr
5ec0: c049a300 c3ed5f04 00000003 ffffff9c c002c044 c3cf4000 c3ed5efc c3ed5ee8 
      <__dentry_open>
5ee0: c0089f64 c0089d58 00000000 00000002 c3ed5f68 c3ed5f00 c0089fb8 c0089f40
       lr		<nameidata_to_filp>		     lr
5f00: c3ed5f04 c3e79724 c0474f20 00000000 00000000 c3edd000 00000101 00000001
      <do_filp_open>
5f20: 00000000 c3ed4000 c046de08 c046de00 ffffffe8 c3cf4000 c3ed5f68 c3ed5f48 
5f40: c008a16c c009fc70 00000003 00000000 c049a300 00000002 bed00edc c3ed5f94 
5f60: c3ed5f6c c008a2f4 c0089f88 00008520 bed00ed4 0000860c 00008670 00000005 
		lr		 <do_sys_open>:
5f80: c002c044 4013365c c3ed5fa4 c3ed5f98 c008a3a8 c008a2b0 00000000 c3ed5fa8 
   					   lr	 	    <sys_open> 
5fa0: c002bea0 c008a394 bed00ed4 0000860c 00008720 00000002 bed00edc 00000001 
       lr								
5fc0: bed00ed4 0000860c 00008670 00000001 00008520 00000000 4013365c bed00ea8 
5fe0: 00000000 bed00e84 0000266c 400c98e0 60000010 00008720 4021a2cc 4021a2dc 

    而棧回溯資訊為:

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:c3e79724 r7:c0474f20 r6:c008d73c r5:c3e700c0 r4:c049a300
[<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:bed00edc 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)

    從上面可以看出這兩個是對應的。也證明我們的推導是正確的。

參考文章: