4. ATF编译系统与配置:使用Makefile构建ATF,理解平台宏、编译选项与链接脚本
ATF(Arm Trusted Firmware)的编译系统基于GNU Make构建,是整个固件开发流程的基石。理解其Makefile体系、平台宏定义、编译选项传递机制以及链接脚本(Linker Script)的结构,是进行启动优化和功能裁剪的前提。本章将深入剖析ATF的构建系统,帮助开发者掌握从源码到二进制镜像的完整编译链路。
4.1 ATF Makefile 体系结构
ATF的Makefile采用分层设计,顶层Makefile负责全局规则,平台和架构相关的配置通过子Makefile和条件包含实现。核心文件包括:
| 文件/目录 | 作用 |
|---|---|
Makefile |
顶层入口,定义编译目标、工具链、通用编译选项 |
make_helpers/ |
辅助函数,如defaults.mk(默认值)、build_macros.mk(构建宏) |
plat/<platform>/ |
平台目录,包含platform.mk定义平台宏和源文件 |
include/ |
公共头文件,包含架构定义和编译选项声明 |
关键概念:ATF的编译系统通过
PLAT变量选择平台,通过BUILD_BASE和BUILD_PLAT控制输出目录,所有平台相关的配置集中在plat/<platform>/platform.mk中。
4.1.1 顶层Makefile执行流程
顶层Makefile的执行顺序决定了编译的初始化过程,主要步骤包括:
- 环境检查:验证工具链(
CROSS_COMPILE)、Python版本、依赖工具。 - 平台加载:通过
include plat/$(PLAT)/platform.mk加载平台配置。 - 架构选择:根据
ARCH变量(aarch32/aarch64)加载对应的架构编译选项。 - 源文件收集:通过
BL_SOURCES、PLAT_BL_COMMON_SOURCES等变量收集所有需要编译的C/汇编文件。 - 链接脚本生成:预处理
.ld.S文件生成最终的.ld链接脚本。 - 编译与链接:执行
$(MAKE) -C $(BUILD_PLAT) ...进行实际构建。
编译命令示例:
# 编译QEMU平台(AArch64)的BL1/BL2/BL31
make PLAT=qemu ARCH=aarch64 DEBUG=1 \
CROSS_COMPILE=aarch64-linux-gnu- \
all fip
4.2 平台宏与编译选项
平台宏是ATF实现硬件抽象和功能定制的核心机制。通过platform.mk中的宏定义,开发者可以控制代码的编译路径、功能开关和内存布局。
4.2.1 平台宏分类
| 宏类别 | 示例 | 作用 |
|---|---|---|
| 平台标识 | PLAT_qemu |
区分不同平台,用于条件编译 |
| 内存配置 | PLAT_PHY_ADDR_SPACE_SIZE |
定义物理地址空间大小(如1TB/4TB) |
| 功能开关 | ENABLE_PIE |
启用位置无关可执行文件(PIE)支持 |
| 调试选项 | ENABLE_BACKTRACE |
启用函数调用回溯打印 |
| 安全特性 | ENABLE_AMU |
启用活动监控单元(AMU)支持 |
4.2.2 编译选项传递机制
ATF通过Makefile变量将编译选项传递给编译器,典型的传递路径如下:
# 在platform.mk中定义平台宏
$(eval $(call add_define,PLAT_qemu))
$(eval $(call add_define,PLAT_PHY_ADDR_SPACE_SIZE,40))
# 在顶层Makefile中,这些宏通过 -D 选项传递给编译器
CFLAGS += -D$(PLAT_DEFINES)
ASFLAGS += -D$(PLAT_DEFINES)
注意:平台宏必须在
platform.mk中通过add_define函数添加,才能被正确传递到编译器和汇编器。直接在源文件中使用#define可能导致与其他平台的冲突。
4.3 链接脚本(Linker Script)解析
链接脚本决定了ATF各个BL镜像(BL1/BL2/BL31)在内存中的布局。ATF使用.ld.S模板文件,通过预处理生成最终的.ld文件,支持条件化段定义。
4.3.1 链接脚本结构
以BL31为例,其链接脚本bl31.ld.S的核心结构如下:
OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
MEMORY {
RAM (rwx): ORIGIN = BL31_BASE, LENGTH = BL31_LIMIT - BL31_BASE
}
SECTIONS {
. = BL31_BASE;
.text : {
*bl31_entrypoint.o(.text*)
*(.text*)
*(.vectors)
} >RAM
.rodata : {
*(.rodata*)
} >RAM
.data : {
*(.data*)
} >RAM
.bss : {
__BSS_START__ = .;
*(.bss*)
*(COMMON)
__BSS_END__ = .;
} >RAM
.xlat_table (NOLOAD) : {
*(.xlat_table)
} >RAM
}
4.3.2 关键段说明
| 段名 | 内容 | 对齐要求 | 备注 |
|---|---|---|---|
.text |
代码段,包含入口点和异常向量表 | 4KB(页对齐) | 入口点必须位于段首 |
.rodata |
只读数据,如平台配置、字符串常量 | 8字节 | 可合并到.text以节省空间 |
.data |
已初始化全局变量 | 8字节 | 启动时从ROM拷贝到RAM |
.bss |
未初始化全局变量 | 8字节 | 启动时清零 |
.xlat_table |
页表转换表 | 页对齐 | 使用NOLOAD标记,不占用镜像文件空间 |
4.4 编译系统与链接脚本的交互流程
下图展示了从Makefile配置到最终二进制镜像生成的完整流程,重点体现平台宏、编译选项与链接脚本之间的数据流关系。
4.5 平台宏实战:以QEMU平台为例
以QEMU虚拟平台为例,展示如何通过平台宏控制编译行为。QEMU平台的platform.mk位于plat/qemu/目录下,核心配置如下:
# plat/qemu/platform.mk 关键内容
PLAT_INCLUDES := -Iinclude/plat/arm/common/ \
-Iplat/qemu/include/
# 平台宏定义
$(eval $(call add_define,PLAT_qemu))
$(eval $(call add_define,PLAT_PHY_ADDR_SPACE_SIZE,40))
$(eval $(call add_define,PLAT_VIRT_ADDR_SPACE_SIZE,40))
# 功能开关
ENABLE_PIE := 1
ENABLE_SVE_FOR_NS := 0
ENABLE_AMU := 0
# 源文件列表
BL31_SOURCES += plat/qemu/qemu_bl31_setup.c \
plat/qemu/qemu_pm.c \
plat/qemu/qemu_topology.c
优化技巧:在启动优化场景中,可以通过
ENABLE_PIE=0禁用PIE支持,减少镜像大小约2-4KB;通过ENABLE_AMU=0禁用AMU,减少约1KB的代码段。这些宏在platform.mk中设置后,编译系统会自动裁剪相关代码。
4.6 链接脚本优化实践
链接脚本的优化直接影响固件的启动速度和内存占用。以下是针对快速启动场景的优化策略:
| 优化项 | 方法 | 效果 |
|---|---|---|
| 段合并 | 将.rodata合并到.text段,减少段头开销 |
减少镜像大小约0.5-1KB |
| 对齐调整 | 将非关键段的对齐从4KB降为64字节 | 减少填充空间,加快加载速度 |
| BSS预清零 | 在链接脚本中标记NOLOAD段,避免镜像中包含零数据 |
减少镜像大小,启动时由BL2/BL31自行清零 |
| 入口点优化 | 将入口函数放在.text段首,确保跳转指令在第一条 |
减少启动时的指令缓存缺失 |
注意:修改链接脚本后,必须确保
BL31_BASE和BL31_LIMIT与平台内存映射一致。错误的基地址会导致启动时异常或内存覆盖。
4.7 编译系统调试技巧
在开发过程中,经常需要调试编译选项和链接脚本。ATF提供了以下调试手段:
- 打印预处理结果:使用
make PLAT=qemu ... CPPFLAGS=-E查看预处理后的链接脚本。 - 查看编译命令:在Makefile中添加
$(info ...)打印变量值,或使用make -p查看所有变量。 - 生成映射文件:在链接脚本中添加
--print-map选项,生成.map文件分析段布局。 - 使用
--verbose选项:在链接命令中添加-Wl,--verbose查看链接脚本的解析过程。
快速验证:在修改
platform.mk后,执行make PLAT=qemu clean && make PLAT=qemu bl31 -n(-n表示只打印命令不执行),检查编译选项是否正确传递。
4.8 本章小结
ATF的编译系统通过分层Makefile、平台宏和链接脚本实现了高度可配置的固件构建。开发者需要掌握:
- Makefile的加载顺序和变量传递机制
- 平台宏的定义、分类和传递方式
- 链接脚本的结构、段定义和优化方法
- 编译系统的调试技巧,用于快速定位问题