前言:
爲了方便查看博客,特意申請了一個公衆號,附上二維碼,有興趣的朋友可以關注,和我一起討論學習,一起享受技術,一起成長。
本文轉載自:第48章 MDK的編譯過程及文件類型全解—零死角玩轉STM32-F429系列
1. Listing目錄下的文件
在 Listing 目錄下包含了 .map 及 .lst 文件,它們都是文本格式的,可使用 Windows 的記事本軟件打開。其中 lst 文件僅包含了一些彙編符號的鏈接信息,這裏重點分析 map 文件。
map 文件是由鏈接器生成的,它主要包含交叉鏈接信息,查看該文件可以瞭解工程中各種符號之間的引用以及整個工程的 Code、RO-data、RW-data 以及 ZI-data 的詳細及彙總信息。它的內容中主要包含了"節區的跨文件引用"、“刪除無用節區”、“符號映像表”、“存儲器映像索引"以及"映像組件大小”,各部分介紹如下:
1.1 節區的跨文件引用
打開"多彩流水燈 .map" 文件,可看到它的第一部分——節區的跨文件引用(Section Cross References):
Section Cross References
startup_stm32f429_439xx.o(RESET) refers to startup_stm32f429_439xx.o(STACK) for __initial_sp
startup_stm32f429_439xx.o(RESET) refers to startup_stm32f429_439xx.o(.text) for Reset_Handler
startup_stm32f429_439xx.o(RESET) refers to stm32f4xx_it.o(i.NMI_Handler) for NMI_Handler
startup_stm32f429_439xx.o(RESET) refers to stm32f4xx_it.o(i.HardFault_Handler) for HardFault_Handler
/**...以下部分省略****/
main.o(i.main) refers to bsp_led.o(i.LED_GPIO_Config) for LED_GPIO_Config
main.o(i.main) refers to stm32f4xx_gpio.o(i.GPIO_ResetBits) for GPIO_ResetBits
main.o(i.main) refers to main.o(i.Delay) for Delay
main.o(i.main) refers to stm32f4xx_gpio.o(i.GPIO_SetBits) for GPIO_SetBits
bsp_led.o(i.LED_GPIO_Config) refers to stm32f4xx_rcc.o(i.RCC_AHB1PeriphClockCmd) for RCC_AHB1PeriphClockCmd
bsp_led.o(i.LED_GPIO_Config) refers to stm32f4xx_gpio.o(i.GPIO_Init) for GPIO_Init
bsp_led.o(i.LED_GPIO_Config) refers to stm32f4xx_gpio.o(i.GPIO_ResetBits) for GPIO_ResetBits
/**...以下部分省略****/
======================================================================
在這部分中,詳細列出了各個 .o 文件之間的符號引用。由於 .o 文件是由 asm 或 c/c++ 源文件編譯後生成的,各個文件及文件內的節區間互相獨立,鏈接器根據它們之間的互相引用鏈接起來,鏈接的詳細信息在這個 "Section Cross References"一一列出。
例如,開頭部分說明的是 startup_stm32f429_439xx.o 文件中的 “RESET” 節區分爲它使用的 “__initial_sp” 符號引用了同文件 “STACK” 節區。
也許我們對啓動文件不熟悉,不清楚這究竟是什麼,那我們繼續瀏覽,可看到main.o 文件的引用說明,如說明 main.o 文件的 i.main 節區爲它使用的LED_GPIO_Config 符號引用了 bsp_led.o 文件的 i.LED_GPIO_Config 節區。
同樣地,下面還有 bsp_led.o 文件的引用說明,如說明了 bsp_led.o 文件的 i.LED_GPIO_Config 節區爲它使用的 GPIO_Init 符號引用了 stm32f4xx_gpio.o 文件的 i.GPIO_Init 節區。
可以瞭解到,這些跨文件引用的符號其實就是源文件中的函數名、變量名。有時在構建工程的時候,編譯器會輸出 “Undefined symbol xxx (referred from xxx.o)” 這樣的提示,該提示的原因就是在鏈接過程中,某個文件無法在外部找到它引用的標號,因而產生鏈接錯誤。例如,見下圖,把 bsp_led.c 文件中定義的函數 LED_GPIO_Config 改名爲 LED_GPIO_ConfigABCD,而不修改 main.c 文件中的調用,就會出現 main 文件無法找到 LED_GPIO_Config 符號的提示。
1.2 刪除無用節區
map 文件的第二部分是刪除無用節區的說明 (Removing Unused input sections from the image.)。
=================================================================
Removing Unused input sections from the image.
Removing startup_stm32f429_439xx.o(HEAP), (512 bytes).
Removing system_stm32f4xx.o(.rev16_text), (4 bytes).
Removing system_stm32f4xx.o(.revsh_text), (4 bytes).
Removing system_stm32f4xx.o(.rrx_text), (6 bytes).
Removing system_stm32f4xx.o(i.SystemCoreClockUpdate), (136 bytes).
Removing system_stm32f4xx.o(.data), (20 bytes).
Removing misc.o(.rev16_text), (4 bytes).
Removing misc.o(.revsh_text), (4 bytes).
Removing misc.o(.rrx_text), (6 bytes).
Removing misc.o(i.NVIC_Init), (104 bytes).
Removing misc.o(i.NVIC_PriorityGroupConfig), (20 bytes).
Removing misc.o(i.NVIC_SetVectorTable), (20 bytes).
Removing misc.o(i.NVIC_SystemLPConfig), (28 bytes).
Removing misc.o(i.SysTick_CLKSourceConfig), (28 bytes).
Removing stm32f4xx_adc.o(.rev16_text), (4 bytes).
Removing stm32f4xx_adc.o(.revsh_text), (4 bytes).
Removing stm32f4xx_adc.o(.rrx_text), (6 bytes).
Removing stm32f4xx_adc.o(i.ADC_AnalogWatchdogCmd), (16 bytes).
Removing stm32f4xx_adc.o(i.ADC_AnalogWatchdogSingleChannelConfig), (12 bytes).
Removing stm32f4xx_adc.o(i.ADC_AnalogWatchdogThresholdsConfig), (6 bytes).
Removing stm32f4xx_adc.o(i.ADC_AutoInjectedConvCmd), (24 bytes).
/**...以下部分省略****/
========================================================================
這部分列出了在鏈接過程它發現工程中未被引用的節區,這些未被引用的節區將會被刪除(指不加入到 .axf 文件,不是指在 .o 文件刪除),這樣可以防止這些無用數據佔用程序空間。
例如,上面的信息中說明 startup_stm32f429_439xx.o 中的 HEAP (在啓動文件中定義的用於動態分配的"堆"區)以及 stm32f4xx_adc.o 的各個節區都被刪除了,因爲在我們這個工程中沒有使用動態內存分配,也沒有引用任何 stm32f4xx_adc.c 中的內容。由此也可以知道,雖然我們把 STM32 標準庫的各個外設對應的 C 庫文件都添加到了工程,但不必擔心這會使工程變得臃腫,因爲未被引用的節區內容不會被加入到最終的機器碼文件中。
1.3 符號映像表
map 文件的第三部分是符號映像表 (Image Symbol Table),分爲 Local Symbols 局部 和 Global Symbols 全局。
Local Symbols 記錄了用 static 聲明的全局變量地址和大小,C 文件中函數的地址和用 static 聲明的函數代碼大小,彙編文件中的標號地址(作用域限本文件)。
==============================================================================
Image Symbol Table
Local Symbols
Symbol Name Value Ov Type Size Object(Section)
../clib/microlib/init/entry.s 0x00000000 Number 0 entry.o ABSOLUTE
../clib/microlib/init/entry.s 0x00000000 Number 0 entry9a.o ABSOLUTE
../clib/microlib/init/entry.s 0x00000000 Number 0 entry9b.o ABSOLUTE
/*...省略部分*/
LED_GPIO_Config 0x080002a5 Thumb Code 106 bsp_led.o(i.LED_GPIO_Config)
MemManage_Handler 0x08000319 Thumb Code 2 stm32f4xx_it.o(i.MemManage_Handler)
NMI_Handler 0x0800031b Thumb Code 2 stm32f4xx_it.o(i.NMI_Handler)
PendSV_Handler 0x0800031d Thumb Code 2 stm32f4xx_it.o(i.PendSV_Handler)
RCC_AHB1PeriphClockCmd 0x08000321 Thumb Code 22 stm32f4xx_rcc.o(i.RCC_AHB1PeriphClockCmd)
SVC_Handler 0x0800033d Thumb Code 2 stm32f4xx_it.o(i.SVC_Handler)
SysTick_Handler 0x08000415 Thumb Code 2 stm32f4xx_it.o(i.SysTick_Handler)
SystemInit 0x08000419 Thumb Code 62 system_stm32f4xx.o(i.SystemInit)
UsageFault_Handler 0x08000469 Thumb Code 2 stm32f4xx_it.o(i.UsageFault_Handler)
__scatterload_copy 0x0800046b Thumb Code 14 handlers.o(i.__scatterload_copy)
__scatterload_null 0x08000479 Thumb Code 2 handlers.o(i.__scatterload_null)
__scatterload_zeroinit 0x0800047b Thumb Code 14 handlers.o(i.__scatterload_zeroinit)
main 0x08000489 Thumb Code 270 main.o(i.main)
/*...省略部分*/
==============================================================================
這個表列出了被引用的各個符號在存儲器中的具體地址、佔據的空間大小等信息。如我們可以查到 LED_GPIO_Config 符號存儲在 0x080002a5 地址,它屬於 Thumb Code 類型,大小爲 106 字節,它所在的節區爲 bsp_led.o 文件的 i.LED_GPIO_Config 節區。
Global Symbols 記錄了全局變量的地址和大小,C 文件中函數的地址及其代碼大小,彙編文件中的標號地址(作用域全工程)。
Symbol Name Value Ov Type Size Object(Section)
BuildAttributes$$THM_ISAv4$P$D$K$B$S$PE$A:L22UL41UL21$X:L11$S22US41US21$IEEE1$IW$USESV6$~STKCKD$USESV7$~SHL$OSPACE$ROPI$EBA8$MICROLIB$REQ8$PRES8$EABIv2 0x00000000 Number 0 anon$$obj.o ABSOLUTE
__ARM_use_no_argv 0x00000000 Number 0 main.o ABSOLUTE
_printf_a 0x00000000 Number 0 stubs.o ABSOLUTE
_printf_c 0x00000000 Number 0 stubs.o ABSOLUTE
_printf_charcount 0x00000000 Number 0 stubs.o ABSOLUTE
_printf_d 0x00000000 Number 0 stubs.o ABSOLUTE
_printf_e 0x00000000 Number 0 stubs.o ABSOLUTE
_printf_f 0x00000000 Number 0 stubs.o ABSOLUTE
_printf_flags 0x00000000 Number 0 stubs.o ABSOLUTE
_printf_fp_dec 0x00000000 Number 0 stubs.o ABSOLUTE
//省略。。。。。。。。。。。。。。。。。
bsp_RunPer1ms 0x08000665 Thumb Code 2 bsp.o(i.bsp_RunPer1ms)
main 0x08000667 Thumb Code 14 main.o(i.main)
Region$$Table$$Base 0x08000674 Number 0 anon$$obj.o(Region$$Table)
Region$$Table$$Limit 0x08000694 Number 0 anon$$obj.o(Region$$Table)
g_iRunTime 0x20000008 Data 4 bsp_timer.o(.data)
__initial_sp 0x20009978 Data 0 startup_stm32f10x_hd.o(STACK)
==============================================================================
各字段解釋:
名稱 | 解釋 |
---|---|
Symbol Name | 符號名稱,分爲 Local,Global |
Value | 存儲對應地址,0x0800xxxx 指存儲在 FLASH 裏面的代碼、變量等;0x2000xxxx 指存儲在內存 RAM 中的變量 Data 等 |
Ov Type | 符號對應的類型:Number、Section、Thumb Code、Data 等幾種;可以發現全局、靜態變量等位於 0x2000xxxx 的內存 RAM 中。 |
Size | 存儲大小 |
Object(Section) | 當前符號所在段名,對應函數、變量所在的源文件 |
1.4 存儲器映像索引
map 文件的第四部分是存儲器映像索引 (Memory Map of the image)。
映像文件可以分爲加載域(Load Region)和運行域(Execution Region)。
加載域:反映了 ARM 可執行映像文件的各個段存放在存儲器中的位置關係,映像中的入口點就是程序開始執行的位置。
運行域:反映了 ARM 可執行映像文件各個段真正執行時在存儲器中的位置關係。
加載域就是程序在 Flash 中的實際存儲,而運行域是芯片上電後的運行狀態。因爲 MCU 沒上電時 RAM 中沒有數據,所以此時所有的東西(包括代碼、變量、初始值等)都是存放在 Flash 中的,當上電後又要把變量等複製到 RAM 中才能正常運行。
==============================================================================
Memory Map of the image
Image Entry point : 0x080001ad
Load Region LR_IROM1 (Base: 0x08000000, Size: 0x000005b0, Max: 0x00100000, ABSOLUTE)
7 Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x000005b0, Max: 0x00100000, ABSOLUTE)
Base Addr Size Type Attr Idx E Section Name Object
0x08000000 0x000001ac Data RO 3 RESET startup_stm32f429_439xx.o
/*..省略部分*/
0x0800020c 0x00000012 Code RO 5161 i.Delay main.o
0x0800021e 0x0000007c Code RO 2046 i.GPIO_Init stm32f4xx_gpio.o
0x0800029a 0x00000004 Code RO 2053 i.GPIO_ResetBits stm32f4xx_gpio.o
0x0800029e 0x00000004 Code RO 2054 i.GPIO_SetBits stm32f4xx_gpio.o
0x080002a2 0x00000002 Code RO 5196 i.HardFault_Handler stm32f4xx_it.o
0x080002a4 0x00000074 Code RO 5269 i.LED_GPIO_Config bsp_led.o
0x08000318 0x00000002 Code RO 5197 i.MemManage_Handler stm32f4xx_it.o
/*..省略部分*/
0x08000488 0x00000118 Code RO 5162 i.main main.o
0x080005a0 0x00000010 Data RO 5309 Region$$Table anon$$obj.o
Execution Region RW_IRAM1 (Base: 0x20000000, Size: 0x00000400, Max: 0x00030000, ABSOLUTE)
Base Addr Size Type Attr Idx E Section Name Object
0x20000000 0x00000400 Zero RW 1 STACK startup_stm32f429_439xx.o
==============================================================================
本工程的存儲器映像索引分爲 ER_IROM1 及 RW_IRAM1 部分,它們分別對應STM32 內部 FLASH 及 SRAM 的空間。相對於符號映像表,這個索引表描述的單位是節區,而且它描述的主要信息中包含了節區的類型及屬性,由此可以區分 Code、RO-data、RW-data 及 ZI-data。
例如,從上面的表中我們可以看到 i.LED_GPIO_Config 節區存儲在內部 FLASH 的 0x080002a4 地址,大小 爲0x00000074,類型爲 Code,屬性爲 RO。而程序的 STACK 節區(棧空間)存儲在 SRAM 的 0x20000000 地址,大小爲 0x00000400,類型爲 Zero,屬性爲 RW(即RW-data)。
1.5 映像組件大小
map 文件的最後一部分是包含映像組件大小的信息 (Image component sizes),這也是最常查詢的內容。
==============================================================================
Image component sizes
4 Code (inc. data) RO Data RW Data ZI Data Debug Object Name
116 10 0 0 0 578 bsp_led.o
298 10 0 0 0 1459 main.o
36 8 428 0 1024 932 startup_stm32f429_439xx.o
132 0 0 0 0 2432 stm32f4xx_gpio.o
18 0 0 0 0 3946 stm32f4xx_it.o
28 6 0 0 0 645 stm32f4xx_rcc.o
292 34 0 0 0 253101 system_stm32f4xx.o
----------------------------------------------------------------------
926 68 444 0 1024 263093 Object Totals
0 0 16 0 0 0 (incl. Generated)
6 0 0 0 0 0 (incl. Padding)
/*...省略部分*/
==============================================================================
Code (inc. data) RO Data RW Data ZI Data Debug
1012 84 444 0 1024 262637 Grand Totals
1012 84 444 0 1024 262637 ELF Image Totals
1012 84 444 0 0 0 ROM Totals
==============================================================================
Total RO Size (Code + RO Data) 1456 ( 1.42kB)
Total RW Size (RW Data + ZI Data) 1024 ( 1.00kB)
Total ROM Size (Code + RO Data + RW Data) 1456 ( 1.42kB)
==============================================================================
這部分包含了各個使用到的 .o 文件的空間彙總信息、整個工程的空間彙總信息以及佔用不同類型存儲器的空間彙總信息,它們分類描述了具體佔據的 Code、RO-data、RW-data 及 ZI-data 的大小,並根據這些大小統計出佔據的 ROM 總空間。
我們僅分析最後兩部分信息,如 Grand Totals 一項,它表示整個代碼佔據的所有空間信息,其中 Code 類型的數據大小爲1012字節,這部分包含了 84 字節的指令數據 (inc .data) 已算在內,另外 RO-data 佔 444 字節,RW-data 佔 0 字節,ZI-data 佔 1024 字節。在它的下面兩行有一項 ROM Totals 信息,它列出了各個段所佔據的 ROM 空間,除了 ZI-data 不佔 ROM 空間外,其餘項都與 Grand Totals 中相等 (RW-data 也佔據 ROM 空間,只是本工程中沒有 RW-data 類型的數據而已)。
最後一部分列出了只讀數據 (RO)、可讀寫數據 (RW) 及佔據的 ROM 大小。其中只讀數據大小爲 1456 字節,它包含 Code 段及 RO-data 段; 可讀寫數據大小爲 1024 字節,它包含 RW-data 及 ZI-data 段;佔據的 ROM 大小爲 1456 字節,它除了 Code 段和 RO-data 段,還包含了運行時需要從 ROM 加載到 RAM 的 RW-data 數據。
綜合整個 map 文件的信息,可以分析出,當程序下載到 STM32 的內部 FLASH 時,需要使用的內部 FLASH 是從 0x0800 0000 地址開始的大小爲 1456 字節的空間;當程序運行時,需要使用的內部 SRAM 是從 0x20000000 地址開始的大小爲 1024 字節的空間。
粗略一看,發現這個小程序竟然需要 1024 字節的 SRAM,實在說不過去,但仔細分析 map 文件後,可瞭解到這 1024 字節都是 STACK 節區的空間(即棧空間),棧空間大小是在啓動文件中定義的,這 1024 字節是默認值 (0x00000400)。它是提供給 C 語言程序局部變量申請使用的空間,若我們確認自己的應用程序不需要這麼大的棧,完全可以修改啓動文件,把它改小一點,查看前面講解的 htm 靜態調用圖文件可瞭解靜態的棧調用情況,可以用它作爲參考。