MDK编译出来一大堆神魔文件到底是啥?今天帮你全弄懂!

本文详细介绍了MDK编译工程中产生的各种文件类型,如.o、.axf、.hex、.map等,并着重讲解了axf文件的调试信息、hex文件的格式以及map文件的功能,包括程序段交叉引用、内存分布和映像组件大小分析。
摘要由CSDN通过智能技术生成

编译文件有哪些

MDK 编译工程,会生文件(如.o、.axf、.map 等),最终生成 hex 文件和bin文件,以
便下载到 MCU 上面执行。
对于 MDK 工程来说,基本上任何工程在编译过程中都会有这 11 类文件,常见的 MDK
编译过程生产文件类型

文件类型说明
.o可重定向对象文件,每个源文件(.c/.s 等)编译都会生成一个.o 文件
.axf由 编译器(armcc-armclang-arm-gcc)生产的可执行对象文件,不可重定向 (绝对地址)多个.o 文件链接生成.axf 文件,相比hex附带了调试信息,我们在仿真的时候,需要用到该文件
.hexIntel Hex 格式文件,对比bin多了下载地址,可用于下载到 MCU,.hex 文件由.axf 文件转换而来
.bin对比axf和hex不包含调试信息和地址,纯二进制代码,我们在进行 BootLoader 升级的时候,一般使用.bin 文件,地址由 Bootloader 程序指定。
.crf交叉引用文件,包含浏览信息(定义、标识符、引用)
.d由 ARMCC/GCC 编译生产的依赖文件(.o 文件所对应的依赖文件)每个.o 文件,都有一个对应的.d 文件
.dep整个工程的依赖文件
.lnpMDK 生成的链接输入文件,用于命令输入
.lstC 语言或汇编编译器生成的列表文件
.htm链接生成的列表文件,包含了整个工程的静态调用图,最大的用处就是可以查看栈深度(最小深度),方便设置栈大小。
.build_log.htm最近一次编译工程时的日志记录文件
.map连接器生成的列表文件/MAP 文件

注 1,可重定向是指该文件包含数据/代码,但是并没有指定地址,它的地址可由后续链接
的时候进行指定。
注 2,不可重定向是指该文件所包含的数据/代码都已经指定地址了,不能再改变。

axf格式文件

axf文件除了包含程序数据(bin)和地址(hex)等数据之外,还包含调试信息。

axf文件内的调试信息附加在程序文件中,有助于分析和调试。

axf文件的调试信息作用:

可将源代码包括注释夹在反汇编代码中,这样我们可随时切换到源代码中进行调试。

还可以对程序中的函数调用情况进行跟踪(通过Watch & Call Stack Window查看)。

对变量进行跟踪(利用Watch & Call Stack Window)。

当然,axf文件调试信息的包含的内容有限,并非所有源码(及注释)相关信息都会包含在其中,想要有效调试,还是需要结合源代码工程进行调试。

hex文件

hex行格式:
:BBAAAATT 【D···D】CC
在这里插入图片描述
在这里插入图片描述
: 代表行开始,固定为冒号:

BB代表Bytes,数据长度

AAAA代表Address,地址

TT代表Type,数据类型(标识)

D···D代表Date,数据

CC代表CheckSum,校验和

.htm文件浅析

包含了整个工程的静态调用图,最大的用处就是可以查看栈深度(最小深度),方便设置栈大小。.htm 文件可以
直接由浏览器打开
在这里插入图片描述
可以看到,例程的最大栈深度是 760字节,最大栈深时的调用关系为:receive_uav_fix_handle ⇒ lppc_battery_message__unpack ⇒ protobuf_c_message_unpack ⇒ parse_required_member ⇒ protobuf_c_message_unpack (Cycle);
不过需要注意的是,这里的最大栈深度仅仅是最低要求(静态栈),因为它并没有统计无栈深的函数(用内存管理)、递归函数、以及无法追踪的函数(函数指针)等所包含的栈(Stack)。
这边我们可以在MDK的启动文件里设置栈的大小,2Kbytes也算是够用了
在这里插入图片描述
除此外,htm文件还有递归调用,函数指针,调用的函数,以及被谁调用

递归调用

在这里插入图片描述

函数指针

在这里插入图片描述

调用与被调用

在这里插入图片描述

map文件分析

.map 文件是编译器链接时生成的一个文件,它主要包含了交叉链接信息。通过.map 文件,我们可以知道整个工程的函数调用关系、FLASH 和 RAM 占用情况及其详细汇总信息,能具体到单个源文件(.c/.s)的占用情况,根据这些信息,我们可以对代码进行优化。
.map 文件可以分为以下 5 个组成部分:
1, 程序段交叉引用关系(Section Cross References)
2, 删除映像未使用的程序段(Removing Unused input sections from the image)
3, 映像符号表(Image Symbol Table)
4, 映像内存分布图(Memory Map of the image)
5, 映像组件大小(Image component sizes)

map 文件的 MDK 设置

下图中红框框出的部分就是我们需要设置的,默认情况下,MDK 这部分设置就是
全勾选的,如果我们想取消掉一些信息的输出,则取消相关勾选即可。
在这里插入图片描述

map文件基础概念

为了更好的分析 map 文件,我们先对需要用到的一些基础概念进行一个简单介绍,相关概念如下:
● Section:描述映像文件的代码或数据块,我们简称程序段
● RO:Read Only 的缩写,包括只读数据(RO data)和代码(RO code)两部分内容,
占用 FLASH 空间
● RW:Read Write 的缩写,包含可读写数据(RW data,有初值,且不为 0),占用 FLASH
(存储初值)和 RAM(读写操作)
● ZI:Zero initialized 的缩写,包含初始化为 0 的数据(ZI data),占用 RAM 空间。
● .text:相当于 RO code
● .constdata:相当于 RO data
● .bss:相当于 ZI data
● .data:相当于 RW data

map文件组成示例说明

程序段交叉引用关系(Section Cross References)

在这里插入图片描述
上图中,红框标出部分代表scheduler.o的数据段中sched_tasks(在本项目中该变量为一个函数指针)调用了power_controller.o的代码段中(err_check_task)函数。

删除映像未使用的程序段(Removing Unused input sections from the image)

One ELF Section per Function 选项告诉编译器将每个函数作为一个独立的 ELF 段,主要作用是优化代码,以减少最终生成的二进制文件的大小。
具体来说,当一个函数被调用时,它的代码会被加载到内存中。如果这个函数没有被使用,那么它的代码就会占用不必要的内存空间。而通过将每个函数作为一个独立的 ELF 段,编译器可以识别出未被使用的函数代码段并将其排除在最终的二进制文件之外,从而优化内存使用,同时我们这里还开了O1优化,
在这里插入图片描述
下图列出所有被移除的程序段,如heap4中的vPortInitialliseBlocks,同时还告诉了我们一些信息,2032unused section (total 52269 bytes)removed from the image。可以看到为我们节省了好一大块内存了啊,
在这里插入图片描述

映像符号表(Image Symbol Table)

映像符号表(Image Symbol Table)描述了被引用的各个符号(程序段/数据)在存储器中的存储地址、类型、大小等信息。映像符号表分为两类:本地符号(Local Symbols)和全局符号(Global Symbols)。

本地符号(Local Symbols)

本地符号(Local Symbols)记录了用 static 声明的全局变量地址和大小,c 文件中函数的地址和用 static 声明的函数代码大小,汇编文件中的标号地址(作用域:限本文件),本地符号如图 2.1.3.1.1 所示:
在这里插入图片描述

全局符号(Global Symbols)

全局符号(Global Symbols)记录了全局变量的地址和大小,C 文件中函数的地址及其代码大小,汇编文件中的标号地址(作用域:全工程),全局符号如图 2.1.3.2.1 所示:这边我app起始地址开始为0x08010800.
在这里插入图片描述

映像内存分布图(Memory Map of the image)(以下转载于正点原子,写的太好了)

映像文件分为加载域(Load Region)和运行域(Execution Region),一个加载域必须有至少一个运行域(可以有多个运行域),而一个程序又可以有多个加载域。加载域为映像程序的实际存储区域,而运行域则是 MCU 上电后的运行状态。加载域和运行域的简化关系(这里仅表示一个加载域的情况)图如图 2.1.4.1 所示:
在这里插入图片描述
加载域
可以看到映像入口地址为0x08010955,实际地址为: 0x08010954(Thumb 指令最低位是 1)。
基本的起始地址,加载地址,大小,类型,映像名,目标
在这里插入图片描述
① 处,表示映像的入口地址,也就是整个程序运行的起始地址,为:0X0800 0299。
实际地址为:0X0800 0298(Thumb 指令最低位是 1)。
② 处,表示 LR_m_stmflash 加载域,其起始地址为:0X0800 0000;占用大小为:0X00002D8C;最大地址范围为:0X0002 0000。其内部包含两个运行域:ER_m_stmflash和 RW_m_stmsram。
③ 处,表示 ER_m_stmflash 运行域,其起始地址为:0X0800 0000;占用大小为:0X0000 2D6C;最大地址范围为:0X0002 0000;即内部 FLASH 运行域,所有需要放内部FLASH 的代码,都应该放到这个运行域里面。
④ 处表示 RW_m_stmsram 运行域,其起始地址为:0X2400 0000;占用大小为:0X00002D6C;最大地址范围为:0X0008 0000;即内部 SRAM 运行域,所有 RAM(包括RW 和 ZI)都是放在这个运行域里面。
⑤ 处,表示 LR_m_qspiflash 加载域,其起始地址为:0X9000 0000;占用大小为:0X0000 0720;最大地址范围为:0X0080 0000。其内部包含一个运行域:ER_m_qspiflash。
⑥ 处,表示 ER_m_qspiflash 运行域,其起始地址为:0X9000 0000;占用大小为:0X0000 0720;最大地址范围为:0X0080 0000;即外部 QSPI FLASH 运行域,所有需要放外部 QSPI FLASH 的代码,都应该放到这个运行域里面。
上图列出了所有加载域及其运行域的具体内存分布,我们可以很方便的查看
任何一个函数所在的运行域、入口地址、占用空间等信息。如 sys_stm32_clock_init 函数:该函数在 ER_m_stmflash 运行域;入口地址为:0X0800 2BC8;大小为:0X168 字节;是 sys.c里面的函数。了解这些信息,对我们分析及优化程序非常有用

映像组件大小(Image component sizes)

映像组件大小(Image component sizes)给出了整个映像所有代码(.o)占用空间的汇总信息,对我们比较有用.
在这里插入图片描述
在这里插入图片描述
上图中,框出的三处信息对我们比较有用,接下来分别介绍:
① 处,表示.c/.s 文件生成对象所占空间大小(单位:字节,下同),即.c/.s 文件编译后所占代码空间的大小。每个项所代表的意义如下:
Code(inc.data):表示包含内联数据(inc.data)后的代码大小。如 delay.o(即delay.c)所占的 Code 大小为 142 字节,其中 8 字节是内联数据。
RO Data:表示只读数据所占的空间大小,一般是指 const 修饰的数据大小。
ZI Data:表示初始化为 0 的可读写数据所占空间大小,它只占用 RAM 空间。
Debug:表示调试数据所占的空间大小,如调试输入节及符号和字符串。
Object Totals:表示以上部分链接到一起后,所占映像空间的大小。
(incl.Generated):表示链接器生产的映像内容大小,它包含在 Object Totals 里面了,这里仅仅是单独列出,我们一般不需要关心。
incl.Padding):表示链接器根据需要插入填充以保证字节对齐的数据所占空间的大小,它也包含在 Object Totals 里面了,这里单独列出,一般无需关心。
② 处,表示被提取的库成员(.lib)添加到映像中的部分所占空间大小。各项意义同①中的说明。我们一般只用看 Library Totals 来分析库所占空间的大小即可。
③ 处,表示本工程全部程序汇总后的占用情况。其中:
Grand Totals:表示整个映像所占空间大小。
ELF Image Totals:表示 ELF 可执行链接格式映像文件的大小,一般和 Grand Totals一样大小。
ROM Totals:表示整个映像所需要的 ROM 空间大小,不含 ZI 和 Debug 数据。
Total RO Size:表示 Code 和 RO 数据所占空间大小,本例程为:13452 字节。
Total RW Size:表示 RW 和 ZI 数据所占空间大小,即本映像所需 SRAM 空间的大小,本例程为:3032 字节。
Total ROM Size:表示 Code、RO 和 RW 数据所占空间大小,即本映像所需 FLASH空间的大小,本例程为:13484 字节。
图中,未框出的:Library Name 部分,实际和②处是一个意思,只是 Library Name 说明了②处的那些.o 文件来自什么库,这里实际上就是:fpinit.o 来自 fz_wv.l 库,其他部分来自 c_w.l 库。fz_wv.l 和 c_w.l 是库名字。
MAP 文件的分析就给大家介绍到这里。

其他

一些比较生僻的文件可以通过help来查看。
在这里插入图片描述
在这里插入图片描述
我这边简单的做一些解释。
.OPT: Keil 早期版本使用的项目配置选项文件,已经被新的文件替代(具体见下面的说明)。

.UVGUI[.user-name]:μVision4 窗口布局文件。即:使用者在调整了Keil 中各窗口布局后,再次打开 Keil 会保持之前的修改,修改就是记录在该文件中。

.UVGUIX[.user-name]:μVision5 窗口布局文件,XML格式,记录了 MDK 软件的 GUI 布局,如代码编辑区窗口的大小、编译输出提示窗口的位置等等。

.UVMPW:用于多个项目的 μVision4 项目文件(工作空间项目文件)。包含对其他项目文件的引用,并将它们绑定到一个项目中。当多个项目分组到一个项目中时,该文件是必需的。具有 XML 结构,可以在工作组中共享。

.UVPROJ: Keil μVision4 的项目文件,它使用了 XML 格式记录了工程的结构,双击它可以打开整个工程。

.UVPROJX: Keil μVision5 的项目文件,它使用了 XML 格式记录了工程的结构,就是我们平时双击打开的工程文件,它记录了整个工程的结构,如芯片类型、工程包含了哪些源文件等内容。

.UVOPT: Keil μVision4 项目配置文件,XML 格式,包括调试配置、跟踪信息配置、断点等。

.UVOPTX: Keil μVision5 项目配置文件,记录了工程的配置选项,如下载器的类型、变量跟踪配置、断点位置以及当前已打开的文件等等。

.UV2: μVision3 项目文件。使用更高版本的 μVision 打开文件,将文件转换为新的项目类型。

.i: C语言预处理器输出文件;

.lst: C编译器或汇编程序生成的文件;

.cod: 包括混合C和汇编代码的完整的程序清单文件;
A51:汇编源码文件

.**A66:**汇编源码文件

.C: C 语言源码文件

.CPP: C++ 语言源码文件

.H: C/C++ 语言的头文件

.INC: 汇编语言的头文件(使用"$include"来包含)

.S: Assembler source file (typical used for ARM source files).

.SRC:Other source file generated by the C compiler.
当然,还有很多神魔,没列出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值