本文简单介绍一下map文件,该文件提供了不少重要信息,在调试过程中能提供不少帮助
map文件是什么?
.map 文件对应的中文名应该是映射文件,用来展示(映射)项目构建的链接阶段的细节。通常包含程序的全局符号、交叉引用和内存映射等
map文件如何打开?
一般情况下,双击工程名就能打开map文件
若无法打开map,部分Keil的配置项可能做了修改,需要手动打开下面的选项
也可以在KEIL工程的MDK-ARM\Listings路径下面找到map文件
什么场景下面需要用到map文件来辅助调试呢?
大致总结一下,可以看着在查看内存布局,优化程序空间占用,甚至是HardFault硬件错误中断时,Map文件都能用做辅助调试
map 基本概念解释
段(section):描述映像文件的代码和数据块。
RO:Read-Only的缩写,包括RO-data(只读数据)和RO-code(代码).RW:Read-Write的缩写,主要是RW-data,RW-data由程序初始化初始值
Z1 : Zero-initialized的缩写,主要是ZI-data,由编译器初始化为0。
.text:与RO-code同义.
constdata:与RO-data同义。
bss. :与ZI-data同义。
data :与RW-data同义
map文件的组成
- 程序段交叉引用关系 描述各文件之间函数调用关系
以下面这句为例:
1 | main.o(.text.RCC_Configuration) refers to n32g45x_rcc.o(.text.RCC_EnableAHBPeriphClk) for RCC_EnableAHBPeriphClk |
上面这个条目表示
main.c 中的 RCC_Configuration 函数调用了 n32g45x_rcc.c 中的 RCC_EnableAHBPeriphClk 函数
2. 删除映像未使用的程序段
描述工程中未用到而被删除的冗余程序段(函数/数据)(比如说定义了变量temp,但是后面并没有使用temp,MAP文件将变量temp删除,生成一个被删除的列表,也可以是函数,表示未被是使用。)
这个部分非常有意义,通过删除这些冗余的部分,可以有效降低代码量,特别是对一些资源紧张的MCU
如果发现空间不够,可以尝试在Keil的配置页面里面勾选上这个,大多数情况下都能显著减少固件的大小。(将每个函数编译到独立的 .text.函数名 段中,而非全部合并到单一的 .text 段,以实现更精细的代码优化和链接控制)
3. 映像符号表
描述各符号(程序段/数据)在存储器中的地址、类型、大小等
这里在分成了两个部分:Local Symbols、Global Symbols
如果在main.c里面全局定义
1 | __IO uint32_t gCnt = 0; |
可以在map文件中Global Symbols部分查看到
1 | gCnt 0x20000050 Data 4 main.o(.bss.gCnt) |
如果在main.c里面给gCnt作用域做限定
1 | static __IO uint32_t gCnt = 0; |
可以在map文件中Local Symbols部分查看到
1 | gCnt 0x20000050 Data 4 main.o(.bss.gCnt) |
证明 gCnt 已被正确分配到 RAM 的 .bss 段
4. 映像内存分布图
描述各个程序段(函数)在存储器中的地址及占用大小
这个部分可以分为加载域Load Region和运行域Execution Region
加载域反映了ARM可执行映像文件各个段存放在存储器中时的位置关系。
运行域反映了ARM可执行映像文件各个段真正执行时在存储器中的位置关系。
可以看到程序入口地址Image Entry point : 0x08000199
为啥不是0x08000000呢?
可以在上面提到的Global Symbols中找到答案
__Vectors_Size 的大小刚好就是0x00000198,这部分就是中断向量表
5. 映像组件大小 给出整个映像代码(.o)占用空间汇总信息