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的执行顺序决定了编译的初始化过程,主要步骤包括:

  1. 环境检查:验证工具链(CROSS_COMPILE)、Python版本、依赖工具。
  2. 平台加载:通过include plat/$(PLAT)/platform.mk加载平台配置。
  3. 架构选择:根据ARCH变量(aarch32/aarch64)加载对应的架构编译选项。
  4. 源文件收集:通过BL_SOURCES、PLAT_BL_COMMON_SOURCES等变量收集所有需要编译的C/汇编文件。
  5. 链接脚本生成:预处理.ld.S文件生成最终的.ld链接脚本。
  6. 编译与链接:执行$(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配置到最终二进制镜像生成的完整流程,重点体现平台宏、编译选项与链接脚本之间的数据流关系。

ATF编译系统与链接脚本交互流程 platform.mk 平台宏、源文件列表 顶层 Makefile 编译规则、工具链 bl31.ld.S 链接脚本模板 预处理阶段(CPP) 展开宏、条件编译、生成 .ld 和 .i 文件 编译阶段(GCC/AS) 生成 .o 目标文件,携带平台宏定义 链接阶段(LD) 使用 .ld 链接脚本,合并段,解析符号 输出镜像(bl31.bin / bl31.elf) 包含平台宏、链接脚本定义的布局 说明:平台宏通过 -D 选项传递到预处理和编译阶段,链接脚本通过预处理生成最终布局

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的加载顺序和变量传递机制
  • 平台宏的定义、分类和传递方式
  • 链接脚本的结构、段定义和优化方法
  • 编译系统的调试技巧,用于快速定位问题