map文件浅析
map文件简介
内容
首先我们需要了解map文件是什么,map文件是通过编译器编译之后,集程序、数据及IO空间的一种映射文件;一般出现内存越界或溢出,又或者是代码优化的情况,首先想到的就是分析map文件,通过map文件可以知道函数大小,入口地址等一些重要信息,总共分为五大类
- Section Cross References:模块、段(入口)交叉引用
- Removing Unused input sections from the image:移除未调用模块
- Image Symbol Table:映射符号表
- Memory Map of the image:内存(映射)分布
- Image component sizes:存储组成大小
配置
其次就是配置,由于笔者这里大多数是使用keil,所有就展示的keil的配置(虽然大概率就是已经配置好的);路径是先点击魔法棒,然后Listing选项卡中,最后在Linker Listing:map.map勾选即可
- Memory Map:内存映射
- Callgraph:图像映射
- Symbols:符号
- Cross Reference:交叉引用
- Size Info:大小信息
- Totals Info:统计信息
- Unused Section Info:未调用模块信息
- Veneers Info:装饰信息

最后的map文件可以在~\xxx\MDK-ARM\xxx中找到
概念
最后是关于map文件基本概念
- 段(section):描述映像文件的代码和数据块
- RO:Read-Only的缩写,包括RO-data(只读数据)和RO-code(代码)
- RW:Read-Write的缩写,主要是RW-data,RW-data由程序初始化初始值
- ZI:Zero-initialized的缩写,主要是ZI-data,由编译器初始化为0
- .text:与RO-code同义
- .constdata:与RO-data同义
- .bss:与ZI-data同义
- .data:与RW-data同义
map文件内容详解
Section Cross References—模块、段(入口)交叉引用
当我们打开map文件的时候,第一大块就是Section Cross References,主要内容是是各个源文件生成的模块、段(定义的入口)之间相互引用的关系
例如: main.o(i.SystemClock_Config) refers to stm32f1xx_hal_rcc.o(i.HAL_RCC_OscConfig) for HAL_RCC_OscConfig,意思是main模块中SystemClock_Config引用了 stm32f1xx_hal_rcc模块中HAL_RCC_OscConfig函数
需要注意的是,在map文件中i指的是独立函数,是一种编译器优化编译的一种方式,可以精确地控制每个函数的位置,或者删除没用的函数,通俗来讲也就是可以在map文件看到更多信息以及更精准的定位
Removing Unused input sections from the image—移除未调用模块
在代码中没有被调用的模块会在map文件生成列表,最后会在这部分显现出来
例如:Removing stm32f1xx_hal_rcc_ex.o(i.HAL_RCCEx_GetPeriphCLKConfig), (44 bytes).,意思是删除了stm32f1xx_hal_rcc_ex模块的HAL_RCCEx_GetPeriphCLKConfig函数,大小是44字节
除此以外,最后会有一个汇总,删除了180个模块,共计7710字节

Image Symbol Table—映射符号表
这部分分为两块,一块是Local Symbols,主要静态函数,变量以及常量;另外一块是Global Symbols,主要是外部函数和全局变量
- Symbol Name:符号的名称以及路径
- Value:地址
- Type:符号的类型,常见的大概是四种
- Number
- Section
- Thumb Code
- Data
- Size:大小,单位是字节
- Object(Section):表示符号所在的目标文件及其对应的段
我们从这部分可以看出一些函数或变量的地址

Memory Map of the image—内存(映射)分布
在执行映像之前,必须将已初始化的RW数据从ROM中复制到RAM中的执行地址并创建ZI Section(初始化为0的变量区)
这里引用rtthread文档中的内存分布图

STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 地址和大小分配出 ZI 段,并将这块 RAM 区域清零
- Exec Addr:执行地址,即当程序运行时,该段在内存中将被加载到的地址
- Load Addr:加载地址,即当程序从存储器(如 Flash)加载到内存时,该段最初加载到的地址,由上图可以知道,启动之后RO是不会变的,但RW是会移动到RAM里面,所以在RO中Exec Addr和Load Addr是一致的,但RW会不一样且Load Addr比Exec Addr大
- Size:段的大小,以字节为单位
- Type:段的类型
- Code:表示可执行代码
- Data:表示数据段,包括已初始化的全局变量等
- RO Data:只读数据,如常量、字符串字面值等
- Attr:段的属性,在RO中Attr都是RO,在RW中Attr都是RW
- Idx:索引号,表示该段在符号表中的索引位置,通常用于区分符号表中的不同段
- E Section Name:段的名称
- Object:目标文件

Image component sizes—存储组成大小
- Code (inc. data):代码部分(即可执行指令)及其包含的任何初始化数据的总大小,包括.text段和任何被标记为只读的初始化数据(如.rodata段)
- RO Data:只读数据的总大小。这部分通常包括常量字符串、枚举、常量数组等,存储在.rodata段中
- RW Data:可读可写数据的总大小。这部分包含已初始化的全局变量和静态变量,存储在.data段中
- ZI Data:零初始化数据的总大小。这部分通常包含未初始化的全局变量和静态变量,存储在.bss段中
- Debug:表示与调试相关的数据占用空间,包括调试符号、调试信息
- Object Name:目标文件的名称

RAM=RW=1.62KB
ROM=ROM = 2.44KB
