map文件解析

admin 2025年5月15日10:21:38评论0 views字数 4633阅读15分26秒阅读模式

来源:大国物联网

我们在使用Keil MDK编译ARM处理器的应用程序时,程序编译完成后会在下方的Build Output窗口中出现下图的信息提示,其中红色框框的内容对我们尤为重要;Code、RO-data、RW-data、ZI-data的含义理解以及程序最终占用处理器中的Flash空间大小和SRAM空间大小如何?今天就此问题跟大家进行详细的讨论,希望可以对大家有所帮助。

map文件解析

一、含义解释:

Code:即代码域,它通常是指编译器生成的机器指令,这些内容会被存储到ROM区。

RO-data:Read Only data,即只读数据域,它指程序中用到的只读数据,这些数据被存储在ROM区,因而程序不能被修改的内容。例如C语言中const关键字定义的变量就是典型的RO-data。

RW-data:Read Write data,即可读写数据域,它指初始化为“非0值”的可读写数据,程序刚运行时,这些数据具有非0的初始值,程序运行的时候它们又会常驻在RAM区,应用程序可以修改其内容。例如C语言中定义的全局变量,且定义时赋予“非0值”给该变量。

ZI-data:Zero Initialie data,即0初始化数据,它指初始化为“0值”的可读写数据域,它与RW-data的区别是程序刚运行时这些数据初始值全都为0,程序运行时和RW-data的性质一样,它们也常驻在RAM区,应用程序可以更改其内容。例如C语言中使用定义的全局变量,且定义时赋予“0值”给该变量(如若定义该变量时没有赋予初始值,编译器会把它当ZI-data来对待,初始化为0);

为什么RW-data与ZI-data区别存储的?因为在RAM创建数据的时候,默认值为0,但如果有的数据要求初值非0,那就需要使用ROM记录该初始值,运行时再复制到RAM中。 
芯片的内部FLASH占用的空间是Code、RO-data及RW-data的总和,所以如果这些内容比芯片的FLASH空间大,程序就无法被正常保存在芯片的FLASH了。当程序在执行的时候,需要占用内部SRAM空间(即RAM区),占用的空间包括RW-data和ZI-data空间之和。

一、要让Keil生成map文件,要设置:

map文件解析

再重新编译,没有错误后,就会生成map文件了。

二、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同义

三、文件分析流程

第一部分:Section Cross References(模块、段的交叉引用关系)主要是各个源文件生成的模块之间相互引用的关系。“refer to”是引用的意思,比如:

main.o(i.main) refers to led.o(i.LED_Init) for LED_Init

首先main.c和led.c会被编译成目标文件main.o和led.o。i.main是main.c中main函数的入口(也是main函数编译出的段,函数编译后以段的形式存在,函数之间的引用,也就是段与段之间的引用)。i.LED_Init是led.c中LED_Init函数的入口(也是LED_Init函数编译出的段)。因此上面这句话意思就是main.c中的main函数引用了led.c中的LED_Init函数,剩下的基本都是这类的意思。

  1. main.o(i.main) refers to sys.o(i.Cache_Enable) for Cache_Enable

  2. main.o(i.main) refers to stm32h7xx_hal.o(i.HAL_Init) for HAL_Init

  3. main.o(i.main) refers to sys.o(i.Stm32_Clock_Init) for Stm32_Clock_Init

  4. main.o(i.main) refers to delay.o(i.delay_init) for delay_init

  5. main.o(i.main) refers to usart.o(i.uart_init) for uart_init

  6. main.o(i.main) refers to led.o(i.LED_Init) for LED_Init

其中有关于启动代码的引用说明:

  1. stm32f10x_vector.o(.text) refers to __main.o(!!!main) for __main

  2. __main.o(!!!main) refers to kernel.o(.textfor __rt_entry

  3. kernel.o(.text) refers to usertask.o(.textfor main

上面这几个对于程序意义比较重大用户在启动代码中调用了__main.o模块中的__main函数,__main又调用了kernel.o中的__rt_entry函数,最后kernel.o又调用了用户定义的main主函数。第二部分:Removing Unused input sections from the image(移除未使用的段)就是将库中没有用到的函数从可执行映像中删除掉,减小程序的体积。

  1.     Removing os_mbox.o(.text), (1094 bytes).

  2.     Removing os_mutex.o(.text), (1744 bytes).

  3.     Removing os_sem.o(.text), (1016 bytes).

最后一栏有个总的统计结果:

2737 unused section(s) (total 289508 bytes) removed from the image.

总共移除了2737个未使用的段,共289508字节。第三部分:Image Symbol Table(映射符号表,列出了各个段所存储的对应地址)分为Local Symbols局部 和 Global Symbols全局。

Local Symbols记录了用static声明的全局变量地址和大小,C文件中函数的地址和用static声明的函数代码大小,汇编文件中的标号地址(作用域限本文件),下面是部分截图:

map文件解析

Global Symbols记录了全局变量的地址和大小,C文件中函数的地址及其代码大小,汇编文件中的标号地址(作用域全工程),下面是部分截图:

map文件解析

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):当前符号所在段名

这里一般指所在模块(所在源文件)。第四部分:Memory Map of the image(映像的内存分布)

映像文件可以分为加载域(Load Region)和运行域(Execution Region):加载域反映了ARM可执行映像文件的各个段存放在存储器中的位置关系。下面是部分截图,另外映像中的入口点就是程序开始执行的位置。

map文件解析

1、Exec Addr:运行域地址

2、Load Addr:加载域地址

3、Size:存储大小

4、Type:类型

Data:数据类型

Code:代码类型

Zero:未初始化变量类型

PAD:这个类型在map文件中放在这个位置,其实它不能算这里的类型。要翻译的话,只能说的“补充类型”。

ARM处理器是32位的,如果定义一个8位或者16位变量就会剩余一部分,这里就是指的“补充”的那部分,会发现后面的其他几个选项都没有对应的值。

运行域反映了ARM可执行映像文件各个段真正执行时在存储器中的位置关系:

map文件解析

加载域就是程序在Flash中的实际存储,而运行域是芯片上电后的运行状态,因为MCU没上电时RAM中没有数据,所以此时所有的东西(包括代码、变量、初始值等)都是存放在flash中的,当上电后又要把变量等复制到RAM中才能正常运行。

map文件解析

通过上面的框图可以看出,RW区也是要存储到ROM/Flash里面的。在执行映像之前,必须将已初始化的RW数据从ROM中复制到RAM中的执行地址并创建ZI Section(初始值为0的变量区),这样才算完成了MCU运行的准备。

第五部分:Image component sizes(映像组成大小)map文件解析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) :链接器根据需要插入填充,以强制字节对齐。

下面的Library Totals显示已提取并作为单个对象添加到映像中的库成员占用了多少字节。

map文件解析

下面是整个映像文件的总结说明:

map文件解析

Grand Totals:显示映像的真实大小。

ELF Image Totals:ELF(Executable and Linking Format)可执行链接格式映像文件大小。

ROM Totals:显示包含映像所需的 ROM的最小大小。这不包括 ZI数据和存储在ROM 中的调试信息。

了解map文件有利于我们分析内存和flash的使用情况。

比如编译一个基础工程RAM的空间也占用了1.6K左右的原因,因为堆栈的空间均分配在RAM中

map文件解析

若工程中使用的局部变量较多,定义的数据长度较大时,若不调整栈的空间大小,则会导致程序出现栈溢出,程序运行结果与预期的不符或程序跑飞。这时我们就需要手动的调整栈的大小。

当工程中使用了malloc动态分配内存空间时,这时分配的空间就为堆的空间。所以若默认的堆空间大小不满足工程需求时,就需要手动调整堆空间的大小。

STM32有两种调节堆栈空间大小的方式:

1. 直接在启动文件中修改堆栈空间的大小;

2. 打开启动文件,点击下方Configuration Wizard,可在Option的设置框中设置堆栈空间的大小。

map文件解析

原文始发于微信公众号(汽车电子嵌入式):map文件解析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月15日10:21:38
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   map文件解析https://cn-sec.com/archives/4066271.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息