oops_根據epc定位linux_kernel_panic位置

韓大衛@吉林師範大學html

2014.12.10linux

轉載請代表出處編程

*****************************************************函數

    關於內核報錯 「Unable to handle kernel paging request at virtual address」 的問題, 絕大多數都是因爲程序使用了不可用的指針而引發的, 定位這類問題的辦法很簡單,也但願個人描述足夠簡單實用. 工具

以我下面的一個實例說明:spa

 

epc :exception program counter  , 異常程序計數器,  ra : return address 返回地址.net

咱們能夠根據 「CPU 0 Unable to handle kernel paging request at virtual address 0000000000000078, epc == ffffffff805e96e8, ra == ffffffff80ec73d0」  找到epc的具體位置,  再根據具體的彙編程序定位出引發epc的具體緣由, 另外, 若有必須知道所有的調用路徑. 那麼重複定位epc的方法,根據call trace 逐步定位便可. 3d

在編譯linux 的時候, 會產生一個System.map, vmlinux, 以及vmlinux.o指針

咱們使用System.map 和vmlinux.o 便可. 由於vmlinux多是被特定壓縮工具壓縮過的(根據您的makefile), 沒法使用objdump工具作反彙編. htm

先打開System.map:

將 epc == ffffffff805e96e8 裏的 ffffffff805e96e8 地址拷貝下來,  直接在System.map 裏面查找, 若是沒有找到, 那麼將ffffffff805e96e8 最後兩位刪掉, 即ffffffff805e96,  查找這個地址, 絕大多數狀況均可以找到, 個人以下:  


看來問題是出在 add_mtd_device 這個函數裏面. 

epc 的位置是在ffffffff805e96e8,  add_mtd_device 的地址是在ffffffff805e96c0 , 那麼應該是在add_mtd_device裏的ffffffff805e96e8 - ffffffff805e96c0 = 0x28 這個偏移位置出了問題. 

 如今咱們須要觀察 vmlinux.o 的彙編代碼, 找到 add_mtd_device 函數的彙編,  觀察 0x28 位置的彙編語言. 

先使用xxx-objdump(xxx爲具體的交叉編譯工具前綴)  將vmlinux.o 反彙編出來, 個人作法是:

mips64-octeon-linux-gnu-objdump -dr vmlinux.o >> linux-dr

以後打開linux-dr 這個文件,  找到 add_mtd_device 的定義處: 

能夠看到, 0x28 位置的彙編:

28:   dc820078    ld  v0,120(a0)

ld v0, 120(a0) 的含義是:

先取寄存器a0的數值的地址, 再將該地址後120字節處的數值加載到v0 寄存器.

(a0)是取a0寄存器的地址, a0是負責傳遞函數的第一個參數的寄存器.

dc820078  就是ld v0, 120(a0) 對應的機器碼.  

根據CPU 0 Unable to handle kernel paging request at virtual address 0000000000000078 這句話的提示能夠知道,  

是在對a0的0x78(120) 地址取值的時候發生了錯誤,  極可能是a0地址自己不可用. 若是能確認的話, 就能夠證實add_mtd_device的第一個參數使用一個不可用的指針.  

這時候就能夠檢查源代碼,  相信您有能力很快到定位問題. 

但若是該函數很大,  不容易定位,  那麼咱們能夠經過120這個信息定位到該函數裏具體的語句. 

個人實例:

打開linux內核源代碼,  計算120字節在add_mtd_device() 第一個參數類型裏的位置, 獲得參數成員:

vi -t  add_mtd_device

以下圖: 

 


找到第一個參數的類型struct mtd_info 定義, 經過逐步計算每一個成員偏移(注意填充字節),  

能夠算出第120字節的成員爲backing_dev_info. 那麼, 在代碼裏, 出現epc的程序就是第一個出現該成員的地方. 

若是這個偏移太大, 很難計算的話, 不妨在代碼裏, 在調用該函數前自定義一個該參數類型的變量, 

估計一個大概的成員, 計算他們的偏移, 在知道這個成員偏移量的基礎上, 再計算120的成員位置. 會容易一些. 

 

個人作法是:

struct mtd_info my = {0};  

unsigned long len = (unsigned long)&(my.backing_dev_info) - (unsigned long)&my;

printk("sizeof is = 0x%lu\n", len);

固然, 這須要重啓設備, 並load新編譯的linux. 

 

關於定位epc 位置, 總結一下:

1, 打開System.map, 找到epc以前的最近函數的地址.計算出epc距離該函數的偏移值. 

2, 使用objdump 找到該函數, 分析 epc 偏移處的彙編代碼. 

3, 打開源代碼, 根據分析彙編代碼獲得的信息進行定位. 

 

轉自:https://blog.csdn.net/han_dawei/article/details/41846055

相關文章
相關標籤/搜索