3. 开发环境搭建:交叉编译工具链安装、依赖库准备、Makefile基础与编译选项
说实话,ATF 开发环境搭建这件事,看着简单,但坑是真不少。我见过太多人卡在第一步——工具链装好了,编译却报一堆莫名其妙的错。嗯,今天咱们就把这事彻底捋清楚。
3.1 交叉编译工具链的选择与安装
ATF 是跑在 ARM 平台上的固件,你肯定不能用 PC 的 gcc 去编译它。你需要一套交叉编译工具链。说白了,就是在 x86 电脑上生成 ARM 机器码的工具集。
核心原则:工具链的版本必须与你的目标 ARM 架构匹配。AArch64 和 AArch32 的工具链不通用。
3.1.1 我常用的工具链
我个人习惯用 ARM 官方提供的 GNU-A 系列工具链。你可以从 ARM 官网的「Developer」->「Downloads」->「GNU Toolchain」找到它。下载时注意区分:
- aarch64-none-elf-:用于编译 64 位 ARMv8-A 裸机程序,ATF 的 BL1/BL2/BL31 都用它。
- arm-none-eabi-:用于编译 32 位 ARM 裸机程序,如果你在移植老平台可能会用到。
下载后解压到 /opt/toolchains/ 目录,然后把 bin/ 路径加到 PATH 环境变量里。我在项目中遇到过有人把工具链装到带空格的路径下,结果 Makefile 解析路径时直接崩了。所以,路径里千万别有空格或中文。
小技巧:装好后跑一下 aarch64-none-elf-gcc -v,确认版本号。我建议用 10.3-2021.07 或更新的版本,太老的工具链对 ATF 新特性的支持有问题。
3.1.2 验证工具链是否可用
写个最简单的 C 文件,交叉编译一下:
// test.c
void main(void) {
while(1);
}
$ aarch64-none-elf-gcc -c test.c -o test.o
$ aarch64-none-elf-objdump -d test.o
如果能正常反汇编出 AArch64 指令,说明工具链没问题。如果报错说找不到 crt0.o 或 _start,别慌——裸机程序本来就没有这些,你只需要加 -nostdlib 选项就行。
3.2 依赖库准备
ATF 编译时依赖几个关键库。你想想看,它要处理加解密、镜像解析、平台初始化,这些功能总不能全自己手写吧?
| 依赖库 | 用途 | 获取方式 |
|---|---|---|
| mbedtls | 加密算法(SHA256、RSA、ECDSA) | ATF 源码自带 mbedtls/ 子模块 |
| fiptool | 打包 FIP 镜像的工具 | ATF 编译时自动生成 |
| dtc | 设备树编译器(某些平台需要) | 系统包管理器安装或源码编译 |
注意:mbedtls 的版本要和 ATF 匹配。我曾经因为用了系统自带的旧版 mbedtls,结果编译 ATF 2.6 时签名验证一直失败。后来发现 ATF 源码里自带了 mbedtls 子模块,直接用 git submodule update --init 拉下来最省心。
拉取子模块的命令:
$ cd atf
$ git submodule update --init --recursive
如果你在编译时遇到 mbedtls_config.h 找不到的错误,八成是子模块没拉全。嗯,这个坑我踩过两次。
3.3 Makefile 基础与编译选项
ATF 的构建系统基于 Makefile。你不需要精通 Makefile 语法,但得知道几个关键变量和编译流程。
3.3.1 核心 Makefile 变量
编译 ATF 时,你通过 make 命令传入参数。我个人习惯把这些参数写到一个 build.sh 脚本里,避免每次敲一长串。
$ make CROSS_COMPILE=aarch64-none-elf- \
PLAT=qemu \
DEBUG=1 \
BL33=../u-boot/u-boot.bin \
all fip
| 变量名 | 说明 | 常用值 |
|---|---|---|
CROSS_COMPILE |
交叉编译工具链前缀 | aarch64-none-elf- |
PLAT |
目标平台名称 | qemu, fvp, mt8186 |
DEBUG |
是否开启调试信息 | 1 或 0 |
BL33 |
非安全世界镜像路径(通常是 U-Boot) | ../u-boot/u-boot.bin |
LOG_LEVEL |
日志输出等级 | 40(ERROR), 50(NOTICE) |
调试建议:开发阶段把 DEBUG=1 和 LOG_LEVEL=40 打开,这样能看到详细的启动日志。量产时记得关掉,否则固件体积会变大,启动速度也会变慢。
3.3.2 编译目标(target)
ATF 的 Makefile 提供了几个常用目标:
all:编译所有 BL 镜像(BL1、BL2、BL31)。fip:生成 FIP 镜像包,把 BL2、BL31、BL33 打包到一起。clean:清理编译产物。distclean:彻底清理,包括配置文件。
你想想看,如果只跑 make all,你得到的是分散的 bl1.bin、bl2.bin、bl31.bin。但实际烧录时,通常只需要一个 fip.bin 文件。所以 make all fip 是我最常用的组合。
3.3.3 平台配置与编译选项
每个平台都有自己的配置文件,放在 plat/<vendor>/<platform>/ 目录下。比如 QEMU 平台的配置在 plat/qemu/qemu/ 里。
你可以通过 make PLAT_<xxx>=1 的方式开启或关闭某些功能。举个例子:
$ make CROSS_COMPILE=aarch64-none-elf- \
PLAT=qemu \
DEBUG=1 \
PLAT_QEMU_MMAP=1 \
all fip
这些平台特有选项的定义,你得去对应平台的 platform.mk 文件里查。我在项目中遇到过有人想当然地加了一个 PLAT_MY_FEATURE=1,结果 Makefile 根本不认,编译出来的固件行为完全不对。所以,加选项前一定先确认平台 Makefile 里有没有定义它。
3.4 知识体系与编译流程
下面这张图总结了 ATF 开发环境搭建的核心逻辑。你顺着这个流程走,基本不会出大问题。
3.5 避坑指南
最后,我把这些年踩过的坑总结一下。你照着做,能省不少时间。
我曾经犯过的错:
- 工具链版本太老:用 4.9 版本的 Linaro 工具链编译 ATF 2.8,结果链接时报错说
__aeabi_uldivmod找不到。换到 10.3 版本后问题消失。 - 忘记拉子模块:直接
git clone完就编译,mbedtls 目录是空的,编译到一半报错。记得跑git submodule update --init。 - PLAT 参数拼错:把
PLAT=qemu写成PLAT=QEMU,Makefile 找不到平台目录,直接报错退出。大小写敏感,注意检查。 - BL33 路径给错:给了相对路径但当前工作目录不对,编译成功但 FIP 包里没有 BL33,启动时卡在
Waiting for BL33。建议用绝对路径。
嗯,环境搭建这部分就这些内容。你把工具链装好、子模块拉全、Makefile 参数写对,编译 ATF 就是一条命令的事。别在这上面花太多时间,后面还有更多有意思的东西等着你呢。