MDK编译过程及其中间文件介绍

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33894122/article/details/83994564

整个工作过程

  • 编译:
    编译器是armcc(C文件代码)和armasm(汇编文件代码),它们根据每个c/c++和汇编源文件编译成对应的以".o"为后缀名的对象文件(Object Code,也称目标文件),其内容主要是从源文件编译得到的机器码,包含了代码、数据以及调试使用的信息。但是其中不包含 地址信息

  • 链接:
    链接器armlink把各个.o文件及库文件根据你在MDK中的芯片选型 地址信息设置 链接成一个映像文件".axf"或".elf"。这个".axf"文件是包含地址信息的。其中还会提示程序存储空间分配,这个具体下面再讲

Program Size: Code=14636 RO-data=576 RW-data=204 ZI-data=2732  
  • 格式转换:
    一般来说Windows或Linux系统使用链接器直接生成可执行映像文件elf后,内核根据该文件的信息加载后,就可以运行程序了。但在单片机平台上,需要把该文件的内容加载到芯片上,所以还需要对链接器生成的elf映像文件利用格式转换器fromelf转换成“.bin”或“.hex”文件,交给下载器下载到芯片的FLASH或ROM中。

程序存储空间分配

应用程序中所有具有同一性质的数据(包括代码)被归到一个域,程序在存储或运行的时候,不同的域存储、读写属性各不相同。主要分为几大类CODE、 RO、 RW、 ZI Data。在《程序员的自我修养》这本书里面讲了很多这种编译、链接类的知识。下面仅仅是针对于MCU的架构,指的是nor flash+sram的结构

  • CODE:代码段,这个是纯粹的代码,MCU就是执行这些二进制指令。在存储时代码段存放在nor flash中,在运行时代码段仍然在nor flash中,这跟PC等架构不一样。MCU内核直接从nor flash取指执行,不需要将CODE加载到RAM中

  • RO:只读数据区,指程序中用到的只读数据,这些数据被存储在ROM区,因而程序不能修改其内容。在存储时只读数据区在flash中,在运行时仍然在flash中,并不会被加载到sram中。比如char *p="abc";,其实abc字符串是存放在rom区的。一般情况下const定义的变量也是属于只读的存放在rom flash中。

  • RW:可读可写数据段,它指初始化为“非0值”的可读写数据,程序刚运行时,这些数据具有非0的初始值,且运行的时候它们会常驻在RAM区,因而应用程序可以修改其内容。RW段在存储时放在flash中,在运行时被加载到sram中。如定义的全局变量(不初始化为0的)。

  • ZI Data:一般又称为BSS段。即0初始化数据(Zero Initialied Data),它指初始化为“0值”的可读写数据域,它与RW-data的区别是程序刚运行时这些数据初始值全都为0,而后续运行过程与RW-data的性质一样,它们也常驻在RAM区,因而应用程序可以更改其内容。在存储的时候并不占用flash中的资源,因为反正都是0,只需要记录有多少个0即可,在运行的时候会在SRAM中分配相应的大小的内存。

  • 堆栈段:在存储的时候是不占用空间的(或者说就没有存储),在运行的时候有着自己的一套规则。

程序状态与区域 组成
程序执行时的只读区域(RO) Code + RO data
程序执行时的可读写区域(RW) RW data + ZI data
程序存储时占用的ROM区 Code + RO data + RW data

MDK工具链

MDK就是将一系列工具如armcc、armlink、armar、 fromelf集成并做了友好的GUI界面,实际干活的还是这些软件工具。
在这里插入图片描述
下面介绍这些软件跟MDK设置界面的关系
首先添加环境变量,方便后续操作:如果是按MDK默认安装路径来的话,需要将fromelf的路径加入到环境变量,即C:\Keil_v5\ARM\ARMCC\bin加入环境变量。

  1. armcc :就是实际干活的编译器
    在cmd窗口中输入armcc会提示一串用法,在MDK的Options For Targets->C/C+±>Compiler control string 窗口的一串字符串其实就是armcc编译时的选项,只不过这些选项是由MDK内置脚本自动生成的而已。
    输入armcc --cpu list可以看到当前版本armcc支持哪些内核的CPU

  2. armlink :就是实际干活的编译器
    在cmd窗口中输入armlink会提示一串用法,在MDK的Options For Targets->Linker>Linker control string窗口的一串字符串其实就是armlink链接时的选项,只不过这些选项是由MDK内置脚本自动生成的而已。
    链接器根据芯片的地址信息 和 armcc生成的.o文件来生成elf格式的axf可执行文件。
    那么这个芯片的地址信息是哪里来的?原来在我们给芯片选型之后,MDK自动生成了一个.sct文件,这个也叫加载文件,链接器也就是根据.sct文件来确定链接地址的,不同芯片选型会生成不同的.sct文件。还有一篇文章将分散加载的博客,其中主要就是讲.sct文件的。

  3. armar:是用于将工程文件打包成库文件的一个工具
    在MDK中有Options for Targets->Output->Create Library选项,就是利用armar来生成库文件的。在你不想给对方源码,只想提供API接口的时候可以使用这种方法。

  4. fromelf:可以根据elf格式的axf文件生成HEX、BIN文件
    但是仅仅集成了hex选项,可以在Options for Targets->Output中看到。如果你想生成BIN文件怎么操作呢?当然第一种方法你可以在cmd命令行中根据已经生成的.axf文件利用fromelf工具来生成BIN文件;第二种方法是MDK中有Options for Targets->User窗口,里面就是让用户根据编译过程来添加自己的脚本命令的,其中分为了三个阶段,编译前、链接前、链接后,如果我们想利用.axf文件仅仅需要在连接后的里面添加脚本fromelf.exe --bin -o ..\OBJ\LED.bin ..\OBJ\LED.axf即可

MDK工程文件类型详解

后缀 文件类型
*.lib 库文件
*.dep 整个工程的依赖文件
*.d 描述了对应.o的依赖的文件
*.crf 交叉引用文件, 包含了浏览信息(定义、 引用及标识符)
*.o 可重定位的对象文件(目标文件)
*.bin 二进制格式的映像文件, 是纯粹的FLASH映像, 不含任何额外信息
*.hex Intel Hex格式的映像文件, 可理解为带存储地址描述格式的bin文件
*.elf 由GCC编译生成的文件, 功能跟axf文件一样, 该文件不可重定位
*.axf 由ARMCC编译生成的可执行对象文件, 可用于调试, 该文件不可重定位
*.sct 链接器控制文件(分散加载)
*.scr 链接器产生的分散加载文件
*.lnp MDK生成的链接输入文件, 用于调用链接器时的命令输入
*.htm 链接器生成的静态调用图文件
*.build_log.htm 构建工程的日志记录文件
*.lst C及汇编编译器产生的列表文件
*.map 链接器生成的列表文件, 包含存储器映像分布
*.ini 仿真、 下载器的脚本文件
*.uvprojx 记录了整个工程的结构,如芯片类型、工程包含了哪些源文件等内容
*.uvoptx 记录了工程的配置选项,如下载器的类型、变量跟踪配置、断点位置以及当前已打开的文件
*.uvguix 记录了MDK软件的GUI布局,如代码编辑区窗口的大小、编译输出提示窗口的位置

HEX与BIN区别和联系

HEX文件是包括地址信息的,HEX文件都是由记录(RECORD)组成的。在HEX文件里面,每一行代表一个记录,而BIN文件格式只包括了数据本身,纯粹的二进制数据连大小端都没有,任何辅助信息都没有。HEX文件是用ASCII来表示二进制的数值。例如一般8-BIT的二进制数值0x5E,用ASCII来表示就需要分别表示字符’5’和字符’E’,每个字符需要一个BYTE,所以HEX文件需要 > 2倍的空间。对一个BIN文件而言,你查看文件的大小就可以知道文件包括的数据的实际大小。而对HEX文件而言,你看到的文件 大小并不是实际的数据的大小。一是因为HEX文件是用ASCII来表示数据,二是因为HEX文件本身还包括别的附加信息,如地址信息。
在烧写或下载HEX文件的时候,一般都不需要用户指定地址,因为HEX文件内部的信息已经包括了地址。而烧写BIN文件的时候,用户是一定需要指定地址信息的(MDK下载的就是HEX文件,ESP8266自己编译固件产生bin文件下载的时候需要指定flash地址)。看图
STM32生成下载文件
MDK截图

ESP8266生成文件跟下载方式Eclipse 和 ESP8266下载工具截图

HEX文件详解

下面是一个hex文件前几行

:020000040800F2
:10000000780B0020E1080008C9020008AB150008C1
:10001000CB020008CF020008D30200080000000055
:10002000000000000000000000000000D7020008EF
:10003000D90200080000000033150008AB360008A4
:10004000FB080008FB080008FB080008FB08000884
:10005000FB080008FB080008FB080008FB08000874
:10006000FB080008FB080008FB080008FB08000864

每行是以:开始,代表一条记录,一条记录的基本格式是:llaaaatt[dd…]cc

  • : :冒号是一条记录开始
  • ll:以16进制数表示这条记录的主体数据区的长度
  • aaaa:表示这条记录中的内容应存放到FLASH中的起始地址
  • tt:表示这条记录的类型,它包含中的各种类型
tt 数据类型
00 数据记录
01 本文件结束记录
02 扩展地址记录
04 扩展线性地址记录(表示后面的记录按个这地址递增)
05 表示一个线性地址记录的起始(只适用于ARM)
  • dd:表示一个字节的数据,一条记录中可以有多个字节数据
  • cc:表示本条记录的校验和,它是前面所有16进制数据 (除冒号外,两个为一组)的和对256取模运算的结果的补码

猜你喜欢

转载自blog.csdn.net/qq_33894122/article/details/83994564