map文件及相关概念
map文件是通过编译器编译之后,集程序、数据及IO空间的一种映射文件。
段(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文件包含
map文件的内容可分为如下几部分:
1、节区的跨文件引用(Section Cross References)
这部分主要是不同文件中函数的调用关系,详细列出了各个.o 文件之间的符号引用。由于.o 文件是由 asm 或 c/c++源文件编译后生成的,各个文件及文件内的节区间互相独立,链接器根据它们之间的互相引用链接起来,链接的详细信息在这个分区中详细列出。例如:
***main.o(.text) refers to delay.o(.text) for delay_ms***
值得注意的是,在构建工程的时候,有时会出现 “Undefined symbol xxx (referred from xxx.o)” 这样的错误提示,就是因为链接过程中,某个文件无法从工程所包含的外部找到它引用的标号,因而产生链接错误。
2、删除无用节区(Removing Unused input ps from the image)
map 文件的第二部分是删除无用节区的说明,这部分列出了在链接过程它发现工程中未被引用的节区,这些未被引用的节区将会被删除(指不加入到*.axf 文件,不是指在*.o 文件删除),这样可以防止这些无用数据占用程序空间。
例如,上面的信息中Removing stm32f4xx_rtc.o(.rev16_text), (4 bytes).就说明stm32f4xx_rtc.o中节区被删除了,也就是我们工程中没有引用。 由此也可以知道,虽然我们把 STM32 标准库的各个外设对应的 c 库文件都添加到了工程,但不必担心这会使工程变得臃肿,因为未被引用的节区内容不会被加入到最终的机器码文件中。 另外,STM32 keil5编译优化,可以勾选如下选型 其作用是,编译器在处理一个 c 文件的时候呢,如果这个选项不选,那么这个C文件中的所有函数在编译后只会产生一个叫 .text 的输出节;如果选了呢,那么每个函数将会产生一个输出节,如果你用C写了一个函数,那么编译器将会产生一个叫 相应的输出节。勾选后,编译器产生的输出节,粒度小多了,便于找到没有使用的函数,将它从最后输出文件中删除。这样,你最后产生的可执行文件大小将会变小。 上述选项如果没有勾选,map文件中删除无用节区部分是按照文件优化的,只要该文件中有函数被使用,该文件中所有函数都会被编译进去;勾选后,由于每个函数一个输出节,所以优化是按照函数优化的,没有使用的函数都没有编译进去。
3、符号映像表(Image Symbol Table (Local Symbols Global Symbols)
这个表列出了被引用的各个符号在存储器中的具体地址、占据的空间大小等信息。 分为Local Symbols局部 和 Global Symbols全局。 Local Symbols记录了用static声明的全局变量地址和大小,C文件中函数的地址和用static声明的函数代码大小,汇编文件中的标号地址(作用域限本文件); Global Symbols记录了全局变量的地址和大小,C文件中函数的地址及其代码大小,汇编文件中的标号地址(作用域全工程)。 1、Symbol Name:符号名称 2、Value:存储对应的地址;会发现有0x0800xxxx、0x2000xxxx这样的地址。 0x0800xxxx指存储在FLASH里面的代码、变量等。0x2000xxxx指存储在内存RAM中的变量Data等。 3、Ov Type:符号对应的类型,大概有几种:Number、Section、Thumb Code、Data等;细心的朋友会发现:全局、静态变量等位于0x2000xxxx的内存RAM中。 4、Size:存储大小,这个容易理解,我们怀疑内存溢出,可以查看代码存储大小来分析。 5、Object(Section):当前符号所在段名这里一般指所在模块(所在源文件)。 例如我们可以查到OV767_Init 符号存储在 0x080096dd地址,它属于 Thumb Code 类型,大小为 228字节,它所在的节区为 OV7670.o 文件节区。
4、存储器映像索引(Memory Map of the image)
映像文件可以分为加载域(Load Region)和运行域(Execution Region) 。 加载域反映了ARM可执行映像文件的各个段存放在存储器中的位置关系 简单的说,加载域就是程序在 Flash 中的实际存储,而运行域是芯片上电后的运行状态,通过下面的框图可以有一个感性的认识: 通过上面的框图可以看出,RW 区也是要存储到 ROM/Flash 里面的,在执行映像之前,必须将已初始化的 RW 数据从 ROM 中复制到 RAM 中的执行地址并创建 ZI Section(初始化为 0 的变量区)。 工程的存储器映像索引分为 ER_IROM1 及 RW_IRAM1 部分,它们分别对应 STM32内部 FLASH 及 SRAM 的空间。 相对于符号映像表,这个索引表描述的单位是节区,而且它描述的主要信息中包含了节区的类型及属性,由此可以区分 Code、 RO-data、 RW-data及 ZI-data。
5、映像组件大小(Image component sizes)
map 文件的最后一部分是包含映像组件大小的信息(Image component sizes),这也是最常查询的内容。 这部分包含了各个使用到的*.o 文件的空间汇总信息、整个工程的空间汇总信息以及占用不同类型存储器的空间汇总信息,它们分类描述了具体占据的 Code、 RO-data、 RW-data及 ZI-data 的大小,并根据这些大小统计出占据的 ROM 总空间。
输出字段内涵: Code (inc. Data) : 显示代码占用了多少字节。 在此映像中,有19442字节的代码, 其中包括1832字节的内联数据 (inc. data),例如文字池和短字符串。 RO Data : 显示只读数据占用了多少字节(比如const char buf[] = “123456”)。这是除 Code (inc. data) 列中包括的内联数据之外的数据。 RW Data : 显示读写数据占用了多少字节。 ZI Data : 显示零初始化的数据占用了多少字节。 Debug : 显示调试数据占用了多少字节,例如,调试输入节以及符号和字符串。 Object Totals : 显示链接到一起以生成映像的对象占用了多少字节。 (incl. Generated): 链接器会生成的映像内容,例如,交互操作中间代码。 如果 Object Totals 行包含此类型的数据,则会显示在该行中。本例中共有 1016 字节的 RO 数据,其中32字节是链接器生成的 RO 数据。 (incl. Padding) : 链接器根据需要插入填充,以强制字节对齐。 Grand Totals: 显示映像的真实大小。 ELF Image Totals: ELF(Executable and Linking Format)可执行链接格式映像文件大小。 ROM Totals: 显示包含映像所需的 ROM的最小大小。这不包括 ZI数据和存储在ROM 中的调试信息。
补充: A.Code、Ro-data:位于FLASH中; B.RW-data、ZI-data:位于RAM中; C.RW-data已初始化的数据会存储在Flash中,上电会从FLASH搬移至RAM。
关系如下: RO Size = Code + RO Data RW Size = RW Data + ZI Data ROM Size = Code + RO Data + RW Data
———————————————————————————————————— 引用声明:本文为学习引用以下博主文章,并予以感谢。 1.CSDN博主「嵌入式大杂烩」的原创文章, 原文链接:https://blog.csdn.net/zhengnianli/article/details/106684347 2.CSDN博主「银耳的脸」的原创文章, 原文链接:https://blog.csdn.net/qq_43761970/article/details/84337089 3.CSDN博主「qlexcel」的原创文章, 原文链接:https://blog.csdn.net/qlexcel/article/details/78884379
|