3. Android启动流程与镜像关系
理解Android设备的启动链路,是做好镜像签名与发布工程的前提。因为签名验证并不是在某个单一位置完成的,而是沿着启动链被拆分成多个“信任交接点”:每一级在把控制权交给下一级之前,都要先验证下一级的完整性与合法性。只有把加载顺序和验证介入点对应起来,才能判断某个镜像该由谁签名、签名放在哪里、失败时会在哪个阶段停住。
一、启动链的加载顺序
以典型的高通/通用ARM平台为例,从上电到进入用户界面,大致经历以下阶段:
- BootROM(固化在芯片内):上电后最先运行,代码不可修改。它从固定介质(eMMC/UFS/SPI Flash)读取第一阶段引导程序,并验证其签名。这是整条信任链的根,通常称为信任根(Root of Trust)。
- SPL / PBL / XBL(第一阶段引导):完成最基础的时钟、DDR初始化,然后加载并验证下一级引导程序(如 ABL、LK、U-Boot)。
- 引导加载程序(ABL/LK/U-Boot):负责加载内核与 ramdisk,读取 cmdline、决定启动槽位(A/B)、处理 fastboot 与 recovery 模式,并验证 boot 镜像。
- 内核(kernel):解压并初始化驱动,挂载根文件系统。若启用 dm-verity,内核会校验 system/vendor 等分区的哈希树。
- ramdisk(initramfs):作为临时根文件系统,运行 init,负责挂载真正的 system、vendor、data 分区。
- system / vendor / product 分区:只读挂载,通过 AVB(Android Verified Boot)或 dm-verity 校验后,才启动 Zygote 与系统服务。
可以看到,加载顺序是“BootROM → SPL → 引导程序 → 内核 → ramdisk → system”,而验证顺序与加载顺序严格一致,形成一条不可跳过的链条。
二、签名验证的介入点与责任划分
把上面的链路拆开看,每个阶段的验证对象、验证者和签名归属都不同。发布工程中最容易出错的地方,正是把“谁签的”和“谁验的”搞混。
| 阶段 | 加载对象 | 验证者 | 签名归属 | 失败表现 |
|---|---|---|---|---|
| BootROM | SPL / XBL | BootROM 固化代码 | 芯片厂商/OEM 私钥 | 无法启动,通常无显示 |
| SPL | ABL / LK | SPL | OEM 私钥 | 停在引导早期 |
| 引导程序 | boot.img(内核+ramdisk) | ABL/LK + AVB | OEM 私钥(vbmeta) | 红屏/黄屏,进 fastboot |
| 内核 | system / vendor | dm-verity / AVB | OEM 私钥(vbmeta) | 挂载失败,进 recovery |
| ramdisk | 分区元数据 | init + AVB | OEM 私钥 | 校验和不匹配,拒绝挂载 |
其中 boot.img 与 vbmeta 的关系尤其关键:vbmeta 中保存了 boot、system、vendor 等分区的哈希与签名描述符。引导程序先验证 vbmeta,再用 vbmeta 中的哈希去校验各分区。因此,只重新签名 boot.img 而不更新 vbmeta,会导致校验失败,这是发布流程中最常见的坑之一。
三、典型场景:一次 OTA 后的启动失败排查
假设某设备 OTA 升级后卡在开机 Logo,串口日志出现如下片段:
[BOOTROM] verify spl ... OK
[SPL] load abl ... OK
[ABL] verify vbmeta ... OK
[ABL] verify boot ... FAIL (hash mismatch)
[ABL] boot failed, entering fastboot
日志说明 BootROM、SPL、vbmeta 都通过了,问题出在 boot 分区与 vbmeta 中记录的哈希不一致。常见原因有三类:
- OTA 包只替换了 boot.img,没有同步更新 vbmeta.img;
- 签名时使用了错误的密钥或签名算法(如 SHA256 与 SHA512 混用);
- boot.img 在打包后被二次修改(如注入调试参数),导致哈希变化。
四、常见误区与复盘
误区一:认为“解锁 Bootloader 后就不需要签名验证”。解锁只是允许刷入非官方镜像,AVB 仍可能以“黄屏警告”的方式继续校验,dm-verity 也可能仍然生效。发布工程必须区分“解锁状态”和“验证关闭状态”。
误区二:把 ramdisk 当成独立签名对象。在 Android 10 之后,ramdisk 通常与内核一起打包进 boot.img(或 init_boot.img),由 vbmeta 统一描述。单独替换 ramdisk 而不重新签名 boot,同样会失败。
误区三:忽略 A/B 槽位。设备可能从 slot_b 启动,而 OTA 只更新了 slot_a。验证失败时,应确认当前活动槽位与镜像版本是否匹配。
总结来说,Android 启动链是一条严格的逐级信任链:BootROM 是根,SPL、引导程序、内核、ramdisk、system 依次被加载并验证。签名验证的介入点与加载顺序一一对应,发布工程的核心工作,就是保证每一级的签名、哈希与元数据始终一致,让设备能够顺利走完整条链路并进入系统。