【每周分享】详解MCU编译生成的map文件,原来这么多内容
2026/8/16 2:57:34 网站建设 项目流程
临近放假,得空研究了一下MCU编译生成的map文件,之前主要是通过这个文件查看RAM和Flash的使用情况,其他的内容倒是没怎么仔细去关注,最近研究了一下,与大家分享。
以Keil编译为例,在工程指定的文件夹下面(一般是listings文件夹)会生成一个map文件,这里的map其实是映像的意思,所以一般称之为映像文件即可:

我们先来总结一下映像文件的作用,概括一下,主要涉及以下这个方面:
1. 内存使用分析
查看各段(Section)大小:代码段(.text)、数据段(.data)、未初始化数据段(.bss)、堆栈等;
检查内存溢出:确认是否超出芯片RAM/Flash容量;
优化内存布局:调整关键代码/数据位置。
2. 调试和定位问题
查找函数/变量地址:将运行时地址映射回源代码;
分析链接错误:如符号重复定义、未解析引用;
定位崩溃位置:通过崩溃地址找到对应函数。
3. 优化代码
识别占用大的模块:找出消耗Flash/RAM最多的文件/函数;
评估优化效果:比较优化前后的内存变化;
管理库依赖:查看链接了哪些库及其大小。
4. 可以查看关键信息
映像文件通常包含以下内容:
- Memory Configuration:内存区域定义
- Linker script info:链接脚本配置
- Section Placement:各段分配地址和大小
- Symbol Table:所有符号(函数/变量)地址
- Cross Reference:模块间引用关系
- Size Breakdown:各模块/文件大小统计
接下来我们以一个实际的映像文件来说明一下相关内容。
打开映像文件后,第一行有下面这样一句话:

其实这句话包含两个信息:
编译器版本:ARM Compiler 5.06 update 7 (build960);
链接器工具:armlink [4d3601],这里的armlink是ARM Compiler工具链中的一部分,负责将编译后的对象文件或者目标文件(.o 或 .obj 文件)和其他必要的库文件链接成最终的可执行文件或库文件。
然后便是“Section Cross References”,即前文所述的段(模块)之间的引用关系:

最开始的内容就是说明了main引用了哪些模块或文件的哪些接口:
比如main文件引用了app_di文件的di_thread接口;
比如main文件引用了app_do文件的do_thread接口;
等等。
而main.o(i.cpu_thread)的括号里面的指的是main里面的哪个接口进行了引用。
在这个里面你可以找到函数之间的调用关系,是不是很Nice?
这部分内容的篇幅大小取决于代码的复杂程度,一般会比较多的,像我这个的话,占了800多行吧。
然后便是“Removing Unused input sections fromthe image”,这部分主要是编译器移除你的工程中没有使用到的文件:

每行的开始都带着“Removing”字样,在该部分的结尾的地方也可以看到下面这样一条内容以说明有多少个段区及总共有多少字节的数据量被移除:

这里表示链接器移除了未使用的段区,共489个,总计18906字节,其实这有助于减少最终映像的大小。
再往下就是“Image Symbol Table”,可以翻译为“映像符号表”,映像符号表其实是 Keil MDK 等嵌入式开发工具生成的 .map 文件中的核心部分,它详细列出了程序中所有符号(如变量、函数、代码段等)在内存中的地址、类型和大小信息,是分析程序内存布局、进行调试和优化的关键依据:

另外,映像符号表主要分为‌Local Symbols(局部符号)‌ 和‌Global Symbols(全局符号)‌ 两类,它们在作用域、内容和用途上存在明显区别。
其中‌Local Symbols(局部符号)大概包括下面几种内容:
a、源文件路径和编译信息

从左到右依次为符号名称,地址,符号类型,大小和所属的目标文件。
这里的符号类型要说明一下,常见的主要有四种,分别是Number‌、‌Section‌、‌Thumb Code‌ 和 ‌Data:
Number‌:
表示一个具有特定数值的标签或地址标识,通常用于定义常量或绝对地址,‌不占用实际的存储空间‌。其大小字段通常为0。
‌Section‌:
标识一个代码段或数据段的‌起始地址‌,通常用于标记函数或变量的入口点。
‌Thumb Code‌:
表示使用‌ARM Thumb‌指令集‌编译的机器代码,通常是程序中的函数体。
‌Data:
表示程序中的‌数据符号‌,如全局变量或静态变量。其大小 字段表示该变量占用的字节数。
b、编译器生成的段区地址和大小
列出了各个段区的地址、大小和所属的目标文件。例如:

startup_at32f403a_407 .o(RESET)位于0x08000000,大小为388字节;
以点号.开头(如.text、.data、.bss);
表示各个段区的起始地址和大小;
这些信息对于链接器布局内存至关重要。
c、Thumb Code

d、局部变量或静态变量信息

其中Global Symbols(全局符号)的各个参数说明与局部符号一样:

全局符号其实是链接器可见的、可以在不同模块间相互引用的符号,构成了程序的公共接口,主要包括:
全局函数;
全局变量;
外部可见的符号;
中断向量表中的符号。
a、代码类符号,主要就是函数
类型为Thumb Code,地址在flash区域,大小则表示函数编译后的机器码字节数:

b、数据类符号,主要就是变量

接下来就是“Memory Map of the image”部分,也就是映像内存分布,是映像文件中最核心的内存布局描述内容,是分析程序内存占用的关键依据,有助于开发者明确程序各段在存储器中的物理位置,加载时与运行时的内存映射关系,以及RAM中动态分配区域的边界,辅助排查内存溢出问题。
在这部分的开始,可以看到下面的内容:

Image Entry point:
指的是映射的入口地址是0x08020185;
Load Region LR_IROM1 (Base: 0x08020000,Size: 0x000092c4, Max: 0x00020000, ABSOLUTE, COMPRESSED[0x00008e84]):
表示Flash的起始地址(基地址)是0x08020000,其实如果没有Bootloader程序,这里的其实地址应该是0x08000000,因为配置了偏移地址为0x20000,即APP程序从0x08020000开始:

Size为0x000092c4,即表示已使用0x000092c4/1024=36.6KB;
Max为0x00020000,即表示最大容量为0x00020000/1024=128KB。
Execution Region ER_IROM1表示Flash中的执行区域,其实这是实际在Flash中运行的部分:

各个参数的含义如下:

Execution Region RW_IRAM1表示RAM区域

Exec base:RAM执行地址;
Load base:初始值在Flash中的位置;
Size:已使用的大小0x00001620,即0x00001620/1024 = 5.53KB;
Max:最大容量0x00038000,即0x00038000/1024 = 224KB,也就是此款MCU的RAM容量;
COMPRESSED :压缩的意思,即这些数据的初始值在Flash中被压缩存储,启动时解压到RAM。
到了最后一部分了,即“Image component sizes”,映像组件大小,其实这块是映像文件比较实用的一部分,它告诉我们程序的每个部分占了多少的RAM或者Flash空间,特别是MCU内存资源有限的时候,有助于我们查看和优化代码:

各个参数的含义参考如下:

在下面你可以看到总计信息:

还可以看到RAM和Flash的总计信息:

这个里面细分了很多数据类型,有兴趣的可以继续了解一下,有助于代码优化。
以上这些内容基本上就是整个映像文件的所有内容信息,里面包含了各个函数和各个变量的地址信息,如果真要继续展开来讲,那简直是太多了。
当遇到程序崩溃或者宕机的时候,可以根据崩溃的地址信息到映像文件里查看地址对应的函数,以尽量定位出现程序问题的接口位置,有助于我们更快更精准的解决Bug。或者当你需要优化代码,以节约内存空间的时候,也可以来翻阅映像文件。
另外,其实在Keil仿真的时候,我们也可以查看函数的地址信息,将鼠标放在函数调用的地方,就可以显示看到:

然后到映像文件里面看下这个函数的地址是不是这个:

地址确实是一样的。
不仅如此,在Keil里的MemoryWindow里面也可以直接输入地址进行查看:

总之,很多时候,你可以结合Keil的很多窗口信息和映像同步进行查看和问题排查。
反正映像文件的内容实在是太多了,我都快解读到晕了~~网上也有很多好的资料可以学习。


---------------------
作者:dffzh
链接:https://bbs.21ic.com/icview-3505686-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询