VC根據崩潰內存地址,查找異常代碼位置

如何通過崩潰地址找到出錯的代碼行作爲程序員,我們平時最擔心見到的事情是什麼?是內存泄漏?是界面不好看?

是,程序發生了崩潰!

經過下面四步,輕鬆解決上述難題:

第一:生成MAP文件;

第二:查找崩潰的內存地址;

第三:定位異常代碼出錯函數;

第四:定位出錯行號

下面詳細介紹具體步驟:

第一:生成MAP文件

首先必須生成程序的MAP文件。什麼是MAP文件?

簡單地講, MAP文件是程序的全局符號、源文件和代碼行號信息的唯一的文本表示方法,它可以在任何地方、任何時候使用,不需要有額外的程序進行支持。

如何生成MAP呢?

在VC6.0中,我們可以按下Alt+F7,打開“Project Settings”選項頁,選擇C/C++選項卡,並在最下面的 Project Options 裏面輸入:/Zd,然後要選擇Link選項卡,在最下面的Project Options裏面輸入:/mapinfo:lines 和 /map:PROJECT_NAME.map。最後按下F7來編譯生成EXE可執行文件和MAP文件。

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

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

生成的MAP文件在項目文件夾下,文件名爲:PROJECT_NAME.map

第二:查找崩潰的內存地址;

OK,通過上面的步驟,我們已經得到了MAP文件,那麼我們該如何利用它呢?讓我們從簡單的實例入手,請打開你的VC,新建這樣一個例子文件:


2 void Crash(void)
3 {
4     int i = 1;
5       int j = 0;
6       i /= j;
7 }

9 void main(void)
10 {
11      Crash();
12 } 

很顯然本程序有“除0錯誤”(墳墓不能爲0),在Debug方式下編譯的話,運行時肯定會產生“非法操作”。好,讓我們運行它,果然,“非法操作”對話框出現了,這時我們點擊“詳細信息”按鈕,記錄下產生崩潰的地址--在我的機器上是0x00401046

如何查看異常“詳細信息”?

在調試狀態下,菜單欄:查看->調試窗口->Dissasembly。或則直接Alt+8。

第三:定位異常代碼出錯函數;

在MAP文件中:(由於文件內容太長,中間沒用的部分我進行了省略),MAP文件可以用文本編輯器打開

addressError

Timestamp is 54abf59b(Tue Jan 06 22:47:55 2015)

 Preferred load address is 00400000

 Start        Length     Name                   Class

 0001:00000000 0001055cH .text                   CODE

 0002:00000000 00001380H .rdata                  DATA

 0002:00001380 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 00003220H .data                   DATA

 0003:00003c50 000019a8H .bss                    DATA

 0004:00000000 00000014H .idata$2                DATA

 0004:00000014 00000014H .idata$3                DATA

 0004:00000028 00000120H .idata$4                DATA

 0004:00000148 00000120H .idata$5                DATA

 0004:00000268 000004f4H .idata$6                DATA

 

  Address         Publics by Value              Rva+Base     Lib:Object

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

 0001:00000050       _main                      00401050 f   addressError.obj

 0001:00000080       __chkesp                   00401080 f   LIBCD:chkesp.obj

 0001:000000c0       _mainCRTStartup            004010c0 f   LIBCD:crt0.obj

 0001:000001f0       __amsg_exit                004011f0 f   LIBCD:crt0.obj

 0001:00000250       __CrtDbgBreak              00401250 f   LIBCD:dbgrpt.obj

 0001:00000260       __CrtSetReportMode         00401260 f   LIBCD:dbgrpt.obj

 0001:000002c0       __CrtSetReportFile         004012c0 f   LIBCD:dbgrpt.obj

 0001:00000340       __CrtSetReportHook         00401340 f   LIBCD:dbgrpt.obj

 0001:00000360       __CrtDbgReport             00401360 f   LIBCD:dbgrpt.obj

….

 

entrypoint at        0001:000000c0

 

Linenumbers for .\Debug\addressError.obj(E:\addressError\addressError.cpp) segment.text

  3 0001:00000020     4 0001:00000034     5 0001:0000003b     6 0001:00000042

  7 0001:0000004c    10 0001:00000050    110001:00000053    12 0001:00000058

 

仔細瀏覽Rva+Base這欄,你會發現第一個比崩潰地址0x00401046大的函數地址是00401050,所以在0x00401050這個地址之前的那個入口就是產生崩潰的函數,也就是這行:

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

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

第四:定位出錯行號

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

3 0001:00000020

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

如果要查找代碼行號,需要使用下面的公式做一些十六進制的減法運算:

崩潰地址 = 偏移地址(Rva)+基地址(Base)

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

爲什麼要這樣做呢?細心的朋友可能會留意到Rva+Base這欄了,我們得到的崩潰地址都是由偏移地址(Rva)+基地址(Base)得來的,所以在計算行號的時候要把基地址減去.

一般情況下,基地址的值是0x00400000(可由MAP文件的第五行得到:Preferred load address is00400000)。另外,由於一般的PE文件的代碼段都是從0x1000(可由MAP文件的Line numbers information得到:3 0001:00000020)偏移開始的,所以也必須減去0x1000。

崩潰行偏移 = 0x00401046-0x00400000 - 0x1000 = 0x46

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

6 0001:00000042 也就是在源代碼中的第6行,讓我們來看看源代碼:16 i /= j;果然就是第6行!

成功查找完畢。

注:VS2005等的查找方法類似,自己思考下就行,不懂的可以留言聯繫。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章