单片机/C/C++八股:(二十四)编译文件( .bin 和 .hex ,包括 .elf 和 .axf )

上一篇 下一篇
#define 和 typedef 的区别

目 录

  • [编译文件( .bin 和 .hex ,包括 .elf 和 .axf )](#编译文件( .bin 和 .hex ,包括 .elf 和 .axf ))
    • [1)编译的交付文件:.hex 与 .bin 的区别](#1)编译的交付文件:.hex 与 .bin 的区别)
      • [① 两种文件的本质](#① 两种文件的本质)
      • [② 为什么 .bin 必须额外指定起始地址](#② 为什么 .bin 必须额外指定起始地址)
      • [③ 表格对比](#③ 表格对比)
    • [2)编译的原始产物:.elf 和 .axf](#2)编译的原始产物:.elf 和 .axf)
    • [3)Keil LOAD / J-Flash / ... 烧录的是哪个文件](#3)Keil LOAD / J-Flash / ... 烧录的是哪个文件)

编译文件( .bin 和 .hex ,包括 .elf 和 .axf )

这四种格式的文件都是程序编译的结果,只不过程序编译的原始产物是 .elf 和 .axf ,而 .bin 和 .hex 是经过处理的

1)编译的交付文件:.hex 与 .bin 的区别

① 两种文件的本质

  • .bin 文件 是最朴素的格式:它就是程序在 Flash 里逐字节的二进制镜像,没有任何额外信息。文件里第 0 个字节就对应芯片 Flash 起始位置的第 0 个字节,第 N 个字节就是第 N 个字节,仅此而已。它不知道自己应该被烧到哪里,也不知道自己有多大段、跳过多少空洞。

  • .hex 文件 (特指 Intel HEX 格式)则是一种带地址信息的 ASCII 文本格式。文件内容是一行行的记录,每行长这样:

    复制代码
    :10010000214601360121470136007EFE09D2190140

    其中 : 是起始符,10 是本行数据长度(16 字节),0100 是本行数据的目标地址,00 是记录类型(数据记录),后面跟着 16 字节数据,最后是校验和。还有专门的"扩展线性地址记录"用来表达高 16 位地址,从而支持 4GB 寻址空间。

    换句话说,hex 文件自带地址信息,而且可以表达不连续的地址段(比如代码在 0x08000000、配置数据在 0x0801F000,中间的空洞不会浪费文件体积)。

② 为什么 .bin 必须额外指定起始地址

原因就在于上面的对比里:bin 文件丢掉了地址信息。烧录器(JFlash、ST-Link Utility 等)拿到一个 bin 文件时,它面对的只是一串字节,根本不知道该把这串字节写到 Flash 的哪个物理地址。是写到 0x08000000(STM32 主 Flash 起点)?还是 0x08020000(某个 Bootloader 后面的 App 区)?还是 0x1FFF0000(系统存储区)?文件本身没有任何线索。

所以使用 bin 文件烧录时,必须在烧录工具里手动告诉它起始地址,烧录器才能把第 0 字节对齐到正确的位置往后写。而 hex 文件不需要这一步------打开 JFlash 加载 hex,地址信息已经在文件里了,工具直接按记录里的地址写就行。

注意:------------------

当使用 bin 烧录单片机的时候,一个常见场景是 Bootloader + App 的双区设计 :比如说 App 链接脚本里把起始地址设成 0x08010000,编译出来的 hex 自动带这个地址,烧录直接就位;但如果是 bin,必须记住 "这个 bin 是 App 的,要烧到 0x08010000",否则就会把 App 烧到 Bootloader 区把 Boot 覆盖掉。

③ 表格对比

维度 .bin .hex
文件大小 小(纯二进制) 大约是 bin 的 2~3 倍(ASCII 编码)
是否含地址
是否含校验 每行有校验和
是否支持空洞 否(空洞要填充) 是(可表达不连续段)
烧录易用性 必须手动指定地址 直接烧
适用场景 OTA 升级、量产烧写器、Bootloader 接收 开发调试、JFlash/JLink 烧录

bin 的优势在"小而纯":OTA 升级时通过串口、网络传输,bin 体积小、解析简单,固件接收端拿到字节流直接往 Flash 里刷就行;量产烧录机也偏好 bin,因为速度快、流程简单。hex 的优势在"自描述":开发调试时谁也不想每次都去查地址,hex 拖进 JFlash 直接点烧录就完事了。

2)编译的原始产物:.elf 和 .axf

编译器真正直接生成的其实是 ELF (GCC)或 AXF(Keil)文件,这俩才是"信息最全"的格式:除了代码和地址,还包含调试符号、段表、符号表等。J-Link 的 JLinkExe、OpenOCD 都能直接烧 elf。

但 .elf/.axf 体积大、格式复杂,不适合单纯的烧录场景,所以实际烧写 Flash 时,几乎所有人都是用从它转出来的 .hex 或 .bin。工具链在编译完成后会自动帮你转(Keil 在 Output 选项卡勾一下就行,Makefile 工程加一行 objcopy 命令),整个过程是透明的。

比如用 objcopy 从 elf 转出 hex 和 bin :

bash 复制代码
arm-none-eabi-objcopy -O ihex   app.elf app.hex
arm-none-eabi-objcopy -O binary app.elf app.bin

转成 hex 时地址信息保留下来了,转成 bin 时地址信息就被丢掉了------这正是为什么 bin 烧录需要手动补回地址的根本原因。

3)Keil LOAD / J-Flash / ... 烧录的是哪个文件

Keil 里点 LOAD 时下载的具体格式,取决于在 Options for Target → Utilities → Settings 里配置的下载算法和工具,但本质上 Keil 并不是把 .hex 文件"传"给单片机,Keil 编译链接后,Keil 默认生成的是 .axf(ARM Executable Format,本质是 ELF 文件),里面既包含可执行的机器码段(代码、常量、初始化数据),也包含调试信息、符号表、段地址等元数据。

点 LOAD 时,Keil 调用的是调试器驱动(比如 ST-Link、J-Link、CMSIS-DAP 的 Flash 编程算法),它会:

  1. .axf解析出各个 LOAD 段(代码段、只读数据段、已初始化数据段的初值),以及它们应该被烧到的物理地址;
  2. 把这些二进制数据通过调试器(SWD/JTAG)按页写进 MCU 的片内 Flash;
  3. 顺便把 .axf 里的调试符号留在 PC 端,供单步调试、查看变量用。

所以严格说,下载到单片机里的既不是 .axf 也不是 .hex,而是从 .axf抽取出来的纯二进制机器码 (相当于 .bin 的内容),按地址写进 Flash。.hex 只是同样这份二进制数据的另一种带地址的文本封装格式而已。

.hex 文件什么时候才有用呢?当你想脱离 Keil、用别的工具(比如 ST-Link Utility、J-Flash、量产烧录器)去烧写时,就需要在 Options for Target → Output 里勾上 Create HEX File ,让 Keil 额外生成一份 .hex 给那些工具用。Keil 自己 LOAD 的时候并不依赖它。

总结:

  • Keil LOAD 时用的是 .axf,烧进 Flash 的是从 .axf 抽出来的机器码二进制;.hex 是给外部烧录工具准备的可选副产品。
  • JFlash 一般是直接将 .bin 文件烧录到 mcu 的 flash 中。

相关推荐
Yunhoa1 小时前
51单片机(DS18B20 数字温度传感器)
单片机·嵌入式硬件·51单片机
cpp_25012 小时前
P10098 [ROIR 2023] 地铁建设 (Day 2)
数据结构·c++·算法·贪心·二分答案·洛谷题解
zlinear数据采集卡3 小时前
2mV纹波的代价:硬核拆解ZLinear模拟前端放大器与正负可调电源设计
arm开发·单片机·嵌入式硬件·fpga开发·架构·开源
hehelm3 小时前
IO 多路复用 — Reactor
linux·服务器·网络·数据库·c++
简单电磁炮4 小时前
硬件基础4
c语言
灵晔君4 小时前
【C++】二叉搜索树
开发语言·c++
鱼子星_4 小时前
【C++】内存管理:内存分布,new/delete的使用及细节处理,new/delete的底层,定位new表达式
开发语言·c++·笔记
鱼子星_5 小时前
【C++】string(上):string的基本使用
c++·笔记·字符串
不上岸的旱鸭子5 小时前
PCB设计
单片机·pcb工艺