4. 镜像文件格式解析
Android 系统的烧录过程,本质上就是把一组分区镜像写入到 eMMC/UFS 的对应分区中。理解每个镜像的格式、内部结构和打包解包方式,是排查“烧录成功但无法启动”“分区大小不匹配”“镜像校验失败”等问题的前提。本章从镜像格式入手,梳理常见分区的组成与操作方法。
一、核心原理:Android 镜像的三种基本形态
Android 分区镜像并非单一格式,按封装方式大致可分为三类:
- Android 专用镜像(Android Image):如 boot.img、recovery.img、dtbo.img,头部带有
ANDROID!魔数,内部由 header、kernel、ramdisk、second、dtb 等段组成,并带有 SHA1 校验。 - 稀疏镜像(Sparse Image):如 system.img、vendor.img、super.img,为节省传输与存储空间,采用
0xED26FF3A魔数的稀疏格式,只记录非零数据块。 - 文件系统镜像:如 userdata.img、metadata.img,通常是 ext4 或 f2fs 裸文件系统镜像,可直接挂载。
boot.img 与 recovery.img 结构相同,区别仅在于 recovery 内含 recovery 模式的 ramdisk;dtbo.img 则是设备树 overlay 的集合,用于在运行时叠加硬件描述。system.img 与 vendor.img 在 Android 10 之后常被合并进 super.img,通过动态分区(Dynamic Partitions)在逻辑上划分。
二、方法流程:镜像的打包与解包
不同镜像使用不同工具链,掌握对应命令即可完成解包与重打包。以下为常用操作:
# 1. boot.img 解包(使用 mkbootimg 工具集中的 unpack_bootimg)
unpack_bootimg --boot_img boot.img --out ./boot_out
# 输出:kernel、ramdisk、second、dtb、bootimg.json
# 2. 重新打包 boot.img
mkbootimg --kernel kernel --ramdisk ramdisk \
--header_version 4 --output boot_new.img
# 3. dtbo.img 解包
mkdtimg dump dtbo.img -b dtbo_out
mkdtimg create dtbo_new.img --page_size=2048 dtbo_out/*.dtb
# 4. 稀疏镜像还原为 raw
simg2img system.img system_raw.img
# raw 转稀疏
img2simg system_raw.img system_sparse.img
# 5. super.img 解包(lpunpack)
lpunpack super.img ./super_out
# 输出 system.img、vendor.img、product.img 等逻辑分区
# 6. 挂载 ext4 镜像查看内容
sudo mount -o loop system_raw.img /mnt/system
打包时需特别注意 header_version 与 page_size 参数,它们必须与设备 bootloader 的解析能力匹配,否则会出现“镜像写入成功但校验失败”的现象。
三、案例场景:一次 super.img 烧录失败的分析
某项目在 Android 12 设备上烧录 super.img 后无法进入系统,串口日志显示 Failed to load metadata for slot _a。排查过程如下:
- 确认 super.img 大小是否超过物理 super 分区容量,动态分区元数据中记录了每个逻辑分区的大小。
- 使用
lpdump super.img查看元数据,发现 system 逻辑分区被设置为 3.2GB,而实际镜像仅 2.8GB,剩余空间未正确回收。 - 重新执行
lpmake并指定--partition system:readonly:3221225472:group=main,确保大小与镜像一致。 - 烧录后系统正常启动,问题定位为动态分区元数据与镜像大小不匹配。
动态分区(super.img)中逻辑分区的大小由元数据决定,而非镜像文件本身。修改镜像内容后,务必同步更新元数据,否则会出现分区读取越界或校验失败。
四、误区与复盘
| 常见误区 | 后果 | 正确做法 |
|---|---|---|
| 直接用 fastboot flash 烧录稀疏镜像 | 部分 bootloader 不支持稀疏格式,写入失败 | 先用 simg2img 还原为 raw 再烧录 |
| 忽略 boot.img 的 header_version | 内核无法解析 ramdisk 地址,启动卡死 | 与设备 bootloader 支持的版本保持一致 |
| 修改 system.img 后未重新生成 super.img | 动态分区内容与元数据不一致 | 使用 lpmake 重新打包 super.img |
| dtbo.img 中 DTB 顺序错误 | 硬件描述叠加失败,外设无法识别 | 按设备树 overlay 优先级排列 DTB 条目 |
| 用 mount 直接挂载稀疏镜像 | 提示“wrong fs type” | 先 simg2img 转换,再 mount |
解包与重打包过程中,务必保留原始镜像的校验信息(如 boot.img 的 SHA1、super.img 的元数据)。任何修改后都应重新计算校验值,否则 bootloader 会拒绝加载。