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 符号的提示。

在这里插入图片描述

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 静态调用图文件可了解静态的栈调用情况,可以用它作为参考。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章