通過“crash地址 + MAP檔案”來定位出錯代碼位置雖然需要經過比較複雜的地址計算,但卻是最簡單實現的方式。如果僅僅想通過崩潰地址定位出錯的函數,就更加方便了。我在網上找到一個解析MAP檔案的小工具,可以非常清晰的列出每個函數的地址,並且可以將分析表格匯出為Excel檔案。工具:http://e.ys168.com/?tinyfun,工具目錄下VCMapper.exe。
另外上篇主要參考兩篇文章:
http://www.vckbase.com/document/viewdoc/?id=908
http://www.vckbase.com/document/viewdoc/?id=1473
方案二:崩潰地址 + MAP檔案 + COD檔案
由於VC8以後的版本都不再支援MAP檔案中產生程式碼資訊,因此我們尋找另一種定位方式:COD檔案。
1、COD檔案
COD檔案是一個包含了彙編碼、二進位機器碼和原始碼對應資訊的檔案,每一個cpp都對應一個COD檔案。通過這個檔案,我們可以非常方便地進行定位。
在VC6中產生COD檔案的設定方式為:Project Settings -> C/C++,在 Category 中選 Listing Files,在 Listing file type 組合框中選 Assembly,Machine code,and source。在VC8中產生COD檔案的設定方式為:Project Properties -> C/C++ -> Output Files -> Assembler Output 項,選擇 Assembly,Machine code,and Source(/Facs)。
2、定位崩潰行
下面通過舉例進行說明。現在我有一個基於對話方塊的MFC應用程式CrashTest,在CCrashTestDlg::OnInitDialog函數中寫入導致crash的代碼語句(第99行),源檔案如下:
根據崩潰地址(0x004012A3)以及MAP檔案(定位片段圖片如下),定位crash函數為OnInitDialog;並且我們可以很容易地計算出崩潰地址相對於崩潰函數的位移量為 0x004012A3 - 0x004011E0 = 0xC3。
再來看看CrashTestDlg.cod檔案,我們根據檔案中源碼資訊找到OnInitDialog函數資訊片段:
可以看到圖片中第一行為OnInitDialog函數彙編代碼的起始行;找到“int * p = NULL;”這一句源碼,其前面的98表示這行代碼在源檔案中的行號,下面的000c1表示相對於函數開始位置的位移量,後面的“33 c0”為機器碼,“xor eax,eax”為彙編碼。那麼我們根據前面算出來的位移量0xC3,找到對應出錯的語句為99行:“*p = 5;”。
總結一下定位步驟:
1) 根據公式 崩潰語句在函數中位移地址 = 崩潰地址 - 崩潰函數地址 計算出位移量X;
2) 根據公式 崩潰語句在COD檔案中地址 = 崩潰函數在COD檔案中地址 + X 計算出地址Y。其中崩潰函數在COD檔案中地址為COD檔案中函數起始括弧“{”後面表明的地址,一般情況下為0x0000;
3) 根據Y在COD檔案中找到對應程式碼。
ok,方案二介紹完了。這種方法最大的好處是沒有VC開發環境版本限制,而且COD檔案裡麵包含的資訊更加豐富,不但可以協助我們定位crash,還能幫我們分析很多東西。當然,這也導致編譯產生了很多資訊檔。