首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

僅透過崩潰位址找出原始碼錯誤行

     編輯:關於VC++

身為程式設計師,平時最擔心看到什麼?記憶體洩漏?介面難看?錯了!我相信沒人會反對——那就是程式發生崩潰!

「該程式執行了非法操作,即將關閉。請與你的軟體供應商聯繫。」這句 Microsoft 的「名言」,恐怕是程式設計師最不想見到的。有時自己程式在自己機器跑得好好的,到了別人機器就崩潰;有時自己開發測試過程中莫名遇到非法操作,卻無法確定到底是原始碼哪一行引起的……很痛苦吧?別緊張,本文幫你走出困境,甚至事後你可自豪要求使用者提供崩潰位址,然後精確定位到原始碼出錯那一行(很神奇吧?)

核心關鍵:MAP 檔案

首先必須產生程式的 MAP 檔案。什麼是 MAP 檔案?簡單說,MAP 檔案是程式全域符號、原始檔案與程式碼行號資訊的唯一純文字表示,任何地方、任何時候可用,無需額外程式支援。而且是找出程式崩潰位置的唯一救星。

如何產生 MAP 檔案

VC 環境:按 Alt+F7 開啟「Project Settings」,選 C/C++ 索引標籤,最下方 Project Options 輸入 /Zd;選 Link 索引標籤,最下方 Project Options 輸入 /mapinfo:lines 和 /map:PROJECT_NAME.map。按 F7 編譯產生 EXE 與 MAP 檔。

MASM 環境:設定編譯連結參數,通常寫成 makem.bat:

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

命令列輸入 makem filename 編譯產生 EXE 與 MAP 檔。

參數說明:

  • /Zd:編譯時產生行號資訊
  • /map[:filename]:產生 MAP 檔路徑與檔名
  • /mapinfo:lines:MAP 檔加入行號資訊
  • /mapinfo:exports:MAP 檔加入 exported functions(產生 DLL 時需加入)

實戰範例

VC 新建檔案:

01 //****************************************************************
02 // 程式名稱:演示如何透過崩潰位址找出原始碼出錯行
03 // 作者:羅聰
04 // 日期:2003-2-7
05 // 出處:https://www.luocong.com
06 // 本程式會產生「除 0 錯誤」導致「非法操作」對話框
07 // 注意:轉載請保持完整並註明出處
08 //****************************************************************
09
10 void Crash(void)
11 {
12     int i = 1;
13     int j = 0;
14     i /= j;  // 第 16 行:除零錯誤
15 }
16
17 void main(void)
18 {
19     Crash();
20 }

Debug 方式編譯執行,點擊「詳細資訊」記錄崩潰位址——作者機器上為 0x0040104a。

分析 MAP 檔案

MAP 檔案關鍵區段:

Address Publics by Value Rva+Base Lib:Object
0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo.obj
0001:00000070 _main 00401070 f CrashDemo.obj
...
Line numbers for .\Debug\CrashDemo.obj(d:\msdev\myprojects\crashdemo\crashdemo.cpp) segment .text
13 0001:00000020
14 0001:00000038
15 0001:0000003f
16 0001:00000046
17 0001:00000050
20 0001:00000070
21 0001:00000088
22 0001:0000008d

步驟 1:找出崩潰函式

瀏覽 Rva+Base 欄,第一個比崩潰位址 0x0040104a 大的函式位址是 0x00401070 (_main),所以它之前的那個入口即為崩潰函式:

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

發生崩潰的函式為 ?Crash@@YAXXZ(C++ 修飾名稱),對應原始碼 Crash() 函式。

步驟 2:精確定位崩潰行號

MAP 檔最後部分為程式碼行號資訊:行號 段:偏移量。計算崩潰行偏移公式:

崩潰行偏移 = 崩潰位址 - 基底位址 - 0x1000

原因:崩潰位址 = RVA + Base,基底位址通常 0x00400000,PE 檔案程式碼段通常從 0x1000 偏移開始。

計算:0x0040104a - 0x00400000 - 0x1000 = 0x4a

瀏覽行號資訊,不超過 0x4a 但最接近的是:

16 0001:00000046

即原始碼第 16 行:i /= j; —— 完全正確!

結語

方法介紹完畢。以後可精確定位原始碼崩潰行,只要編譯器能產生 MAP 檔(VC、MASM、VB、BCB、Delphi…)皆適用。Microsoft 無意間提供許多有價值資訊,只看我們是否善用。現在面對「非法操作」更從容,甚至可要求使用者提供崩潰位址,在家舒服找到出錯行並修正。很爽吧?:)

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved