一、什么是Map文件 简单来说,Map文件是编译器编译工程后生成的一个文件,这个文件反映了各个源文件生成的模块间的交叉引用、移除的未使用模块、符合映射表、内存映射以及各个模块的大小和汇总数据等。 所以说,当你在遇到或怀疑存在内存越界或溢出的情况时,首先想到的应该就是分析Map文件,确认嫌疑分子、构建RAM的分布图,还原问题发生的过程,才能从根本上解决问题。 那,我们就一起来看下怎么看Map文件这个问题吧~ 二、如何生成Map文件 根据设置的不同,生成的Map文件包含的内容也不同。如图1所示,在“Options for Target ‘XXX’”窗口的Listing页面,通过勾选不同的项目可以定制Map文件中的记录的内容。 图1 PS:点击快捷工具栏的魔法棒按钮 或 菜单Project->“Options for Target ‘XXX’...”可以打开“Options for Target ‘XXX’”窗口。 设置完,代码编译成功后,在指定目录就可以找到生成的Map文件。 三、Map文件解析 Map文件已经生成了,那么接下来我们一起看下Map是何方神圣。既然要看,就需要先打开Map文件,那么Map文件怎么打开呢? 很简单,打开Map文件的方式有多种,在KEIL的左侧的Project窗口中目标工程上双击即可打开;或者,直接找到Map文件,用文本编辑器等方式都可以打开查看这里就不再赘述。 按照最全的配置,Map文件包括以下几个部分: 1. Section Cross References 主要是指各源文件生成的模块间的相互引用关系。 比如,下面这句表示: spi.c文件编译生成的模块spi.o中调用了stm32f4xx_rcc.c文件编译生成的模块stm32f4xx_rcc.o z中的函数RCC_AHB1PeriphClockCmd。 剩下的也差不多都是这个意思。 Section Cross References……spi.o(.text) refers to stm32f4xx_rcc.o(.text) for RCC_AHB1PeriphClockCmd……2. Removing Unused input sections from the image 将未使用的函数之类的删除,以减少image映像的大小。 Removing Unused input sections from the image.……Removing data_quk.o(.rev16_text), (4 bytes).…… 这个从我个人目前接触的内容看,没用到过,如果XDJM在调试程序的过程中有用到这些信息的场景也希望不吝赐教,我也开阔下视野,多谢~ 3. Image Symbol Table 映像中涉及的符号表,包括局部符号(Local Symbols)和全局符号(Global Symbols)。 Image Symbol Table // 局部符号 Local Symbols // 符号名 // 地址 // 类型 // 大小 Symbol Name Value Ov Type Size Object(Section) ../clib/angel/boardlib.s 0x00000000 Number 0 boardinit1.o ABSOLUTE ..\TASKS\alm_task.c 0x00000000 Number 0 alm_task.o ABSOLUTE ...... HEAP 0x20006248 Section 512 startup_stm32f40_41xxx.o(HEAP) Heap_Mem 0x20006248 Data 512 startup_stm32f40_41xxx.o(HEAP) STACK 0x20006448 Section 2048 startup_stm32f40_41xxx.o(STACK) Stack_Mem 0x20006448 Data 2048 startup_stm32f40_41xxx.o(STACK) __initial_sp 0x20006c48 Data 0 startup_stm32f40_41xxx.o(STACK) // 全局符号 Global Symbols // 符号名 // 地址 // 类型 // 大小 Symbol Name Value Ov Type Size Object(Section) ...... limit_check 0x0800d27d Thumb Code 566 alm_task.o(.text) aaaaa_err 0x20000089 Data 1 global.o(.data) play_cnt 0x2000008a Data 1 global.o(.data) lock_cnt 0x2000008b Data 1 global.o(.data) ...... Region$$Table$$Base 0x0801bf24 Number 0 anon$$obj.o(Region$$Table) Region$$Table$$Limit 0x0801bf44 Number 0 anon$$obj.o(Region$$Table) ...... 注意,这里的符号包括函数名,变量名。局部的static变量和全局变量在这里都可以找到,如果疑似存在内存越界的变量属于这两种类型,那么可以从这里找到他们的地址,看看他上下左右的小伙伴儿都是谁,就能确定嫌疑分子了。 另外,类型包括Number、Section、Thumb Code、Data。其中,Number是指它并不占据程序空间,而只是具有一定数值的符号,类似于程序中用宏定义define和EQU。 4. Memory Map of the image 映像的内存分布,顾名思义,这部分内容主要记录了映像的加载域和运行域的起始地址、大小和最大Size以及各个段的起始地址。 在说介绍之前,我们先了解下这几个段的意义,方便理解: 段名 | 说明 | .constdata | 只读常量数据段,属于RO-data。 | .text | 代码段。 用来存放程序执行代码的内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(当然也有些架构允许代码段为可写,即允许修改程序)。也有可能包含一些只读的常数变量,例如字符串常量等。 | .data | 数据段。 data 段用于存储已经赋初值(非零)的全局变量,且变量占有实际的内存空间。本段的内容由程序初始化,因此会占用exe文件空间。 | .bss | 数据段,Block Started by Symbol。 bss段用于存储未赋初值的全局变量和静态局部变量,这些变量在程序运行前会被初始化为0或NULL。 另外,初始化为零的全局变量和静态局部变量也会存储在bss中的数据不分配实际的空间,只体现为一个占位符,只记录数据所需空间的大小,因此不会占用exe文件空间。 bss段的大小从可执行文件中得到 ,然后链接器得到这个大小的内存块,紧跟在data段后面。 | heap | 堆。 用于存放运行中被动态分配的内存段,可动态扩张或缩减。 例如,malloc分配的内存就在堆上。 | stack | 栈。 用于存放程序临时创建的局部变量,即:函数括弧“{}”中定义的临时变量。注意,不包括用static声明的变量,static声明的变量存储在data段中。当函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。 |
|