1. 固件镜像基础:它到底是什么?
做嵌入式这么多年,我经常被刚入行的同事问一个问题:「固件镜像不就是一堆二进制数据吗?」
嗯,这么说也没错。但如果你只把它当成一堆数据,那后面遇到校验失败、启动卡死、OTA 变砖的时候,你连排查的方向都没有。
说白了,固件镜像就是嵌入式系统的「灵魂载体」。硬件是躯壳,固件是意识。没有镜像,芯片就是一块死硅片。
1.1 固件在嵌入式系统里的角色
你想想看,一个嵌入式设备从按下电源键到正常运行,中间发生了什么?
- BootROM 先上电,它是个固化在芯片内部的小程序
- BootROM 去指定的位置(比如 SPI Flash 的 0x0 地址)加载固件镜像
- 校验通过后,把控制权交给镜像里的启动代码
- 启动代码初始化硬件、配置时钟、搬运数据段……最终跳转到 main()
整个过程里,固件镜像就是那个被加载、被校验、被执行的对象。我在项目中遇到过好几次,因为镜像头部校验字段被意外修改,导致 BootROM 直接拒绝加载——设备变砖,连串口都打印不出任何信息。
核心认知:固件镜像不是「文件」,它是芯片上电后第一条指令的源头。镜像坏了,系统连「报错」的能力都没有。
1.2 镜像文件的常见格式
实际工作中,我们主要跟三种格式打交道:bin、hex、elf。它们的区别,我习惯用一个表格来对比:
| 格式 | 全称 | 包含信息 | 是否可执行 | 典型用途 |
|---|---|---|---|---|
| bin | Binary | 纯二进制数据,无地址信息 | 是(烧录后) | 量产烧录、OTA 升级包 |
| hex | Intel HEX | 二进制数据 + 地址 + 校验和 | 否(需解析) | 烧录器输入、调试阶段 |
| elf | Executable and Linkable Format | 代码 + 数据 + 符号表 + 调试信息 | 否(需加载器) | 编译输出、调试器输入 |
我个人习惯这样区分:elf 是给调试器看的,hex 是给烧录器看的,bin 是给芯片直接吃的。
1.3 深入看看每种格式
bin 格式
最纯粹的格式。没有头部、没有元数据、没有校验——就是你要烧录到 Flash 里的原始字节流。
我记得第一次做量产工具时,踩过一个坑:bin 文件没有地址信息,烧录器必须事先知道目标地址。如果你把 bin 烧到了错误的位置,芯片启动时读到的全是乱码,根本跑不起来。
// 一个典型的 bin 镜像布局(STM32 为例)
地址 0x08000000: 栈顶指针 (MSP)
地址 0x08000004: 复位向量 (Reset_Handler)
地址 0x08000008: NMI 向量
...
地址 0x08001000: .text 代码段
地址 0x08005000: .rodata 只读数据段
地址 0x08006000: .data 已初始化数据段
小技巧:用 hexdump 或 xxd 查看 bin 文件时,注意前 4 个字节。如果是 ARM Cortex-M 系列,前 4 字节应该是栈顶地址,通常接近 RAM 的顶端。如果看到 0xFFFFFFFF 或 0x00000000,那这个镜像大概率是空的或损坏的。
hex 格式
Intel HEX 是文本格式,每行以冒号开头,包含长度、地址、类型、数据和校验和。它比 bin 多了一层保护——每行都有校验和,烧录器可以逐行验证数据完整性。
:1000000000000020A5010008B1010008C1010008F0
:10001000C1010008C1010008C1010008C1010008C0
:10002000C1010008C1010008C1010008C1010008B0
:0400000508000000F7
:00000001FF
上面这个例子,第一行表示从地址 0x0000 开始,16 个字节的数据。最后一行是文件结束记录。
我曾经在产线上遇到过烧录器报「校验和错误」,排查了半天,发现是 hex 文件里某一行被文本编辑器多插了一个空格——烧录器解析时把数据读歪了。
注意:hex 文件是纯文本,不要用 Word 或带格式的编辑器打开修改。用 Notepad++、VS Code、Sublime 这类纯文本工具。我曾经见过有人用 Word 打开 hex 文件保存后,文件体积膨胀了 3 倍——烧录器直接不认。
elf 格式
elf 是编译器的最终输出,结构最复杂。它包含多个 section(.text、.data、.bss、.rodata……),还有符号表、重定位信息、调试信息。
调试器(比如 J-Link、ST-Link)通过 elf 文件知道「变量 x 在哪个地址」「函数 foo 的代码在哪里」。
但注意:elf 不能直接烧录到 Flash。你需要用 objcopy 把它转换成 bin 或 hex:
# 将 elf 转换为 bin
arm-none-eabi-objcopy -O binary firmware.elf firmware.bin
# 将 elf 转换为 hex
arm-none-eabi-objcopy -O ihex firmware.elf firmware.hex
我见过不少新手直接拿 elf 文件去烧录,烧录器要么报错,要么只烧了部分数据——因为 elf 里有很多 Flash 里不需要的调试信息。
1.4 三种格式的关系——一张图说清楚
下面这张图展示了从源码到最终烧录的完整链路:
从这张图你可以看到:elf 是「母版」,bin 和 hex 是「分发格式」。调试时用 elf,量产时用 bin 或 hex。
1.5 为什么理解这些对「校验异常排查」很重要?
好,回到我们的课程主题。为什么第一章要先讲这些基础?
- 校验算法作用在 bin 上:CRC、SHA256 这些校验值,是对纯二进制数据计算的。如果你拿 hex 文件去算校验,结果会包含地址信息和冒号——跟芯片端算出来的完全对不上。
- 镜像格式决定了校验位置:有些方案把校验值放在镜像头部(前 32 字节),有些放在尾部。如果你不清楚镜像结构,根本不知道去哪里读校验值。
- elf 里的段信息能帮你定位问题:当校验失败时,通过 elf 的符号表可以快速定位到出错的函数或数据段,而不是在二进制流里大海捞针。
一句话总结:不懂镜像格式,校验异常排查就是盲人摸象。这一章打下的基础,后面每一章都会用到。