ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Cortex-M3-STM32F1 开发:(八)编译生成的 .MAP 中间文件详解

Cortex-M3-STM32F1 开发:(八)编译生成的 .MAP 中间文件详解 上一篇下一篇新建 HAL 工程模板详细的 STM32 启动过程这里写目录标题.MAP 文件1是什么2组成与如何解读3日常排查时怎么快速用.MAP 文件1是什么MDK 在编译的过程中会产生一些中间文件文件类型简介.o可重定向对象文件每个 .c/.s 文件都对应一个 .o 文件.axf可执行对象文件由 .o 文件链接生成仿真的时候需要用到此文件.hexINTELHex 格式文件用于下载到 MCU 运行由 .axf 转换而来.map连接器生成的列表文件对分析程序存储占用情况非常有用其他.crf、.d、.dep、.lnp、.lst、.htm、.build_log.htm等一般用不到.o 文件→ \rightarrow→.axf 文件→ \rightarrow→.hex 文件。其中 .MAP 文件和 .hex 文件是最重要的。配置输出方式单片机开发工具篇十四配置输出 .map 文件及其本地路径-CSDN博客。双击工程文件名即可在 MDK 中打开 .map 文件2组成与如何解读下面以我做过的一个冷链运输项目的 .map 文件为例不过都是 M3 内核MCU 是 STM32F103RCT6 。【免费】冷链运输项目里冷库端的.map文件资源-CSDN下载.MAP 文件组成组成部分简介程序段交叉引用关系Section Cross References描述各文件之间函数调用关系删除映像未使用的程序段Removing Unused input sections from the image.描述工程中未用到而被删除的冗余程序段函数/数据映像符号表Image Symbol TableLocal Symbols、Global Symbols描述各符号程序段/数据在存储器中的地址、类型、大小等映像内存分布图Memory Map of the image描述各个程序段函数在存储器中的地址及占用大小映像组件大小Image component sizes给出整个映像代码.o占用空间汇总信息某个 .map 文件内容如下Component: ARM Compiler 5.06 update 7 (build 960) Tool: armlink [4d3601] Section Cross References ...... usart.o(i.USART1_IRQHandler) refers to usart.o(.bss) for USART_RX_BUF usart.o(i.uart_init) refers to stm32f10x_usart.o(i.USART_Init) for USART_Init usart.o(i.fputc) refers (Special) to use_no_semi_2.o(.text) for __use_no_semihosting ...... pow.o(i.pow) refers to pow.o(.constdata) for .constdata ...... _printf_d.o(.ARM.Collect$$…) refers (Weak) to _printf_dec.o(.text) for _printf_int_dec ...... ...... Removing Unused input sections from the image. ...... Removing lcd.o(i.LCD_Set_Window), (908 bytes). Removing core_cm3.o(.emb_text), (32 bytes). Removing key.o(.data), (1 bytes). ...... ...... 563 unused section(s) (total 34109 bytes) removed from the image. Image Symbol Table Local Symbols Symbol Name Value Ov Type Size Object(Section) ...... ucHeap 0x200007e8 Data 20480 heap_4.o(.bss) .text 0x080002f4 Section 64 startup_stm32f10x_hd.o(.text) ...... Global Symbols ...... Adc1_Init 0x08002841 Thumb Code 132 adc1.o(i.Adc1_Init) __ARM_use_no_argv 0x00000000 Number 0 main.o ABSOLUTE ...... Memory Map of the image Image Entry point : 0x08000131 Load Region LR_IROM1 (Base: 0x08000000, Size: 0x000110e0, Max: 0x00040000, ABSOLUTE, COMPRESSED[0x00010fd0]) ← 第 1 层整个烧录映像 Execution Region ER_IROM1 (Exec base: 0x08000000, Load base: 0x08000000, Size: 0x00010f94, Max: 0x00040000, ABSOLUTE) ← 第 2 层运行区 1 Exec Addr Load Addr Size Type Attr Idx E Section Name Object ← 第 3 层明细 0x08000000 0x08000000 0x00000130 Data RO 3 RESET startup_stm32f10x_hd.o 0x08000130 0x08000130 0x00000008 Code RO 5827 * !!!main c_w.l(__main.o) ...... ...... Execution Region RW_IRAM1 (Exec base: 0x20000000, Load base: 0x08010f94, Size: 0x00006248, Max: 0x0000c000, ABSOLUTE, COMPRESSED[0x0000003c]) 0x20000000 COMPRESSED 0x00000014 Data RW 28 .data system_stm32f10x.o 0x200007e8 - 0x00005000 Zero RW 5588 .bss heap_4.o ...... ...... Image component sizes Code (inc. data) RO Data RW Data ZI Data Debug Object Name 452 42 0 0 0 12316 adc1.o 586 86 0 20 512 5266 atk_esp8266.o ...... ---------------------------------------------------------------------- Code (inc. data) RO Data RW Data ZI Data Debug Library Member Name 60 8 0 0 0 84 __0sscanf.o 24 4 0 0 0 84 __2printf.o ...... ---------------------------------------------------------------------- Code (inc. data) RO Data RW Data ZI Data Debug Library Name 9358 306 488 0 96 5164 c_w.l 5184 356 16 0 0 3268 fz_ws.l ...... Code (inc. data) RO Data RW Data ZI Data Debug 61532 6204 7992 332 24828 555690 Grand Totals 61532 6204 7992 60 24828 555690 ELF Image Totals (compressed) 61532 6204 7992 60 0 0 ROM Totals Total RO Size (Code RO Data) 69524 ( 67.89kB) Total RW Size (RW Data ZI Data) 25160 ( 24.57kB) Total ROM Size (Code RO Data RW Data) 69584 ( 67.95kB) 此文件内容的具体解析如下Section Cross References —— 谁引用了谁Keil 配置由 Options for Target → Listing 里的Linker Listing → Cross Reference勾选项生成。作用它是 armlink 从 ELF 重定位项还原出来的引用报告。每行的基本格式源段refers[(引用类型)]to目标段for符号名/* 例如↓ usart.o(i.uart_init) refers to stm32f10x_usart.o(i.USART_Init) for USART_Init └──── 源段 ────────┘ └──── 目标段 ───────────────────┘ └─ 符号名 ─┘ */源段格式是.o目标文件名(段名)即谁发起了这次引用。例如段名i.uart_init表示这是一个函数一个段的代码段前缀i.通常代表 Initializer 或 Image表示该段存储的是仅占用 Flash 空间而无需分配 RAM 的只读初始化数据或代码常量段名.text/.emb_text是汇编或多函数合并的代码段段名.data/.bss是数据段。引用类型默认空普通引用另有(Special)、(Weak)两种修饰插在refers和to中间。目标段被引用符号所在的位置格式同源段。库成员会写成c_w.l(__main.o)这种库名(成员名)的形式。符号名本次引用最终解析到的符号。这是全行信息量最大的字段但它不一定是函数名—— 任何用到某个符号地址的地方都会产生一条引用记录调用函数是重定位读写全局变量也是。常见符号类型和例子如下情形for后面的符号是判断依据refers to后目标段的段名① 函数C 函数名i.*、.text、.emb_text、locale$$code、…② 变量或常量全局或静态变量名、常量数组名.data、.bss、.constdata、.conststring③ 段名符号以.开头的段名本身与符号同名的段④ 库标记或配置符号__use_*等内部符号库的特制段几乎都带(Special)⑤ 弱引用可能未被解析的符号目标可为任意库成员带(Weak)用途查某个库函数是被谁拖进来的想给 Flash 瘦身时先看是谁引入了大块头库代码搜for 函数名即可。查谁访问了这个全局变量。可以查真实函数调用链搜for 函数名得到完整调用者清单逐级往上就能画出调用图。验证代码是否真的被链接进去如果某个函数在源码里明明写了这里却搜不到同时它又出现在Removing Unused input sections里就能确定它没被调用。Removing Unused input sections —— 被丢掉的代码Keil 配置由 Listing →Unused Sections Info勾选项打印。要注意的是裁剪本身是 armlink 的默认行为勾选项只决定要不要把它报告出来。每行的基本格式Removing目标文件(段名),(字节数bytes)./* 例如↓ Removing lcd.o(i.LCD_Set_Window), (908 bytes). Removing core_cm3.o(.emb_text), (32 bytes). Removing key.o(.data), (1 bytes). */这里每一行 一个被整体丢弃的输入段末尾字节数是它原本要占用的空间。段的粒度是全留或全丢没有半个函数留下这种情况。列表按目标文件顺序排列不按大小排。上述三个例子的段名含义如下段名i.函数名未被引用的函数在 keil 中开启 C/C 选项卡里的 “One ELF Section per Function”就会让每个函数独占一个段段名.emb_text未被引用的汇编代码段段名.data未被引用的静态/全局变量用途确认某个函数到底有没有被链接进去。这是最高频的用法和 Section Cross References 部分联合查看。判断哪些.c其实完全没用估算砍掉某个功能能省多少 Flash。验证裁剪配置是否按预期生效。Image Symbol Table —— 符号地址表这一节是全文件里最像字典的部分Keil 配置由 Options for Target → Listing 里的Linker Listing → Symbols勾选项生成。它把链接完成后所有符号的最终地址列了出来包括占多大。它分两张子表Local Symbols局部符号和Global Symbols全局符号。每行的基本格式两张子表共用同一个表头Symbol Name Value Ov Type SizeObject(Section)─────┬───── ──┬── ─┬ ──┬─── ─┬── ───────┬─────── │ │ │ │ │ └─ 符号属于哪个目标文件的哪个段 │ │ │ │ └─ 符号大小字节函数是可执行代码长度 │ │ │ └─ 符号类型值见下表 │ │ └─ 覆盖(overlay)编号本工程全为空 │ └─ 符号的最终绝对地址 └─ 符号名/* 例如↓ ucHeap 0x200007e8 Data 20480 heap_4.o(.bss) i.LCD_Init 0x08002841 Thumb Code 10592 lcd.o(i.LCD_Init) .text 0x080002f4 Section 64 startup_stm32f10x_hd.o(.text) __ARM_use_no_argv 0x00000000 Number 0 main.o ABSOLUTE ucHeap 0x200007e8 Data 20480 heap_4.o(.bss) ← FreeRTOS 堆 Stack_Mem 0x20005a48 Data 2048 startup_stm32f10x_hd.o(STACK) ← MSP 主栈 Heap_Mem 0x20005848 Data 512 startup_stm32f10x_hd.o(HEAP) ← C 库堆 */Value是链接后的最终地址不是编译时的偏移。注意函数地址的末位是 1如0x08002841那是 ARM Thumb 标志位真实指令地址要减 1。上述四个例子的四种符号类型含义如下Type含义Thumb Code函数有地址和机器码长度Data变量、常量数组、结构体有地址和字节数Section段符号只标记某个段的起始地址不表示具名对象Number编译期常量ABSOLUTE值为 0用来声明编译配置或依赖Local 和 Global 的区别Global Symbols 是对外可见的符号 —— 非static的函数和全局变量以及库导出的符号。链接器就是靠它们把不同.o拼到一起的。Local Symbols 是只在本文件内可见的符号 ——static函数、static变量外加编译器和汇编器为每个目标文件生成的段符号.text、.data、.bss。由此有一个实用推论同一个符号名在 Local 里可能出现多次。不同.c里各写一个static uint8_t buf[16]符号表里就是多条buf各占一块 RAM。短名i、j、buf、tmp特别容易撞用 Golbal/Local 表能查出到底有几份同名变量、各占多少 RAM。用途地址反查函数名 —— HardFault 调试的基石在 debug 里也可以直接查。核实 RAM/Flash 占用到底花在哪。查出隐藏的同名副本和常量表。Memory Map of the image —— 最重要的一节⭐️Keil 配置由 Options for Target → Listing 里的Linker Listing → Memory Map勾选项生成。前一节是按名字组织信息这里反过来是按地址顺序把整幅映像铺开告诉你每个段最终落在哪个地址、多大、是只读还是读写、要去哪里取初值。它是理解启动流程和判断 Flash/RAM 余量的核心。结构典型的三层/三行标题结构各行格式行类型格式Load Region 头Base/Size/Max/ 属性 / 压缩后大小Execution Region 头Exec base运行时地址 /Load base初值存放地址 /Size/Max/ 属性明细行Exec Addr/Load Addr/Size/Type/Attr/Idx/E Section Name/Object这里的Max不是芯片手册里的容量而是在 keil 中 Options for Target → Target 选项卡里给 IROM1/IARM1 声明的容量Flash 0x40000 256 KB、RAM 0xC000 48 KB。各行中各字段的含义Load Region 与 Execution Region 的关系Load Region 是烧进 Flash 的那一整块Execution Region 是运行时实际使用的区域。一个 Load Region 可以包含多个 Execution Region上图就是两个 ER代码区ER_IROM1和数据区RW_IRAM1。Execution Region 中 Exec base、Load base 的含义Exec base 是运行时地址表示该段代码或数据在运行时内存如 RAM中的实际使用起始地址。Load base 是初值存放地址表示该段代码或数据在非易失性存储器如 Flash中的物理存储起始地址。两者是否相等直接决定了该段数据在启动时的行为、Flash 占用以及CPU 访问方式。具体区别如下当 Exec base Load base 时这意味着代码或数据直接在存储介质通常是 Flash中原地运行或读取无需搬运。一般不占用运行时 RAM典型段类型.text代码段、.rodata只读常量、向量表等。当 Exec base ≠ Load base 时这意味着数据存储在 Flash 中但必须在 RAM 中使用需要启动代码进行搬运或初始化。除自身内容外还需额外存储初始值镜像必须预留对应大小的 RAM 空间用于运行时读写。典型段类型.data已初始化全局/静态变量等。维度Exec LoadExec ≠ Load物理位置Flash 原地使用Flash 存储 RAM 运行启动耗时无额外开销需拷贝/初始化增加启动时间Flash 消耗仅代码/常量本身代码/常量 初始值镜像RAM 消耗0纯只读段等于段大小可写性❌ 不可写RO✅ 可读写RW调试关注点确认 Flash 读取时序确认启动拷贝逻辑及 RAM 容量上图实例ER_IROM1 两者都是0x08000000→ 代码在 Flash 里原地执行不需要搬运。RW_IRAM1 的 Exec base 是0x20000000RAMLoad base 是0x08010f94Flash→ 这个区域的初值先存在 Flash 里启动时由__main的分散加载代码搬到 RAM。注意0x08010f94正好等于0x08000000 0x10f94也就是代码区的结尾——Flash 里代码和数据初值是首尾相接的。明细行的各列含义列含义Exec Addr运行时的绝对地址调试器里看到的就是它Load Addr初值的来源地址与 Exec 相同表示原地执行COMPRESSED表示初值在 Flash 里被压缩存放-表示没有初值纯零初始化Size该段字节数TypeCode指令 /Data数据 /Zero零初始化数据 /PAD对齐填充AttrRO只读占 Flash/RW可读写占 RAMIdx段索引号供符号表引用E Section Name段名i.函数名/.text/.constdata/.data/.bss/RESET/STACK/HEAPObject所属目标文件库成员形如c_w.l(__main.o)四种 TypeCode机器码Data有初值的数据。在ER_IROM1里是只读常量.constdata/.conststring在RW_IRAM1里是可读写变量.dataZero零初始化的.bss只在 RAM 里占位Flash 里不占空间PAD链接器为了满足对齐要求插入的填充用途一眼判断 Flash/RAM 余量拿Size和Max一比就知道还能加多少功能、够不够加 OTA 双备份。理解并验证启动流程以后自己写分散加载文件比如要把某个数组放到特定 RAM 段、或者加 bootloader 挪动 Flash 基址改完就看这里 Exec/Load 是否符合预期。地址区间反查手上有个裸地址想知道它是什么这一节是最快的入口。Image component sizes —— 按模块汇总 总量Keil 配置由 Options for Target → Listing 里的Linker Listing → Size Info勾选项生成。这一节主要用于按模块源文件/库文件维度统计固件的体积构成。结构三段表 末尾总账这一节全部是十进制字节数五列的含义列含义Code (inc. data)代码段大小。括号/右侧那个数是它的一部分表示其中有多少字节是内联数据不是额外开销。RO Data只读数据.constdata、.conststring、字库、字符串常量RW Data有初值的可读写变量.data同时占 Flash初值和 RAMZI Data零初始化数据.bss只占 RAM完全不占 FlashDebugDWARF 调试信息体积存在.axf里不烧进芯片第三段库汇总表解析库CodeRO DataZI是什么c_w.l9,35848896C 库printf/scanf/ 字符串 / 内存拷贝fz_ws.l5,184160浮点运行库ddiv、dmul、dsqrt、f2d…m_ws.l3,2981440数学库主要就是pow2,520末尾总账解析用途判断我的代码和库代码的比例。一键定位体积热点按Code列排个序即可看出谁体积最大。查 RAM 的静态占用构成。做改前 / 改后对比。和Image Symbol Table分工配合这一节按.o文件汇总符号表按符号明细。3日常排查时怎么快速用几个高频动作崩溃定位HardFault 时拿到 PC/LR直接在Image Symbol Table里找地址 ≤ PC 且最接近的符号就是出错函数。也可以直接在 debug 页面查看。查变量地址搜变量名看地址区间。如果它是0x2000xxxx且落在ucHeap0x200007e8–0x200057e8范围内说明它是动态分配的。查库函数来源搜refers to ... for 函数名能追到调用链的第一环。对比两次编译只比Image component sizes里的 Object Totals 和最后三行总账就能知道这次改动是省了还是涨了 Flash/RAM。确认函数是否真的没用出现在Removing Unused里 没被调用可放心删源码。
返回列表