.map文件的使用(程序入口地址)


C 語言編程
一.實驗目的
1.學習用標准C 語言編制程序; 2.了解常用的C 語言程序設計方法和組成部分.

二.實驗設備
1.PC 兼容機一台; 2.Code Composer Studio 2.21 軟件.

三.實驗原理
1.標准C 語言程序 CCS 支持使用標准C 語言開發DSP 應用程序.當使用標准C 語言編制的程序時,其 源程序文件名的后綴應為.c(如:volume.c). CCS 在編譯標准C 語言程序時,首先將其編譯成相應匯編語言程序,再進一步編譯成 目標DSP 的可執行代碼.最后生成的是coff 格式的可下載到DSP中運行的文件,其文件名 后綴為.out. 由於使用C 語言編制程序,其中調用的標准C 的庫函數由專門的庫提供,在編譯連接 時編譯系統還負責構建C 運行環境.所以用戶工程中需要注明使用C 的支持庫. 2.命令文件的作用 命令文件(文件名后綴為cmd)為鏈接程序提供程序和數據在具體DSP 硬件中的位置分 配信息.通過編制命令文件,我們可以將某些特定的數據或程序按照我們的意圖放置在DSP 所管理的內存中.命令文件也為鏈接程序提供了DSP 外擴存儲器的描述.在程序中使用 CMD文件描述硬件存儲區,可以只說明使用部分,但只要是說明的,必須和硬件匹配,也 就是只要說明的存儲區必須是存在的和可用的. 3.內存映射(map)文件的作用 一般地,我們設計,開發的DSP 程序在調試好后,要固化到系統的ROM 中.為了更 精確地使用ROM 空間, 我們就需要知道程序的大小和位置, 通過建立目標程序的map 文件 可以了解DSP代碼的確切信息.當需要更改程序和數據的大小和位置時,就要適當修改cmd 文件和源程序,再重新生成map 文件來觀察結果.另外,通過觀察map文件,可以掌握DSP 存儲器的使用和利用情況,以便進行存儲器方面的優化工作.

四.實驗步驟
1.實驗准備 . 設置軟件仿真模式. 2.建立新的工程文件 . (1)啟動Code Composer Studio 2.21. (2)選擇菜單項Project/New以后進行設置,建立CProgram.pjt,保存路徑:
C:\ICETEK\VC5416AES61\VC5416AES61\Lab0102-CProgram

3.編輯輸入源程序 . (1) C 語言程序(步驟1) - 先新建源程序窗口:file/new/source files - 輸入源程序: main() { int x,y,z; x=1; y=2;

while ( 1 ) { z=x+y; } } - 保存源程序為CProgram.c,保存路徑:C:\ICETEK\VC5416AES61\VC5416AES61\ Lab0102-CProgram (2) 連接命令文件 - 如同第(1)的步驟1 操作,建立空的源程序窗口. - 輸入連接命令文件內容: -l rts.lib MEMORY { PAGE 0: PRAM: o=0x100, l=0x2000 PAGE 1: DRAM:o=0x3000,l=0x2000 } SECTIONS { .text: {} > PRAM PAGE 0 .bss: {} > DRAM PAGE 1 .stack {} > DRAM PAGE 1 } 注意:第1 行中減號后面和第5,6 行中等號前邊字母是小寫的"L". - 如同第(1)的步驟2 操作,將文件存為 C:\ICETEK\VC5416AES61\VC5416AES61\Lab0102-CProgram\CProgram.cmd 在連接命令文件中,將可用內存分為兩塊:PAGE 0 和PAGE 1,其中PAGE 0 是程序段; 而PAGE 1 是數據段.其中又詳細分配了不同程序段的起始地址和長度. (3) 將上述編譯的源程序加入工程CProgram.pjt. 4.編譯源文件,下載可執行程序 .編譯源文件, (1) 單擊菜單"Project","Rebuild All". (2) 執行File/Load Program ,在隨后打開的對話框中選擇剛剛建立的 C:\ICETEK\VC5416AES61\VC5416AES61\Lab0102-CProgram\debug\CProgram.out 文件.完 成后,系統自動打開一個反匯編窗口"Disassembly",並在其中指示程序的入口地址為 "_c_int00". 5.打開觀察窗口 . 開啟CPU 寄存器觀察窗口:單擊菜單View->Registers->CPU Registers. 6.觀察程序運行結果 . 這時,在"Disassembly"代表程序運行位置的綠色箭頭指向程序的入口地址,程序將從此 開始執行. (1) 選擇菜單中Debug->Go Main,CCS 自動打開CProgram.c,程序會停在用戶主程序入口 main 上,這從反匯編窗口和CProgram.c 窗口中的指示箭頭位置可以看出. (2) 在內存觀察窗口中觀察變量的值: 選擇"View"菜單中"Memory…"項,在"Memroy Window Options"窗口中的"Adress" 項中輸入&x,單擊"OK"完成設置;"Memory"窗口中x 的當前取值顯示在第1 個地址

的后面. (3) 將變量x,y,z 分別加入觀察窗口: 在源程序中雙擊變量名,再單擊鼠標右鍵,選擇"Add to Watch Window". 這時,這3 個變量還未作初始化. (4) 單步運行3 次,在觀察窗中觀察到變量x,y 被賦值.變化的值被顯示成紅色.同時在 "Memory"窗口中也能觀察到x 和y 值的改變. (5 )再單步運行,可觀察到z的值被計算出來.雙擊觀察窗口中變量x,y 在"Value"欄中 的取值並修改成其他取值,單步運行后觀察結果. (6) 雙擊觀察窗口中變量x,y在"Value"欄中的取值,並修改成0;選擇菜單Debug->Restart, 返回程序起點. (7) 選擇菜單中Debug->Go Main 后,重新單步運行程序,觀察在CPU 寄存器窗口中,各 寄存器使用情況,觀察哪個寄存器參與了運算. 7.內存映像文件 . (1) 選擇菜單Project->Build Options…,啟動"Build Options"工程設置對話框. (2) 單擊"Linker"屬性頁,在"Map Filename"項中觀察生成的map 文件名和路徑. (3) 單擊"取消"退出. 8.對照觀察 文件和cmd 文件的內容 .對照觀察map 文件和 (1) 選擇菜單File->Open…,將找到 C:\ICETEK\VC5416AES61\VC5416AES61\Lab0102-CProgram\Debug 目錄,將文件類型改為 "Memory Map Files",選擇CProgram.map 文件,打開. (2) 打開CProgram.cmd 文件. (3) 程序的入口地址:map 文件中"ENTRY POINT SYMBOL"中說明了程序入口地址 (_c_int00). (4) 內存使用情況: - map 文件中"MEMORY CONFIGURATION"標明了程序占用PRAM 的使用情況,共占 用0beH 個存儲單元. - 觀察map 文件中的"SECTION ALLOCATION MAP"段,可以看出CProgram.obj 的入 口地址為100H,這也是main 函數的入口地址. - 用戶堆棧段從3023H 開始,程序運行到main 函數中后,變量x,y,z 均開設在棧中. - 還能看出程序運行都需要調用rts.lib 中的哪些模塊. 9.改變內存分配 . 修改cmd 文件中的 PRAM: o=0x100, l=0x2000 改為 PRAM: o=0x200, l=0x1f00 重新編譯工程,觀察map 文件中有何變化.

 

 

在看《深入理解計算機系統》的時候總會看到MAP文件,一直不懂,今天看到一片相關的文章,收錄下來,大家一起看看吧。畢竟有些時候我們的IDE提示的信息不夠明白,使用MAP文件是一個調試的好幫手。

僅通過崩潰地址找出源代碼的出錯行

作為程序員,我們平時最擔心見到的事情是什么?是內存泄漏?是界面不好看?……錯啦!我相信我的看法是不會有人反對的——那就是,程序發生了崩潰!

“該程序執行了非法操作,即將關閉。請與你的軟件供應商聯系。”,呵呵,這句 M$ 的“名言”,恐怕就是程序員最擔心見到的東西了。有的時候,自己的程序在自己的機器上運行得好好的,但是到了別人的機器上就崩潰了;有時自己在編寫和測試的過程中就莫名其妙地遇到了非法操作,但是卻無法確定到底是源代碼中的哪行引起的……是不是很痛苦呢?不要緊,本文可以幫助你走出這種困境,甚至你從此之后可以自豪地要求用戶把崩潰地址告訴你,然后你就可以精確地定位到源代碼中出錯的那行了。(很神奇吧?呵呵。)

首先我必須強調的是,本方法可以在目前市面上任意一款編譯器上面使用。但是我只熟悉 M$ 的 VC 和 MASM ,因此后面的部分只介紹如何在這兩個編譯器中實現,請讀者自行融會貫通,掌握在別的編譯器上使用的方法。

Well,廢話說完了,讓我們開始! :)

首先必須生成程序的 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 中,我們要設置編譯和連接參數,我通常是這樣做的:

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 文件了。

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

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

OK,通過上面的步驟,我們已經得到了 MAP 文件,那么我們該如何利用它呢?

讓我們從簡單的實例入手,請打開你的 VC ,新建這樣一個文件:

01  //****************************************************************
02  //程序名稱:演示如何通過崩潰地址找出源代碼的出錯行
03  //作者:羅聰
04  //日期:2003-2-7
05  //出處:http://www.luocong.com(老羅的繽紛天地)
06  //本程序會產生“除0錯誤”,以至於會彈出“非法操作”對話框。
07  //“除0錯誤”只會在 Debug 版本下產生,本程序為了演示而盡量簡化。
08  //注意事項:如欲轉載,請保持本程序的完整,並注明:
09  //轉載自“老羅的繽紛天地”(http://www.luocong.com
10  //****************************************************************
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 。

再看看它的 MAP 文件:(由於文件內容太長,中間沒用的部分我進行了省略)

CrashDemo

Timestamp 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
0001:00000070       _main                      00401070 f   CrashDemo.obj
0004:00000000       __IMPORT_DESCRIPTOR_KERNEL32 00424000     kernel32:KERNEL32.dll
0004:00000014       __NULL_IMPORT_DESCRIPTOR   00424014     kernel32:KERNEL32.dll
0004:00000138       __imp__GetCommandLineA@0   00424138     kernel32:KERNEL32.dll
0004:0000013c       __imp__GetVersion@0        0042413c     kernel32:KERNEL32.dll
0004:00000140       __imp__ExitProcess@4       00424140     kernel32:KERNEL32.dll
0004:00000144       __imp__DebugBreak@0        00424144     kernel32:KERNEL32.dll
0004:00000148       __imp__GetStdHandle@4      00424148     kernel32:KERNEL32.dll
0004:0000014c       __imp__WriteFile@20        0042414c     kernel32:KERNEL32.dll
0004:00000150       __imp__InterlockedDecrement@4 00424150     kernel32:KERNEL32.dll
0004:00000154       __imp__OutputDebugStringA@4 00424154     kernel32:KERNEL32.dll
0004:00000158       __imp__GetProcAddress@8    00424158     kernel32:KERNEL32.dll
0004:0000015c       __imp__LoadLibraryA@4      0042415c     kernel32:KERNEL32.dll
0004:00000160       __imp__InterlockedIncrement@4 00424160     kernel32:KERNEL32.dll
0004:00000164       __imp__GetModuleFileNameA@12 00424164     kernel32:KERNEL32.dll
0004:00000168       __imp__TerminateProcess@8  00424168     kernel32:KERNEL32.dll
0004:0000016c       __imp__GetCurrentProcess@0 0042416c     kernel32:KERNEL32.dll
0004:00000170       __imp__UnhandledExceptionFilter@4 00424170     kernel32:KERNEL32.dll
0004:00000174       __imp__FreeEnvironmentStringsA@4 00424174     kernel32:KERNEL32.dll
0004:00000178       __imp__FreeEnvironmentStringsW@4 00424178     kernel32:KERNEL32.dll
0004:0000017c       __imp__WideCharToMultiByte@32 0042417c     kernel32:KERNEL32.dll
0004:00000180       __imp__GetEnvironmentStrings@0 00424180     kernel32:KERNEL32.dll
0004:00000184       __imp__GetEnvironmentStringsW@0 00424184     kernel32:KERNEL32.dll
0004:00000188       __imp__SetHandleCount@4    00424188     kernel32:KERNEL32.dll
0004:0000018c       __imp__GetFileType@4       0042418c     kernel32:KERNEL32.dll
0004:00000190       __imp__GetStartupInfoA@4   00424190     kernel32:KERNEL32.dll
0004:00000194       __imp__HeapDestroy@4       00424194     kernel32:KERNEL32.dll
0004:00000198       __imp__HeapCreate@12       00424198     kernel32:KERNEL32.dll
0004:0000019c       __imp__HeapFree@12         0042419c     kernel32:KERNEL32.dll
0004:000001a0       __imp__VirtualFree@12      004241a0     kernel32:KERNEL32.dll
0004:000001a4       __imp__RtlUnwind@16        004241a4     kernel32:KERNEL32.dll
0004:000001a8       __imp__GetLastError@0      004241a8     kernel32:KERNEL32.dll
0004:000001ac       __imp__SetConsoleCtrlHandler@8 004241ac     kernel32:KERNEL32.dll
0004:000001b0       __imp__IsBadWritePtr@8     004241b0     kernel32:KERNEL32.dll
0004:000001b4       __imp__IsBadReadPtr@8      004241b4     kernel32:KERNEL32.dll
0004:000001b8       __imp__HeapValidate@12     004241b8     kernel32:KERNEL32.dll
0004:000001bc       __imp__GetCPInfo@8         004241bc     kernel32:KERNEL32.dll
0004:000001c0       __imp__GetACP@0            004241c0     kernel32:KERNEL32.dll
0004:000001c4       __imp__GetOEMCP@0          004241c4     kernel32:KERNEL32.dll
0004:000001c8       __imp__HeapAlloc@12        004241c8     kernel32:KERNEL32.dll
0004:000001cc       __imp__VirtualAlloc@16     004241cc     kernel32:KERNEL32.dll
0004:000001d0       __imp__HeapReAlloc@16      004241d0     kernel32:KERNEL32.dll
0004:000001d4       __imp__MultiByteToWideChar@24 004241d4     kernel32:KERNEL32.dll
0004:000001d8       __imp__LCMapStringA@24     004241d8     kernel32:KERNEL32.dll
0004:000001dc       __imp__LCMapStringW@24     004241dc     kernel32:KERNEL32.dll
0004:000001e0       __imp__GetStringTypeA@20   004241e0     kernel32:KERNEL32.dll
0004:000001e4       __imp__GetStringTypeW@16   004241e4     kernel32:KERNEL32.dll
0004:000001e8       __imp__SetFilePointer@16   004241e8     kernel32:KERNEL32.dll
0004:000001ec       __imp__SetStdHandle@8      004241ec     kernel32:KERNEL32.dll
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.dll

entry point at        0001:000000f0


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 

如果仔細瀏覽 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$ 還是有意無意間提供了很多有價值的信息給我們的,只是我們往往不懂得怎么利用而已……相信這樣一來,你就可以更為從容地面對“非法操作”提示了。你甚至可以要求用戶提供崩潰的地址,然后就可以坐在家中舒舒服服地找到出錯的那行,並進行修正。

是不是很爽呢? :) 

 

 

付:MAP文件是dsp開發軟件編譯后產生的有關DSP用到所有程序、數據及IO空間的一種映射文件。
有時候,僅僅根據ide提供的調試信息不足以找到錯誤的發生地,可以通過看map文件的信息進行查找,找到自己程序崩潰地的地址。

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM