天天看點

MDK的編譯過程及檔案類型全解——(三)

前言:

為了友善檢視部落格,特意申請了一個公衆号,附上二維碼,有興趣的朋友可以關注,和我一起讨論學習,一起享受技術,一起成長。

MDK的編譯過程及檔案類型全解——(三)

本文轉載自:第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 符号的提示。

MDK的編譯過程及檔案類型全解——(三)

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 靜态調用圖檔案可了解靜态的棧調用情況,可以用它作為參考。

繼續閱讀