VC6.0 經過崩潰地址中找到異常代碼行

來源:http://blog.csdn.net/mydeardingxiaoli/article/details/20371585程序員

這是從「VC編程經驗總結7」中轉出來的借花獻佛——如何經過崩潰地址找到出錯的代碼行做爲程序員,咱們平時最擔憂見到的事情是什麼?是內存泄漏?是界面很差看?……錯啦!我相信個人見解是不會有人反對的--那就是,程序發生了崩潰! 「該程序執行了非法操做,即將關閉。請與你的軟件供應商聯繫。」,呵呵,這句 M$ 的「名言」,恐怕就是程序員最擔憂見到的東西了。有的時候,本身的程序在本身的機器上運行得好好的,可是到了別人的機器上就崩潰了;有時本身在編寫和測試的過程當中就莫名其妙地遇到了非法操做,可是卻沒法肯定究竟是源代碼中的哪行引發的……是否是很痛苦呢?沒關係,本文能夠幫助你走出這種困境,甚至你今後以後能夠自豪地要求用戶把崩潰地址告訴你,而後你就能夠精確地定位到源代碼中出錯的那行了。(很神奇吧?呵呵。首先我必須強調的是,本方法能夠在目前市面上任意一款編譯器上面使用。可是我只熟悉 M$ 的 VC 和 MASM ,所以後面的部分只介紹如何在這兩個編譯器中實現,請讀者自行融會貫通,掌握在別的編譯器上使用的方法。Well,廢話說完了,讓咱們開始!:)編程

首先必須生成程序的 MAP 文件。什麼是 MAP 文件?簡單地講, MAP 文件是程序的全局符號、源文件和代碼行號信息的惟一的文本表示方法,它能夠在任何地方、任什麼時候候使用,不須要有額外的程序進行支持。並且,這是惟一能找出程序崩潰的地方的救星。windows

好吧,既然 MAP 文件如此神奇,那麼咱們應該如何生成它呢?在 VC 中,咱們能夠按下 Alt+F7 ,打開「Project Settings」選項頁,選擇 C/C++ 選項卡,並在最下面的 Project Options 裏面輸入:/Zd ,而後要選擇 Link 選項卡,在最下面的 Project Options 裏面輸入: /mapinfo:lines 和 /map:PROJECT_NAME.map 。最後按下 F7 來編譯生成 EXE 可執行文件和 MAP 文件。api

在 MASM 中,咱們要設置編譯和鏈接參數,我一般是這樣作的:函數

rc %1.rc
ml /c /coff /Zd %1.asm
link /subsystem:windows /mapinfo:exports /mapinfo:lines /map:%1.map %1.obj %1.res測試

把它保存成 makem.bat ,就能夠在命令行輸入 makem filename 來編譯生成 EXE 可執行文件和 MAP 文件了。.net

在此我先解釋一下加入的參數的含義:命令行

/Zd 表示在編譯的時候生成行信息
/map[:filename] 表示生成 MAP 文件的路徑和文件名
/mapinfo:lines 表示生成 MAP 文件時,加入行信息
/mapinfo:exports 表示生成 MAP 文件時,加入 exported functions (若是生成的是 DLL 文件,這個選項就要加上)orm

OK,經過上面的步驟,咱們已經獲得了 MAP 文件,那麼咱們該如何利用它呢?讓咱們從簡單的實例入手,請打開你的 VC ,新建這樣一個文件:11 
12 void Crash(void)
13 {
14 int i = 1;
15 int j = 0;
16 i /= j;
17 }
18 
19 void main(void)
20 {
21 Crash();
22 } 很顯然本程序有「除0錯誤」,在 Debug 方式下編譯的話,運行時確定會產生「非法操做」。好,讓咱們運行它,果真,「非法操做」對話框出現了,這時咱們點擊「詳細信息」按鈕,記錄下產生崩潰的地址--在個人機器上是0x0040104a 。blog

再看看它的 MAP 文件:(因爲文件內容太長,中間沒用的部分我進行了省略)CrashDemoTimestamp is 3e430a76 (Fri Feb 07 09:23:02 2003)

Preferred load address is 00400000

Start Length Name Class
0001:00000000 0000de04H .text CODE
0001:0000de04 0001000cH .textbss CODE
0002:00000000 00001346H .rdata DATA
0002:00001346 00000000H .edata DATA
0003:00000000 00000104H .CRT$XCA DATA
0003:00000104 00000104H .CRT$XCZ DATA
0003:00000208 00000104H .CRT$XIA DATA
0003:0000030c 00000109H .CRT$XIC DATA
0003:00000418 00000104H .CRT$XIZ DATA
0003:0000051c 00000104H .CRT$XPA DATA
0003:00000620 00000104H .CRT$XPX DATA
0003:00000724 00000104H .CRT$XPZ DATA
0003:00000828 00000104H .CRT$XTA DATA
0003:0000092c 00000104H .CRT$XTZ DATA
0003:00000a30 00000b93H .data DATA
0003:000015c4 00001974H .bss DATA
0004:00000000 00000014H .idata$2 DATA
0004:00000014 00000014H .idata$3 DATA
0004:00000028 00000110H .idata$4 DATA
0004:00000138 00000110H .idata$5 DATA
0004:00000248 000004afH .idata$6 DATA

Address Publics by Value Rva+Base Lib:Object

0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo.obj
0004:000001f0 __imp__FlushFileBuffers@4 004241f0 kernel32:KERNEL32.dll
0004:000001f4 __imp__CloseHandle@4 004241f4 kernel32:KERNEL32.dll
0004:000001f8 /177KERNEL32_NULL_THUNK_DATA 004241f8 kernel32:KERNEL32.dllentry point at 0001:000000f0
Line numbers for ./Debug/CrashDemo.obj(d:/msdev/myprojects/crashdemo/crashdemo.cpp) segment .text13 0001:00000020 14 0001:00000038 15 0001:0000003f 16 0001:00000046
17 0001:00000050 20 0001:00000070 21 0001:00000088 22 0001:0000008d 若是仔細瀏覽 Rva+Base 這欄,你會發現第一個比崩潰地址 0x0040104a 大的函數地址是 0x00401070 ,因此在 0x00401070 這個地址以前的那個入口就是產生崩潰的函數,也就是這行:

0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo.obj

所以,發生崩潰的函數就是 ?Crash@@YAXXZ ,全部以問號開頭的函數名稱都是 C++ 修飾的名稱。在咱們的源程序中,也就是 Crash() 這個子函數。

OK,如今咱們垂手可得地便知道了發生崩潰的函數名稱,你是否是很興奮呢?呵呵,先別忙,接下來,更厲害的招數要出場了。

請注意 MAP 文件的最後部分--代碼行信息(Line numbers information),它是以這樣的形式顯示的:

13 0001:00000020

第一個數字表明在源代碼中的代碼行號,第二個數是該代碼行在所屬的代碼段中的偏移量。

若是要查找代碼行號,須要使用下面的公式作一些十六進制的減法運算:

崩潰行偏移 = 崩潰地址(Crash Address) - 基地址(ImageBase Address) - 0x1000

爲何要這樣作呢?細心的朋友可能會留意到 Rva+Base 這欄了,咱們獲得的崩潰地址都是由 偏移地址(Rva)+基地址(Base) 得來的,因此在計算行號的時候要把基地址減去,通常狀況下,基地址的值是 0x00400000 。另外,因爲通常的 PE 文件的代碼段都是從 0x1000 偏移開始的,因此也必須減去 0x1000 。

好了,明白了這點,咱們就能夠來進行小學減法計算了:

崩潰行偏移 = 0x0040104a - 0x00400000 - 0x1000 = 0x4a

若是瀏覽 MAP 文件的代碼行信息,會看到不超過計算結果,但卻最接近的數是 CrashDemo.cpp 文件中的:

16 0001:00000046 也就是在源代碼中的第 16 行,讓咱們來看看源代碼:16 i /= j;

哈!!!果真就是第 16 行啊!興奮嗎?我也同樣! :)方法已經介紹完了,從今之後,咱們就能夠精確地定位到源代碼中的崩潰行,並且只要編譯器能夠生成 MAP 文件(包括 VC、MASM、VB、BCB、Delphi……),本方法都是適用的。咱們時常抱怨 M$ 的產品如何如何差,但其實 M$ 仍是有意無心間提供了不少有價值的信息給咱們的,只是咱們每每不懂得怎麼利用而已……相信這樣一來,你就能夠更爲從容地面對「非法操做」提示了。你甚至能夠要求用戶提供崩潰的地址,而後就能夠坐在家中舒舒服服地找到出錯的那行,並進行修正。

相關文章
相關標籤/搜索