CM3/4 map文件分析
1. map 文件的MDK设置
- 要生成 map 文件,我们需要在 MDK 的魔术棒→Listing 选项卡里面,进行相关设置。
默认情况下,MDK 这部分设置是全勾选的,如果想取消掉一些信息的输出,则取消相关勾选即可(一般不建议)如图所示:
- 设置好 MDK 以后,全编译当前工程,当编译完成后(无错误),就会生成.map 文件。如图所示:
2. MDK 编译生成文件简介
MDK 编译工程,会生成一些中间文件(如.o、.axf、.map 等),最终生成 hex 文件,以便下载到 MCU 上面执行,编译过程产生的所有文件,都存放在 OBJ 文件夹下,如下图所示:
这里总共生成了 66 个文件,共 11 个类型,分别是:.axf、.crf、.d、.dep、.hex、.lnp、.lst、.o、.htm、
bulild_log.htm 和.map。66 个文件看着不是很多,但是随着工程的增大,这些文件也会越来越多,大项目编译一次,可以生成几百甚至上千个这种文件,不过文件类型基本就是上面这些。
对于 MDK 工程来说,基本上任何工程在编译过程中都会有这 11 类文件,常见的 MDK 编译过程生产文件类型如表所示:
文件类型 说明 .o 可重定向
对象文件,每个源文件(.c/.s 等)编译都会生成一个.o 文件.axf 由 ARMCC 编译生产的可执行对象文件, 不可重定向
(绝对地址),它是由 armlink 链接器,将整个工程参与编译的多个.o 文件链接生成.axf 文件,我们在仿真的时候,需要用到该文件.hex Hex 格式文件,可用于下载到 MCU,可执行对象文件 .hex 由.axf 文件转换而来。.hex 文件和.bin 文件的区别是:.bin 文件不含地址信息,全部都是可执行代码;而.hex 文件则是包含地址信息的可自行代码。同样的.bin 文件也是由.axf 文件转换而来的。我们在使用 ISP 软件进行程序下载的时候,一般使用的是.hex 文件,由 ISP 软件解析.hex 文件包含的地址信息来实现程序下载。而我们在进行 BootLoader 升级的时候,一般使用.bin 文件,地址由 Bootloader 程序指定。 .crf 交叉引用文件,包含浏览信息(定义、标识符、引用) .d 由 ARMCC/GCC 编译生产的依赖文件(.o 文件所对应的依赖文件)每个.o 文件,都有一个对应的.d 文件 .dep 整个工程的依赖文件 .lnp MDK 生成的链接输入文件,用于命令输入 .lst C 语言或汇编编译器生成的列表文件 .htm 它是编译器在编译代码的时候生成的一个列表文件,包含了整个工程的静态调用图,最大的用处就是可以查看栈深度(最小深度), 方便设置栈大小
。.htm 文件可以直接由浏览器打开(双击打开).build_log.htm 最近一次编译工程时的日志记录文件 .map 连接器生成的列表文件/MAP 文件, 该文件对我们非常有用
注1:
可重定向
是指该文件包含数据/代码,但是并没有指定地址,它的地址可由后续链接的时候进行指定。
注2:不可重定
向是指该文件所包含的数据/代码都已经指定地址了,不能再改变。
其他文件类型及说明,请大家参考:MDK→help→uVision Help→B. File Types,如图所示:
注意:.htm 文件包含两部分内容:
- 整个工程最大栈(Stack)深度及其调用关系
我们打开:新建工程例程→Output→XXX.htm 文件(双击,注意:必须整个工程编译一遍,才会生成 XXX.htm 文件,否则是找不到这个文件的!),可以看到如图所示:
例程的最大栈深度是 416 字节,最大栈深时的调用关系为:__rt_entry_main ⇒ main ⇒ sys_stm32_clock_init ⇒ HAL_RCC_ClockConfig ⇒ HAL_InitTick ⇒HAL_NVIC_SetPriority ⇒ __NVIC_SetPriority。
不过需要注意的是,这里的最大栈深度仅仅是最低要求(静态栈),因为它并没有统计无栈深的函数(用内存管理)、递归函数、以及无法追踪的函数(函数指针)等所包含的栈(Stack)。
不过它给我们指明了最低需求,我们在分配栈深度的时候,就可以参考这个值来做设置,一般不低于静态栈的2倍
。默认设置的栈深(Stack_Size)通过startup_stm32f***.s
文件设置。- 各个函数的栈深及其调用关系
.htm 文件还给出了每个函数所使用的栈深度以及其调用关系,如图所示:
3. map 文件的基本概念
Section:
描述映像文件的代码或数据块,我们简称程序段RO:
包括只读数据(RO data)和代码(RO code)两部分内容,占用 FLASH 空间RW:
包含可读写数据(RW data,有初值,且不为 0),占用FLASH(存储初值)和 RAM(读写操作)ZI:
Zero initialized 的缩写,包含初始化为 0 的数据(ZI data),占用 RAM 空间.constdata:
相当于 RO data.text:
相当于 RO code.data:
相当于 RW data.bss:
相当于 ZI data
4. map 文件分析
.map 文件是编译器链接时生成的一个文件,它主要包含了交叉链接信息。通过.map 文件,我们可以知道整个工程的函数调用关系、FLASH 和 RAM 占用情况及其详细汇总信息,能具体到单个源文件(.c/.s)的占用情况,根据这些信息,我们可以对代码进行优化。
.map 文件可以分为以下 5 个组成部分:
- 程序段交叉引用关系(Section Cross References)
- 删除映像未使用的程序段(Removing Unused input sections from the image)
- 映像符号表(Image Symbol Table)
- 映像内存分布图(Memory Map of the image)
- 映像组件大小(Image component sizes)
4.1 程序段交叉引用关系(Section Cross References)
在这部分中,详细列出了各个 *.o 文件之间的符号引用。由于 *.o 文件是由 asm 或 c/c++ 源文件(.c/.s 等)编译后生成的,各个文件及文件内的节区间互相独立,链接器根据它们之间的互相引用链接起来,链接的详细信息在
Section Cross References
下面一一列出。
即这部分内容描述了各个文件(.c/.s 等)之间函数(程序段)的调用关系,如图所示:
例如:上图框出部分
main.o(i.main) refers to os_core.o(i.OSInit) for OSInit
表示:
main.c 文件中的 main 函数,调用了 os_core.c 中的 OSInit 函数。
- i.main 表示 main 函数的入口地址;
- i.OSInit 表示 OSInit 函数的入口地址。
这些跨文件引用的符号其实就是源文件中的函数名、变量名。有时在构建工程的时候,编译器会输出Undefined symbol xxx (referred from xxx.o)
这样的提示,该提示的原因就是在链接过程中,某个文件无法在外部找到它引用的标号,因