STM32 map文件解析
什么是map文件?
簡單的說:map文件是通過編譯器編譯之后,集程序、數(shù)據(jù)及IO空間的一種映射文件。
Keil生成map文件的設置如下圖所示:
生成文件保存路徑與Listings選擇路徑相同。
keil 主要包含配置:
Memory Map:內(nèi)存映射
Callgraph:圖像映射
Symbols:符號
Cross Reference:交叉引用
Size Info:大小信息
Totals Info:統(tǒng)計信息
Unused Section Info:未調(diào)用模塊信息
Veneers Info:裝飾信息
【如何查看map文件】
1.到對應文件生成文件目錄下查看(KEIL即為Listings目錄)
2.雙擊工程目標名則會打開對應工程的map文件(需要是編譯完成的情況下)
MAP文件基本概念
段(section):描述映像文件的代碼和數(shù)據(jù)塊
RO:Read-Only的縮寫,包括RO-data(只讀數(shù)據(jù))和RO-code(代碼)
RW:Read-Write的縮寫,主要是RW-data,RW-data由程序初始化初始值
ZI:Zero-initialized的縮寫,主要是ZI-data,由編譯器初始化為0。
.text:與RO-code同義
.constdata:與RO-data同義
.bss:與ZI-data同義
.data:與RW-data同義
文件分析
map文件分為如下五大部分內(nèi)容:
1.Section Cross References:模塊、段(入口)交叉引用
2.Removing Unused input sections from the image:移除未調(diào)用模塊
3.Image Symbol Table:映射符號表
4.Memory Map of the image:內(nèi)存(映射)分布
5.Image component sizes:存儲組成大小
第一部分:Section Cross References(模塊、段的交叉引用關系)
主要是各個源文件生成的模塊之間相互引用的關系。“refer to”是引用的意思,比如:
首先main.c和led.c會被編譯成目標文件main.o和led.o。i.main是main.c中main函數(shù)的入口(也是main函數(shù)編譯出的段,函數(shù)編譯后以段的形式存在,函數(shù)之間的引用,也就是段與段之間的引用)。i.LED_Init是led.c中LED_Init函數(shù)的入口(也是LED_Init函數(shù)編譯出的段)。因此上面這段的意思就是main.c中的main函數(shù)引用了led.c中的LED_Init函數(shù),該部分剩余的其他語句基本都類似。
第二部分:Removing Unused input sections from the image(移除未使用的段)
就是將庫中沒有用到的函數(shù)從可執(zhí)行映像中刪除掉,減小程序的體積。
刪除(指不加入到*.axf 文件,不是指在*.o 文件刪除),這樣可以防止這些無用數(shù)據(jù)占用程序空間
最后一欄有個總的統(tǒng)計結(jié)果:
總共移除了666個未使用的段,共51615字節(jié)。
第三部分:Image Symbol Table(映射符號表,列出了各個段所存儲的對應地址)
分為Local Symbols局部 和 Global Symbols全局。
局部標號(用static聲明的全局變量地址和大小,C文件中函數(shù)的地址和用static聲明的函數(shù)代碼大小,匯編文件中的標號地址(作用域限本文件)
全局標號,全局變量的地址和大小,C文件中函數(shù)的地址及其代碼大小,匯編文件中的標號地址(作用域全工程)
Local Symbols記錄了用static聲明的全局變量地址和大小,C文件中函數(shù)的地址和用static聲明的函數(shù)代碼大小,匯編文件中的標號地址(作用域限本文件),下面是部分截圖:
1、Symbol Name:符號名稱
2、Value:存儲對應的地址;
大家會發(fā)現(xiàn)有0x0800xxxx、0x2000xxxx這樣的地址。
0x0800xxxx指存儲在FLASH里面的代碼、變量等。
0x2000xxxx指存儲在內(nèi)存RAM中的變量Data等。
3、Ov Type:符號對應的類型
符號類型大概有幾種:Number、Section、Thumb Code、Data等;
細心的朋友會發(fā)現(xiàn):全局、靜態(tài)變量等位于0x2000xxxx的內(nèi)存RAM中。
4、Size:存儲大小
這個容易理解,我們懷疑內(nèi)存溢出,可以查看代碼存儲大小來分析。
5、Object(Section):當前符號所在段名
這里一般指所在模塊(所在源文件)。
舉例:
LED_Init 符號存儲在 0x08001861 地址,它屬于 Thumb Code 類型,大小為 62 字節(jié),它所在的節(jié)區(qū)為 led.o 文件的 i.LED_Init 節(jié)區(qū)。
以下這段位于Local Symbols中,可以判斷LCD_Gpio_Init是一段static的函數(shù),函數(shù)占用flash 150字節(jié),在lcd.o中定義了。
堆和棧也在Local Symbols中,可以看出HEAP和STACK大小分別是512字節(jié)和1024字節(jié)。
lcd_buf位于Local Symbols中,因此lcd_buf是一個static變量,大小為1152字節(jié),符號類型是Data,在bss段可以判定沒有初始化,【bss(Block Started by Symbol segment):未初始化靜態(tài)變量, 未初始化數(shù)據(jù)】,定義在lcd.o文件
第四部分:Memory Map of the image(映像的內(nèi)存分布)
映像文件可以分為加載域(Load Region)和運行域(Execution Region):加載域反映了ARM可執(zhí)行映像文件的各個段存放在存儲器中的位置關系。下面是部分截圖,另外映像中的入口點就是程序開始執(zhí)行的位置。
1、Exec Addr:運行域地址
2、Load Addr:加載域地址
3、Size:存儲大小
4、Type:類型
Data:數(shù)據(jù)類型
Code:代碼類型
Zero:未初始化變量類型
PAD:這個類型在map文件中放在這個位置,其實它不能算這里的類型。要翻譯的話,只能說的“補充類型”。
ARM處理器是32位的,如果定義一個8位或者16位變量就會剩余一部分,這里就是指的“補充”的那部分,會發(fā)現(xiàn)后面的其他幾個選項都沒有對應的值。
5、Attr 屬性 RO:存儲與ROM中的段;RW:存儲與RAM中的段
6、Section Name 段名
7、Object 目標
運行域反映了ARM可執(zhí)行映像文件各個段真正執(zhí)行時在存儲器中的位置關系:
指加載域位于LR_IROM1開始地址0x08000000,大小有0x000122a8,這塊區(qū)域最大為0x00080000
Execution Region ER_IROM1 (Exec base: 0x08000000, Load base: 0x08000000, Size: 0x0001213c, Max: 0x00080000, ABSOLUTE) Execution Region RW_IRAM1 (Exec base: 0x20000000, Load base: 0x0801213c, Size: 0x00011de0, Max: 0x00018000, ABSOLUTE, COMPRESSED[0x00000064])執(zhí)行區(qū)域:對應我們目標配置中的區(qū)域
【補充:加載域和運行域】
加載域就是程序在Flash中的實際存儲,而運行域是芯片上電后的運行狀態(tài),因為MCU沒上電時RAM中沒有數(shù)據(jù),所以此時所有的東西(包括代碼、變量、初始值等)都是存放在flash中的,當上電后又要把變量等復制到RAM中才能正常運行。
通過上面的框圖可以看出,RW區(qū)也是要存儲到ROM/Flash里面的。在執(zhí)行映像之前,必須將已初始化的RW數(shù)據(jù)從ROM中復制到RAM中的執(zhí)行地址并創(chuàng)建ZI Section(初始值為0的變量區(qū)),這樣才算完成了MCU運行的準備。
第五部分:Image component sizes(映像組成大小)
Code (inc. Data) :顯示代碼占用了多少字節(jié)。 在此映像中,有19442字節(jié)的代碼, 其中包括1832字節(jié)的內(nèi)聯(lián)數(shù)據(jù) (inc. data),例如文字池和短字符串。
RO Data :顯示只讀數(shù)據(jù)占用了多少字節(jié)(比如const char buf[] = “123456”)。這是除 Code (inc. data) 列中包括的內(nèi)聯(lián)數(shù)據(jù)之外的數(shù)據(jù)。
RW Data :顯示讀寫數(shù)據(jù)占用了多少字節(jié)。
ZI Data :顯示零初始化的數(shù)據(jù)占用了多少字節(jié)。
Debug :顯示調(diào)試數(shù)據(jù)占用了多少字節(jié),例如,調(diào)試輸入節(jié)以及符號和字符串。
Object Totals :顯示鏈接到一起以生成映像的對象占用了多少字節(jié)。
(incl. Generated):鏈接器會生成的映像內(nèi)容,例如,交互操作中間代碼。 如果 Object Totals 行包含此類型的數(shù)據(jù),則會顯示在該行中。本例中共有 1016 字節(jié)的 RO 數(shù)據(jù),其中32字節(jié)是鏈接器生成的 RO 數(shù)據(jù)。
(incl. Padding) :鏈接器根據(jù)需要插入填充,以強制字節(jié)對齊。
最后是整個映像文件的總結(jié)說明:
Grand Totals:顯示映像的真實大小。
ELF Image Totals:ELF(Executable and Linking Format)可執(zhí)行鏈接格式映像文件大小。
ROM Totals:顯示包含映像所需的 ROM的最小大小。這不包括 ZI數(shù)據(jù)和存儲在ROM 中的調(diào)試信息。
【補充:KEIL 計算程序占用大小】
如圖是MDK編譯之后的信息:
Code:指代碼的大小;
Ro-data:指除了內(nèi)聯(lián)數(shù)據(jù)(inline data)之外的常量數(shù)據(jù);
RW-data:指可讀寫(RW)、已初始化的變量數(shù)據(jù);
ZI-data:指未初始化(ZI)的變量數(shù)據(jù);
提醒:
A.Code、Ro-data:位于FLASH中;
B.RW-data、ZI-data:位于RAM中;
C.RW-data已初始化的數(shù)據(jù)會存儲在Flash中,上電會從FLASH搬移至RAM。
占用的FLASH = Code + RO-data + RW-data
占用的SRAM = RW-data + ZI-data
(為什么RW-data既在FLASH中又在SRAM?—因為RW-data已初始化的數(shù)據(jù)會存儲在Flash中,上電會從FLASH搬移至RAM。)
關系如下:
RO Size = Code + RO Data
RW Size = RW Data + ZI Data
ROM Size = Code + RO Data + RW Data
(此數(shù)據(jù)在map文件末尾就可以查看)
【補充:內(nèi)存分布】
text:程序 文字區(qū)段(text segment)也稱為程序段(code segment),存放可執(zhí)行命令(instructions)。
該區(qū)段通常位于 heap 或 stack 之後,避免因 heap 或 stack 溢出或者覆蓋 CPU 指令。
data:初始化靜態(tài)變量 初始化數(shù)據(jù)區(qū)段(initialized data segment)存儲已經(jīng)初始化的靜態(tài)變量,例如有經(jīng)過初始化的 C 語言的全局變量(global variables)以及靜態(tài)變量(static variables),分為RW-data和RO-data。
bss(Block Started by Symbol segment):未初始化靜態(tài)變量 未初始化數(shù)據(jù)
stack:棧(stack segment)用于存儲函數(shù)的局部變量,以及各種函數(shù)調(diào)用時需要存儲的信息(比如函數(shù)返回的存儲器地址、函數(shù)狀態(tài)等),每一次的函數(shù)調(diào)用就會在棧上建立一個 stack frame(棧幀),存儲該次調(diào)用的所有數(shù)據(jù)于狀態(tài),這樣以來同一個函數(shù)被調(diào)用多次時,就會有不同棧幀,不會相互干擾。
heap:堆是用于存放進程運行中被動態(tài)分配的內(nèi)存段,它的大小并不固定,可動態(tài)擴張或縮減,例如 C 語言的 malloc 以及 C++ 的 new 所建立的變量都是儲存于此。
棧(Stack)一般的狀態(tài)會從高地址往低地址生長,而 heap 相反。
前面提到的運行域從Section Name也可以印證上面的內(nèi)存分布圖。
參考鳴謝:
https://blog.csdn.net/qlexcel/article/details/78884379
https://neyzoter.cn/2019/04/25/Map-Of-Keil/#3%E5%86%85%E5%AD%98%E7%A7%BB%E9%99%A4%E6%8E%92%E6%9F%A5%E6%96%B9%E6%B3%95
https://www.cnblogs.com/chengeputongren/p/12177423.html
https://blog.csdn.net/IT_B_O_Y/article/details/93722989
https://zhuanlan.zhihu.com/p/108358035
https://www.cnblogs.com/ycpkbql/p/9160192.html
總結(jié)
以上是生活随笔為你收集整理的STM32 map文件解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言 十六进制整数字符串转十进制整数
- 下一篇: C语言 内联函数