反彙編定義段錯誤

2021-10-08 17:11:27 字數 4749 閱讀 1676

段錯誤是程式設計師最討厭的問題之一,其發生往往很突然,且破壞巨大。典型的段錯誤是由於操作記憶體不當引起的(如使用野指標或訪問受保護的位址等),發生段錯誤時,核心以乙個訊號sigse**強行終止程序,留下的出錯資訊極少,從而導致難以定位。但利用gdb和反彙編工具,可以較準確地定位段錯誤產生的原因。但想用這種方法除錯,一些準備工作和工具是必需的。

準備工作

(1)coredump:程序異常中止時,核心生成的記錄檔案,其中儲存了程序異常時所占用的記憶體和cpu資源,如pc計數器、各個暫存器的值等。這個檔案是除錯段錯誤最重要的依據。要使核心生成coredump,需要在核心配置中開啟config_elf_core選項,如果沒有開啟,將其選上後重新編譯核心即可。

此外,利用命令ulimit -c unlimited,可以設定coredump大小為不受限制,可以儲存更完整的資訊。檔案/proc/sys/kernel/core_pattern可以配置生成coredump的命名格式,如果不設定格式,則coredump預設生成的位置在出錯程序的目錄下,且生成的core同名,也就意味著舊的coredump可能被新的coredump所覆蓋。如果我想在/tmp目錄下生成以core.pid格式命名的coredump檔案,只需執行命令:

echo "/tmp/core.%p" > /proc/sys/kernel/core_pattern

(2)編譯:為了利用gdb進行除錯,在編譯程式時,需要在編譯選項中加入-g選項,以將除錯資訊編譯到目標檔案中。

(3)反彙編:顧名思義,反彙編就是將編譯好的二進位制可執行檔案翻譯成彙編檔案。一般來說,編譯器會自帶一套反彙編工具,只有選擇正確的工具才能正確地進行反彙編,這不難理解。比如我是用gcc4.6.3編譯的用於mips的應用程式,那麼,在編譯器的目錄下可以找到gcc463/usr/bin/mipsel-buildroot-linux-uclibc-objdump,這就是我要使用的反彙編工具。將二進位制檔案反彙編成彙編檔案只需執行命令:***x-objdump -s ***x(程式名),即可生成可以閱讀的、關聯到c**的彙編**,如下所示:

42f24c: 8fbc0018 lw gp,24(sp)

可以看到,c**下面跟著一串彙編**,而彙編**前面有一段位址,這個位址是什麼呢?如果熟悉linux程序空間的概念,很容易就可以聯想到,這個位址其實就是相應的彙編指令在.text段(即**段)中的位址。也就是說,這個位址就是我們用於定位具體出錯地點的依據。(4)gdb:可以說是linux下除錯程式最常用的工具,功能強大,操作也很簡單。對於mips程式除錯,只需安裝相應的gdb:mips-linux-gdb即可。
開始除錯

上面的準備工作都完成後,就可以開始除錯了。當程序再次異常終止時,就可以在/tmp目錄下找到coredump檔案:比如core.126(程序id為126的程序生成的coredump)。

用gdb的-c選項開啟coredump:mips-linux-gdb -c /tmp/core.126,可以看到如下資訊:

(gdb)前面是gdb的版本資訊,不必理會。我們主要關注下面的內容:

#0 0x2b17ff50 in ?? ()表示這個coredump是為程序httpd生成的,而程序退出的原因是signal 11,即sigse**,這正是我們想要的。最後一行,0x2b17ff50是乙個位址,這裡??的地方本來應該顯示乙個函式名,之所以這裡沒有顯示,我猜想這應該是乙個庫函式,而編譯這個庫時,並沒有帶入-g資訊。

不要緊,接下來只需要輸入where,即可顯示訊號產生時程式中止的位置:

#0 0x2b17ff50 in ?? ()

(gdb) where

#0 0x2b17ff50 in ?? ()

#1 0x0045c034 in ?? ()

backtrace stopped: previous frame identical to this frame (corrupt stack?)

(gdb)至此,我們已經拿到最重要的資訊:0x0045c034,就是程序中止時停留的位置。對照上面生成的反彙編檔案,搜尋45c034,即可找到:

rpm_monitorap_trace("\r\n%s\r\n\r\n", out);

可以看到,出錯時,對應的c函式是httpnprintf,對應的彙編**為:    lw    gp, 16(sp)。
在反彙編檔案中再稍微對照上下文,即可知道具體是哪個模組、哪個檔案中的呼叫。如果看得懂彙編**,基本可以定位到函式中的具體語句,即使看不懂彙編,利用列印除錯或者靜態**分析等常規除錯手段也基本可以定位到具體的出錯原因了。在本例中,最終確定這個函式出錯的原因是操作了呼叫malloc(0)而獲取的乙個空指標(malloc(0)返回什麼),著實令人始料未及。