<form id="dlljd"></form>
        <address id="dlljd"><address id="dlljd"><listing id="dlljd"></listing></address></address>

        <em id="dlljd"><form id="dlljd"></form></em>

          <address id="dlljd"></address>
            <noframes id="dlljd">

              聯系我們 - 廣告服務 - 聯系電話:
              您的當前位置: > 關注 > > 正文

              什么是Oops?linux之Oops原理及解析

              來源:CSDN 時間:2023-03-10 15:05:19

              前言

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


              (資料圖片)

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

              1. Oops的產生

              挑選一位隨機幸運內核,insmod oops.ko產生如下標準打印,產生了一段如下圖打印: Oops 信息包含以下幾部分內容:

              一段文本描述信息。 比如類似“Unable to handle kernel NULL pointer dereference at virtual address 00000000”的信息,它說明了發生的是哪類錯誤Oops 信息的序號 比如是第 1 次、第 2 次等。這些信息與下面類似,中括號內的數據表示序號。 Internal error: Oops: 817 [#1] PREEMPT SMP ARM內核中加載的模塊名稱(也可能沒有),以下面字樣開頭 Modules linked in:xxx發生錯誤的 CPU 的序號,對于單處理器的系統,序號為 0 CPU: 1 PID: 1412 Comm: insmod Tainted: P O 4.9.37 #1 下圖是關于Tainted(污染)后面字段具體含義(可以注意到,第3部分加載的模塊后面有些模塊帶有(PO)等字樣,實際上就是和這里是相同的含義),源碼路徑: \kernel\panic.c

              /** *  print_tainted - return a string to represent the kernel taint state. * *  "P" - Proprietary module has been loaded.(沒有模塊MODULE_LICENSE或者帶有insmod認為是與GPL不相容的的MODULE_LICENSE的模塊被認定是專有的) *  "F" - Module has been forcibly loaded.(通過“insmod -f”被強制裝載的模塊) *  "S" - SMP with CPUs not designed for SMP.(oops發生在SMP內核中,運行于沒有證明安全運行多處理器的硬件。 當前這種情況僅限于幾種不支持SMP的處理器) *  "R" - User forced a module unload.(rmmod –f強制卸載) *  "M" - System experienced a machine check exception.(機器檢查異常) *  "B" - System has hit bad_page.(頁釋放函數發現了一個錯誤的頁引用或者一些非預期的頁標志) *  "U" - Userspace-defined naughtiness. *  "D" - Kernel has oopsed before.(內核以前已經OOPS過了) *  "A" - ACPI table overridden. *  "W" - Taint on warning. *  "C" - modules from drivers/staging are loaded. *  "I" - Working around severe firmware bug. *  "O" - Out-of-tree module has been loaded.(樹外模塊加載) *  "E" - Unsigned module has been loaded.(未簽名模塊加載) *  "L" - A soft lockup has previously occurred.(發生過軟鎖定) *  "K" - Kernel has been live patched. * *  The string is overwritten by the next call to print_tainted().*/

              發生錯誤時 CPU 的各個寄存器值

              當前進程的名字及進程 ID Process insmod (pid: 1412, stack limit = 0x9eb8e210) 這并不是說發生錯誤的是這個進程,而是表示發生錯誤時,當前進程是它。錯誤可能發生在內核代碼、驅動程序,也可能就是這個進程的錯誤

              棧信息

              棧回溯信息,可以從中看出函數調用關系

              出錯指令附近的指令的機器碼(出錯指令在小括號里),也有可能沒有 關于錯誤碼,如下為armv7架構定義的FSR(錯誤狀態寄存器,分為DFSR和IFSR,根據不同處理器使用不同的FSR)的錯誤代碼,實際上源碼中,oops的錯誤碼就是通過匯編獲取的寄存器值,如下為手冊中IFSR獲取錯誤碼的方法(DFSR同樣) 如下為DFSR結構(IFSR關于FS碼是相同的) 對于上面0x817的錯誤碼解釋為:寫入內存時報錯,錯誤原因是:Translation fault 也就是頁表轉換出現問題

              從上可以大致知道Oops 可以看成是內核級的Segmentation Fault。應用程序如果進行了非法內存訪問或執行了非法指令,會得到Segfault信號,一般的行為是coredump,應用程序也可以自己截獲 Segfault信號,自行處理。如果內核自己犯了這樣的錯誤,則會打出Oops信息,也就是說Oops一般是由于內存原因導致的。

              2.源碼分析

              2.1 溯源過程C部分

              直接通過打印找到產生的對應代碼,oops的打印為__die函數(\arch\arm\kernel\traps.c)。 第265行打印就是oops信息,后邊三個字符串來自三個編譯開關,分別表示允許搶占,支持對稱多處理器,采用ARM指令。 第269行忽略,備注也寫的陷阱和錯誤數在ARM上幾乎沒有意義 第273行,打印加載的模組信息 第274行,打印寄存器信息(CPU號,任務名,污染原因,PC,LR(鏈接寄存器,保存函數返回的地址),SP(棧指針),IP,FP(棧頂指針),R10-R0等寄存器值,CPU的Flags (Flags后邊大寫字母表示相應的位為1,小寫表示為0)【指NZCV這幾個狀態寄存器】) 第275行,打印了當前出錯的進程名,pid值,和堆棧限制,在ARM平臺棧的增長方向是從高地址向低地址,sp指針是當前的棧頂,stack limit打印的是棧的限制,表示最小地址是多少,如果SP比這個值小,那么表示棧溢出了。 第279行之后打印堆棧和函數的調用回溯。 如下為pt_reg的定義 進一步溯源,深入探究oops源碼,調用__die的函數為die(\arch\arm\kernel\traps.c): 第344行:調用oops_begin,在這個地方關閉本CPU中斷,獲取CPUID, 對oops上鎖.如果同一個CPU已經在處理die了,那么就是嵌套die,不需要再獲取鎖了 第347行獲取cpu是不是處于用戶模式,如果不是用戶模式并且report_bug的返回值如果不等于BUG_TRAP_TYPE_NONE打印會變為”Oops - BUG”,如果是這種情況,就比較嚴重,一般會打印如下 第355行:die的最后是調用oops_end,這里邊的操作很多是和oops_begin相對應的,然后調用oops_exit,該函數會打印trace結束標志,調用kmsg_dump(KMSG_DUMP_OOPS)。但是如果oops產生在中斷過程中,oops_end函數會直接產生panic或者如果配置宏CONFIG_PANIC_ON_OOPS_VALUE的值為1(panic_on_oops),則也會直接panic 通常情況由于空指針或者錯誤的虛擬地址導致的oops,函數為:__do_kernel_fault。源碼位于\arch\arm\mm\fault.c 第138行:嘗試進行異常修復,這里有一套很復雜的內存異?;貜吞幚?,不深入,失敗后會繼續向下執行 第152行:執行完前面的die操作后,直接干掉出問題的進程 繼續溯源,可以找到在\arch\arm\mm\fault.c中發現兩個函數都有調用__do_kernel_fault。分別是do_bad_area和do_page_fault,這里先不具體分析其源碼,繼續溯源

              調用do_bad_area函數的有如下函數:do_alignment,do_translation_fault,do_sect_fault 調用do_page_fault函數的有:do_translation_fault 而最終匯總成如下該數組 最終由函數do_DataAbort調用

              2.2溯源過程匯編部分

              以下部分為匯編過程,并且涉及到部分內存申請流程。 當內核申請內存時,虛擬內存映射到實際物理內存,系統自動觸發缺頁中斷,缺頁中斷機制根據所訪問頁面的狀態來分配物理頁面并建立映射關系。觸發缺頁中斷的情況有兩種 , 第一,程序訪問了非法地址(我們主要分析的);第二,訪問的地址是合法的,但是該地址還未分配物理頁框。

              當程序訪問的虛擬頁面沒有進行過物理頁面的映射時,會通過發生缺頁中斷來分配和映射物理頁面。發生缺頁中斷時,處理器會跳轉到異常向量表 Data abort 向量中開始執行缺頁中斷的匯編階段,這個階段與處理器架構緊密聯系,例如對于ARMv7-A架構,匯編處理流程為:__vectors_start -> vector_dabt -> __dabt_usr/__dabt_svc -> dabt_helper -> v7_early_abort 如下為中斷向量表,源碼位于:arch\arm\kernel\entry-armv.S

              以svc為例,會調用dabt_helper 最后dabt_helper會bl到CPU_DABORT_HANDLER這個函數中,根據使用的架構不同,該函數使用的可能會不相同 如下使用的v7架構,使用函數為v7_early_abort v7_early_abort源碼位于:\arch\arm\mm\abort-ev7.S 這個函數實際上就是實現了從arm中獲取FSR(錯誤狀態寄存器)和FAR(錯誤地址寄存器,也就是要映射的地址),r0=地址,ri=錯誤碼,r2=pt_regs(在對應的__dabt_svc中已經獲取)

              2.3 do_DataAbort的函數注冊

              從2.1和2.2分別對C部分和匯編部分進行簡單的分析,下面來看一下do_DataAbort是如何識別不同的頁面分配的 如下函數為對fsr_info數組的注冊函數,因為do_DataAbort實際上就是根據fsr_info這個數組進行函數調用的 全局搜索hook_fault_code可以發現如下:實際上也就是對fsr_info數組補齊了段錯誤的函數回調 也就是說,接下來只要對著fsr_info數組這個數組進行分析,就能知道oops的全部產生原因了

              2.4 總流程圖

              3.oops產生原因分析

              如下表,為匯總的frs_info,包括對齊,頁表轉換,頁,段權限 我們繼續對源碼進行分析。如下圖為do_DataAbort函數中對fsr寄存器讀取數據的處理。也就是對fsr寄存器取fs,因為fs分布為fs[3:0]位于bit3:0,fs[4]位于bit10,所以處理后對fsr_info進行直接查表即可 第547行如上分析 第550行執行表中對應函數,只有do_bad會返回1,其余函數皆返回0. 第561行,執行由于do_bad對應的fsr導致的錯誤,arm_notify_die中判斷當前CPU是否處于用戶態,如果不是則執行die

              3.1 do_translation_fault

              static int __kprobesdo_translation_fault(unsigned long addr, unsigned int fsr,             struct pt_regs *regs){/* …… */#define TASK_SIZE       (UL(CONFIG_PAGE_OFFSET) - UL(SZ_16M))/* 如果是用戶空間的地址,用do_page_fault處理 */    if (addr < TASK_SIZE)         return do_page_fault(addr, fsr, regs);/* 至此的地址都是內核空間,如果regs顯式為用戶空間。說明兩者沖突,進入bad_area */    if (user_mode(regs))          goto bad_area;    /* 中間略過一部分代碼 */    return 0;bad_area:    do_bad_area(addr, fsr, regs);    return 0;}

              如下圖為do_bad_area源碼 第195判斷是否處于用戶模式,如果不是就Oops

              3.2 do_page_fault

              直接看下圖流程即可,不進行具體分析,總之在處于非用戶模式下缺頁且處理出現錯誤,會執行__do_kernel_fault。題外:do_page_fault完成了真正的物理頁面分配工作,另外棧擴展、mmap的支持等也都在這里。對于物理頁面的分配,會調用到do_anonymous_page->。。。-> __rmqueue,__rmqueue中實現了物理頁面分配的伙伴算法

              3.3 do_sect_fault

              源碼如下,一旦出錯,直接do_bad_area

              3.4 bad_mode

              bad_mode(中斷異常)也可以導致die并且最終直接panic,源碼如下 引用流程:xx中斷異常 -> xx_invalid -> common_invalid -> bad_mode

              3.5總結

              4. Oops的解決思路

              1.先看是否由BUG/BUG_ON引起,如果是BUG引起的,則直接看產生條件,這種具體情況具體分析 2.如果不是,則可以根據Oops現場打印進一步分析。我們繼續看在第一章中產生的Oops信息。如下圖。首先直接看到了錯誤原因,空指針引起的,然后看到錯誤碼0x817:即寫內存時,缺頁,映射失敗。接下來直接看PC指針就行了 3.PC is at myoops_test_init +0xc/0x14 確定了出問題的函數位置,然后看一下出錯進程是insmod 也就是說就是在insmod oops.ko驅動時后出的問題。接下來需要對該進程增加調試信息,以供我們能夠找到出錯位置 4.增加 –g編譯選項,見下圖。如果file帶有stripped,說明makefile或者腳本中存在選項,將其暫時屏蔽即可。 5.對于內核增加調試信息,直接搜索debug_info,將其打開即可 6.使用對應工具鏈的gbd定位問題源碼所在位置 查看代碼(默認顯示10行) l/list 例:l *(函數名+偏移) 然后根據定位到的對應源碼上下文繼續查找問題即可 7.使用addr2line定位內核中問題源碼。如下圖為之前出現問題的一串oops打印??梢园l現問題出現在dwc2_queue_transaction。我們直接找到內核對應的符號表,找到該函數對應內核的所在位置 確定其偏移為 0x8054ced8+0xf8=0x8054CFD0 使用命令 xxx(工具鏈)-addr2line -C -f -e vmlinux 8054CFD0,確定到了問題所在行數2805 附addr2line參數說明: (1).-a:在函數名、文件名和行號信息之前,以十六進制形式顯示地址。 (2).-b:指定目標文件的格式為bfdname。 (3).-C:將低級別的符號名解碼為用戶級別的名字。 (4).-e:指定需要轉換地址的可執行文件名,默認文件是a.out。 (5).-f:在顯示文件名、行號信息的同時顯示函數名。 (6).-s:僅顯示每個文件名(the base of each file name)去除目錄名。 (7).-i:如果需要轉換的地址是一個內聯函數,則還將打印返回第一個非內聯函數的信息。 (8).-j:讀取指定section的偏移而不是絕對地址。 (9).-p:使打印更加人性化:每個地址(location)的信息都打印在一行上。 (10).-r:啟用或禁用遞歸量限制。 (11).–help:打印幫助信息。 (12).–version:打印版本號。

              進階:反匯編方案,適合高手 使用命令:arm-seev100-linux-gnueabihf-objdump -d oops.ko > test.s 然后直接生擼匯編,從PC可以看出出錯在0xc。此時r3=0,r2=1。Str即將r2中數據給到r3指向的內存即0。而0這個內存地址很明顯是非法的

              責任編輯:

              標簽:

              相關推薦:

              精彩放送:

              新聞聚焦
              Top 中文字幕在线观看亚洲日韩