41.Linux應用調試-修改內核來列印用戶態的oops

来源:http://www.cnblogs.com/lifexy/archive/2017/12/15/8045191.html
-Advertisement-
Play Games

1.在之前第36章里,我們學習了通過驅動的oops定位錯誤代碼行 第36章的oops代碼如下所示: 1.1那為什麼在上一章,我們用錯誤的應用程式,卻沒有列印oops,如下圖所示: 接下來,我們便來配置內核,從而列印應用程式的oops 2.首先來搜索oops里的:Unable to handle ke ...


1.在之前第36章里,我們學習了通過驅動的oops定位錯誤代碼行

第36章的oops代碼如下所示:

Unable to handle kernel paging request at virtual address 56000050
      //無法處理內核頁面請求的虛擬地址56000050
pgd = c3850000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
        //內部錯誤oops
Modules linked in: 26th_segmentfault
        //表示內部錯誤發生在26th_segmentfault.ko驅動模塊里
CPU: 0    Not tainted  (2.6.22.6 #2)
PC is at first_drv_open+0x78/0x12c [26th_segmentfault]
        //PC值:程式運行成功的最後一次地址,位於first_drv_open()函數里,偏移值0x78,該函數總大小0x12c
LR is at 0xc0365ed8             //LR值

/*發生錯誤時的各個寄存器值*/
pc : [<bf000078>]    lr : [<c0365ed8>]    psr: 80000013
sp : c3fcbe80  ip : c0365ed8  fp : c3fcbe94
r10: 00000000  r9 : c3fca000  r8 : c04df960
r7 : 00000000  r6 : 00000000  r5 : bf000de4  r4 : 00000000
r3 : 00000000  r2 : 56000050  r1 : 00000001  r0 : 00000052

Flags: Nzcv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 33850000  DAC: 00000015
Process 26th_segmentfau (pid: 813, stack limit = 0xc3fca258)
            //發生錯誤時,進程名稱為26th_segmentfault

Stack: (0xc3fcbe80 to 0xc3fcc000)        //棧信息,從棧底0xc3fcbe80到棧頂0xc3fcc000
be80: c06d7660 c3e880c0 c3fcbebc c3fcbe98 c008d888 bf000010 00000000 c04df960
bea0: c3e880c0 c008d73c c0474e20 c3fb9534 c3fcbee4 c3fcbec0 c0089e48 c008d74c
bec0: c04df960 c3fcbf04 00000003 ffffff9c c002c044 c380a000 c3fcbefc c3fcbee8
bee0: c0089f64 c0089d58 00000000 00000002 c3fcbf68 c3fcbf00 c0089fb8 c0089f40
bf00: c3fcbf04 c3fb9534 c0474e20 00000000 00000000 c3851000 00000101 00000001
bf20: 00000000 c3fca000 c04c90a8 c04c90a0 ffffffe8 c380a000 c3fcbf68 c3fcbf48
bf40: c008a16c c009fc70 00000003 00000000 c04df960 00000002 be84ce38 c3fcbf94
bf60: c3fcbf6c c008a2f4 c0089f88 00008588 be84ce84 00008718 0000877c 00000005
bf80: c002c044 4013365c c3fcbfa4 c3fcbf98 c008a3a8 c008a2b0 00000000 c3fcbfa8
bfa0: c002bea0 c008a394 be84ce84 00008718 be84ce30 00000002 be84ce38 be84ce30
bfc0: be84ce84 00008718 0000877c 00000003 00008588 00000000 4013365c be84ce58
bfe0: 00000000 be84ce28 0000266c 400c98e0 60000010 be84ce30 30002031 30002431

Backtrace:                                        //回溯信息
[<bf000000>] (first_drv_open+0x0/0x12c [26th_segmentfault]) from [<c008d888>] (chrdev_open+0x14c/0x164)
 r5:c3e880c0 r4:c06d7660
[<c008d73c>] (chrdev_open+0x0/0x164) from [<c0089e48>] (__dentry_open+0x100/0x1e8)
 r8:c3fb9534 r7:c0474e20 r6:c008d73c r5:c3e880c0 r4:c04df960
[<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:be84ce38 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: bf000094 bf0000b4 bf0000d4 e5952000 (e5923000)

Segmentation fault 

1.1那為什麼在上一章,我們用錯誤的應用程式,卻沒有列印oops,如下圖所示:

接下來,我們便來配置內核,從而列印應用程式的oops

 

2.首先來搜索oops里的:Unable to handle kernel列印語句,看在哪個函數列印的

如下圖所示,找到位於__do_kernel_fault()函數中:

 

 

3.繼續找,發現__do_kernel_fault()被do_bad_area()調用

 

do_bad_area()函數,從字面上分析,表示代碼執行到錯誤段位置

其中user_mode(regs)函數,通過判斷CPSR寄存器若是用戶模式則返回0,否則返回正數.

所以我們上一章的錯誤的應用程式便會調用__do_user_fault()函數

 

4.__do_user_fault()函數如下所示:

 

從上圖來看,要想列印應用程式的錯誤信息,還需要:

3.1配置內核,設置巨集CONFIG_DEBUG_USER(只要巨集是以"CONFIG_"開頭,都是與配置相關)

1)make menuconfig里搜索DEBUG_USER,如下圖所示:

 

所以將Kernel hacking-> Verbose user fault messages 置為Y,並重新燒內核

3.2使if (user_debug & UDBG_SEGV)為真

1)其中user_debug定義如下所示:

 

顯然當uboot傳遞進來的命令行字元里含有"user_debug="時,便會調用user_debug_setup()->get_option(),最終會將"user_debug="後面帶的字元串提取給user_debug變數.

比如:當命令行字元里含有"user_debug=0xff"時,則user_debug變數等於0xff

2)其中UDBG_SEGV定義如下所示:

#define UDBG_UNDEFINED  (1 << 0)        //用戶態的代碼出現未定義指令(UNDEFINED)

#define UDBG_SYSCALL (1 << 1)           //用戶態系統調用已過時(SYSCALL)     

#define UDBG_BADABORT    (1 << 2)       //用戶態數據錯誤已中止(BADABORT) 

#define UDBG_SEGV     (1 << 3)         //用戶態的代碼出現段錯誤(SEGV)

#define UDBG_BUS       (1 << 4)        //用戶態訪問忙(BUS)

從上面的定義分析得出,我們只需要將user_debug設為0xff,上面的所有條件就都成立.

比如:當用戶態的代碼出現未定義指令時,由於user_debug最低位=1,所以列印出oops.

所以,進入uboot,在uboot命令行里添加: "user_debug=0xff"

 

4. 啟動內核,試驗

如下圖所示,執行錯誤的應用程式,只列印了各個寄存器值,以及函數調用關係,而沒有棧信息:

 

 

5.接下來,繼續修改內核,使應用程式的oops也列印棧信息出來

在驅動的oops里有"Stack: "這個欄位,搜索"Stack: "看看,位於哪個函數

5.1如下圖所示, 找到位於__die()函數中:

 

這個__die()會被die()調用,die()又會被__do_kernel_fault()調用,而我們應用程式調用的__do_user_fault()里沒有die()函數,所以沒有列印出Stack棧信息。

上圖裡dump_mem():

dump_mem("Stack: ", regs->ARM_sp,THREAD_SIZE + (unsigned long)task_stack_page(tsk));    //列印stack棧信息

 主要是通過sp寄存器里存的棧地址,每列印一個棧地址里的32位數據, 棧地址便加4(一個地址存8位,所以加4)。

接下來我們便通過這個原理,來修改應用程式調用的__do_user_fault()

 

5.2 在__do_user_fault(),添加以下帶紅色的字:

static void  __do_user_fault(struct task_struct *tsk, unsigned long addr,unsigned int fsr, unsignedint sig, int code,struct pt_regs *regs)

{

       struct siginfo si;

       unsigned long val ;

       int i=0;

#ifdef CONFIG_DEBUG_USER

       if (user_debug & UDBG_SEGV) {

              printk(KERN_DEBUG "%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",

                     tsk->comm, sig, addr, fsr);

              show_pte(tsk->mm, addr);

              show_regs(regs);

        printk("Stack: \n");

        while(i<1024)

        {

               /* copy_from_user()只是用來檢測該地址是否有效,如有效,便獲取地址數據,否則break */

           if(copy_from_user(&val, (const void __user *)(regs->ARM_sp+i*4), 4))

           break;

            printk("%08x ",val);    //列印數據

            i++;

            if(i%8==0)

            printk("\n");

        }

        printk("\n END of Stack\n");

       }

#endif

       tsk->thread.address = addr;

       tsk->thread.error_code = fsr;

       tsk->thread.trap_no = 14;

       si.si_signo = sig;

       si.si_errno = 0;

       si.si_code = code;

       si.si_addr = (void __user *)addr;

       force_sig_info(sig, &si, tsk);

}

 

6.重新燒寫內核,試驗

如下圖所示:

 

接下來,便來分析PC值,Stack棧,到底如何調用的

 

7.首先來分析PC值,確定錯誤的代碼

1)生成反彙編:

arm-linux-objdump -D test_debug > test_debug.dis

 

2)搜索PC值84ac,如下圖所示:

從上面看出,主要是將0x12(r3)放入地址0x00(r2)中

而0x00是個非法地址,所以出錯

 

8.分析Stack棧信息,確定函數調用過程

參考: 37.Linux驅動調試-根據oops的棧信息,確定函數調用過程

8.1分析過程中,遇到main()函數的返回地址為:LR=40034f14

內核的虛擬地址是c0004000~c03cebf4,而反彙編里也沒有該地址,所以這是個動態庫的地址.

需要用到靜態鏈接方法,接下來重新編譯,反彙編,運行:

#arm-linux-gcc -o -static  test_debug test_debug.c
          //-static   靜態鏈接,生成的文件會非常大, 好處在於不需要動態鏈接庫,也可以運行
#arm-linux-objdump -D test_debug > test_debug.dis

8.2最終, 找到main()函數的返回地址在__lobc_start_main()里

所以函數出錯時的調用過程:

 __lobc_start_main()->
    main()->
    A()->
        B()->
            C()  //將0x12(r3)放入地址0x00(r2)中

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Excel表格中的迷你圖表能夠直觀地向我們展示出數據的變化趨勢。本文將介紹C#如何實現為表格數據生成迷你圖表,以及修改和刪除迷你圖表的方法。下麵將詳細講述。 所用組件工具:Spire.XLS for .NET 原Excel圖表: 一、添加迷你圖表(折線圖、柱形圖、盈虧圖) 1.添加命名空間 2.主要 ...
  • 1.引言 2.解決方案各部分介紹圖 3.添加數據模型 4.添加資料庫上下文 5.修改配置文件 6.使用依賴關係註入容器註冊資料庫上下文 7.添加基架工具並執行初始遷移 1.引言 NetCore出來有一段時間了,跨平臺、開源、高性能 讓每個從事.net的開發者都興奮了一把,對此我也有濃厚的興趣。 2. ...
  • 這篇是對象與集合操練,物件的創建,集合的一些基本功能,如添加,編輯,刪除等功能。 對象,即是網店的商品物件,Insus.NET只為其添加2個屬性,物件的ID的Key和名稱ItemName以及2個構造函數,最後一個方法是重寫ToString()方法。 class Item { private int ...
  • 每個版本的 .NET framework 都包含公共語言運行時 (CLR)、基類庫和其他托管庫。 本主題按版本介紹了 .NET Framework 的關鍵功能,提供了有關基礎 CLR 版本和相關開發環境的信息,並標識了 Windows 操作系統所安裝的版本。 備註 若要瞭解如何下載和安裝 .NET ...
  • 今天做了一個統計站點的網頁,想要發佈一下,中間碰到不少問題,現在和大家分享一下! 這是我想要最終的網頁結果: 1.發佈站點到桌面(任意路徑) 2.安裝IIS 3.安裝好後,打開IIS,新建站點web,結果瀏覽時,提示瀏覽文件的許可權不夠。於是,新建用戶Everyone,修改該web站點的許可權。 給Ev ...
  • .Net Core 將之前Web.Config中的配置遷移到了appsettings.json文件中,並使用ConfigurationBuilder來讀取這個配置文件。並可設置在配置文件變化以後,自動重新載入,這樣可不用重啟你的程式。 ...
  • 一、MVC簡介 MVC:Model-View-Controller(模型-視圖-控制器),MVC是一種軟體開發架構模式。 1、模型(Model) 模型對象是實現應用程式數據域邏輯的應用程式部件。 通常,模型對象會檢索模型狀態並將其存儲在資料庫中。 例如,Product 對象可能會從資料庫中檢索信息, ...
  • 背水一戰 Windows 10 之 控制項(自定義控制項): 自定義控制項的基礎知識,依賴屬性和附加屬性 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...