近期有网友问:如何分析 Keil 中的 map 文件?这个文件主要有哪些内容,都代表什么意思?
什么是 map 文件?
简单地说:map 文件是经过 编译器 编译之后,集程序、数据及 IO 空间的一种映射文件。很多经验丰富的工程师在遇到内存越界或溢出时,首先想到的就是分析 map 文件。通过 map 文件可以知道函数大小、入口地址等重要信息。
在 Keil 中最常见的是编译之后,编译窗口会显示类似下面一段关于程序和数据大小的信息:
Program Size: Code=1112 RO-data=320 RW-data=0 ZI-data=1632
这一段提示信息汇总了程序和数据的信息,而这些信息由单个模块汇总而来,在 map 文件里有详细列表。
关于 Keil 中的 map 文件
Keil 软件中的 map 文件,对分析项目中的 bug 很重要,很多内存相关的 bug 都能通过 map 文件进行分析。
如何打开 map 文件
最直接也最简单的办法:双击工程目标,出现 map 文件(注意双击的是工程目标,不是工程或文件组)。如下图:

当然,也可以找到 map 文件的位置(Listings 文件夹),用编辑器打开。
map 文件输出内容配置
map 文件输出信息的配置位于:Project -> Options for Target -> Listing,如下图:

主要包含配置:
- Memory Map:内存映射
- Callgraph:调用图
- Symbols:符号
- Cross Reference:交叉引用
- Size Info:大小信息
- Totals Info:统计信息
- Unused Section Info:未调用模块信息
- Veneers Info:Veneer 信息
可以根据自己的情况,需要输出什么信息就勾选对应的选项。
提示:
map 文件内容分类
从上面的输出配置可以看出 map 文件大概包含哪些信息。map 文件将其分为如下五大类:
- Section Cross References:模块、段(入口)交叉引用
- Removing Unused input sections from the image:移除未调用模块
- Image Symbol Table:映射符号表
- Memory Map of the image:内存(映射)分布
- Image component sizes:存储组成大小
下面针对 Keil MDK、ARM Compiler 5 组件生成的 map 文件五大类内容展开说明。
各个模块说明
1、Section Cross References:模块、段(入口)交叉引用
配置中需勾选 Cross Reference。

Section Cross References 指各个源文件生成的模块、段(定义的入口)之间相互引用的关系。
比如:
main.o(i.System_Initializes) refers to bsp.o(i.BSP_Initializes) for BSP_Initializes
意思是:main 模块(main.o)中的 System_Initializes 函数(i.System_Initializes)引用(或者说调用)了 bsp 模块(bsp.o)中的 BSP_Initializes 函数。
提示:
main.o 是 main.c 源文件生成的目标文件模块;
i.System_Initializes 是 System_Initializes 函数的入口。
配置中需勾选 Unused Sections Info。

这一类很好理解:代码中未被调用的模块(或者说函数)会在 map 文件中生成一个列表。
比如:
Removing stm32f10x_gpio.o(i.GPIO_AFIODeInit), (20 bytes).
意思是:stm32f10x_gpio.c 文件中的 GPIO_AFIODeInit 模块(函数)未被调用,其代码大小为 20 字节。
最后还有一个统计信息:
52 unused section(s) (total 2356 bytes) removed from the image.
表示总共有 52 段没有被调用,未调用部分大小为 2356 字节。
3、Image Symbol Table:映射符号表
配置中需勾选 Symbols。

Image Symbol Table 即映射符号表,也就是各个段所存储对应地址的表(上图中删除了中间部分内容)。
Symbol 分为两大类:
- Local Symbols:局部符号
- Global Symbols:全局符号
Symbol 内容要点:
- Symbol Name:符号名称。名称命名及分类请参考官方文档。
- Value:存储对应的地址。大家会看到
0x0800xxxx、0x2000xxxx 这样的地址。其中 0x0800xxxx 指存储在 FLASH 中的代码、变量等;0x2000xxxx 指存储在 RAM 中的变量 Data 等。
- Ov Type:符号对应的类型。符号类型大概有几种:Number、Section、Thumb Code、Data 等。细心的朋友会发现:全局、静态变量等位于
0x2000xxxx 的 RAM 中。
- Size:存储大小,即当前行 Symbol 占用的大小。
- Object(Section):段目标,这里一般指所在模块(源文件)。
4、Memory Map of the image:内存(映射)分布
配置中需勾选 Memory Map。

Memory Map of the image 即内存(映射)分布,内容相对较多,也是比较重要的一项。
主要介绍:
Image Entry point : 0x08000131:指映射入口地址。
Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00000598, Max: 0x00080000, ABSOLUTE):指加载区域位于 LR_IROM1,起始地址 0x08000000,大小 0x00000598,该区域最大为 0x00080000。
执行区域有两个:
- Execution Region ER_IROM1
- Execution Region RW_IRAM1
这个区域其实对应目标配置中的区域,如下图所示:

内容要点:
- Base Addr:存储地址。
0x0800xxxx 为 FLASH 地址,0x2000xxxx 为 RAM 地址。
- Size:存储大小。
- Type:类型。Data 表示数据类型,Code 表示代码类型,Zero 表示未初始化变量类型,PAD 则是“补充类型”。ARM 处理器是 32 位的,如果定义一个 8 位或 16 位变量就会剩余一部分,这里就是指“补充”的那部分,此时后面的其他几个选项都没有对应的值。
- Attr:属性。RO 表示存储于 ROM 中的段,RW 表示存储于 RAM 中的段。
- Section Name:段名。也可以说是入口分类名,与第一节“Section Cross References”提到的模块、段一样。大概包含:
RESET、.ARM、.text、i、.data、.bss、HEAP、STACK 等。
- Object:目标。
5、Image component sizes:存储组成大小
配置中需勾选 Size Info。

Image component sizes 即存储组成大小,主要是对模块进行汇总存储大小信息。
这一部分内容很好理解。编译工程后,编译窗口一般会看到类似如下信息:
Program Size: Code=1112 RO-data=320 RW-data=0 ZI-data=1632
其中:
- Code:指代码的大小;
- RO-data:指除了内联数据(inline data)之外的常量数据;
- RW-data:指可读写(RW)、已初始化的变量数据;
- ZI-data:指未初始化(ZI)的变量数据。
提醒:
- Code、RO-data 位于 FLASH 中;
- RW-data、ZI-data 位于 RAM 中;
- RW-data 已初始化的数据会存储在 Flash 中,上电后从 FLASH 搬移至 RAM。
关系如下:
RO Size = Code + RO Data
RW Size = RW Data + ZI Data
ROM Size = Code + RO Data + RW Data
上面 map 信息是比较全面的汇总。如果不想看模块明细,只想看汇总统计,可以在配置中只勾选 Totals Info,对比信息如下:

以上是传统分析 map 文件的方法。对于有时间和精力的同学,可以花时间掌握一些主要内容,方便今后自己能看懂 map 文件。
当然,现在 AI 时代,也可以借助 AI 工具分析 map 文件,请参考:看懂.map文件很难?现在AI一键帮你分析了!