4. TF-A BL2 镜像签名与加载校验

任务说明

本案例面向ARM Trusted Firmware-A(TF-A)的BL2镜像生成与验证环节。BL2是Trusted Boot流程中的第二级引导加载程序,负责在BL1之后初始化DRAM并加载BL31/BL32/BL33。本案例目标为:使用TF-A源码编译生成BL2镜像文件,并通过解析ELF头与符号表,验证镜像的加载地址(LOAD地址)和入口点(Entry Point)与链接脚本中声明的值一致。

实现方案

采用TF-A标准编译流程生成BL2镜像(bl2.elf与bl2.bin)。随后编写一个独立的Python验证脚本,通过pyelftools库解析bl2.elf,提取以下关键信息:

  • ELF文件头中的入口地址(e_entry)
  • 第一个LOAD段的虚拟地址(p_vaddr)与物理地址(p_paddr)
  • 符号表中bl2_entrypoint符号的地址

同时从链接脚本(bl2.ld.S)中提取预期的BL2_BASE值,通过编译宏定义传入验证脚本,最终比对四者是否一致。

代码实现

以下为验证脚本verify_bl2.py的完整实现:


#!/usr/bin/env python3
"""
verify_bl2.py - 验证BL2镜像加载地址与入口点
用法: python3 verify_bl2.py <bl2.elf路径> <BL2_BASE值(十六进制, 如0x4020000)>
"""
import sys
from elftools.elf.elffile import ELFFile

def main():
    if len(sys.argv) != 3:
        print("用法: python3 verify_bl2.py <bl2.elf> <BL2_BASE>")
        sys.exit(1)

    elf_path = sys.argv[1]
    expected_base = int(sys.argv[2], 16)

    with open(elf_path, 'rb') as f:
        elf = ELFFile(f)

        # 1. 从ELF头获取入口点
        entry = elf['e_entry']
        print(f"[1] ELF入口点 (e_entry)     : 0x{entry:08x}")

        # 2. 遍历程序头,找到第一个LOAD段
        load_vaddr = None
        load_paddr = None
        for seg in elf.iter_segments():
            if seg['p_type'] == 'PT_LOAD':
                load_vaddr = seg['p_vaddr']
                load_paddr = seg['p_paddr']
                print(f"[2] LOAD段虚拟地址 (p_vaddr) : 0x{load_vaddr:08x}")
                print(f"    LOAD段物理地址 (p_paddr) : 0x{load_paddr:08x}")
                break

        if load_vaddr is None:
            print("错误: 未找到LOAD段")
            sys.exit(1)

        # 3. 从符号表获取bl2_entrypoint
        sym_entry = None
        for sec in elf.iter_sections():
            if sec['sh_type'] == 'SHT_SYMTAB':
                for sym in sec.iter_symbols():
                    if sym.name == 'bl2_entrypoint':
                        sym_entry = sym['st_value']
                        print(f"[3] bl2_entrypoint符号地址   : 0x{sym_entry:08x}")
                        break
            if sym_entry is not None:
                break

        if sym_entry is None:
            print("错误: 符号表中未找到 bl2_entrypoint")
            sys.exit(1)

    # 4. 与链接脚本期望值比对
    print(f"[4] 链接脚本期望BL2_BASE   : 0x{expected_base:08x}")

    checks = [
        ("入口点 == BL2_BASE", entry == expected_base),
        ("LOAD虚拟地址 == BL2_BASE", load_vaddr == expected_base),
        ("LOAD物理地址 == BL2_BASE", load_paddr == expected_base),
        ("bl2_entrypoint == BL2_BASE", sym_entry == expected_base),
    ]

    all_pass = True
    for name, result in checks:
        status = "通过" if result else "失败"
        print(f"  [{status}] {name}")
        if not result:
            all_pass = False

    if all_pass:
        print("\n验证结果: 全部一致,BL2镜像加载地址与入口点正确。")
    else:
        print("\n验证结果: 存在不一致,请检查链接脚本与编译配置。")
        sys.exit(1)

if __name__ == "__main__":
    main()

编译与生成

在TF-A源码根目录执行以下命令生成BL2镜像(以FVP平台为例):


make PLAT=fvp BL33=/path/to/bl33.bin DEBUG=1 \
     BL2_BASE=0x4020000 \
     all -j$(nproc)

编译产物位于build/fvp/debug/bl2/bl2.elf与bl2.bin。其中BL2_BASE=0x4020000为链接脚本中定义的加载地址,需与平台内存映射一致。

验证方法

运行验证脚本,传入生成的ELF文件与编译时使用的BL2_BASE值:


python3 verify_bl2.py build/fvp/debug/bl2/bl2.elf 0x4020000

脚本将依次打印ELF入口点、LOAD段虚拟/物理地址、bl2_entrypoint符号地址,并与期望的BL2_BASE逐一比对。

判定标准

验证通过需同时满足以下条件:

  • 脚本输出的四项检查全部显示“通过”;
  • ELF入口点、LOAD段虚拟地址、LOAD段物理地址、bl2_entrypoint符号地址四者数值完全一致,且等于编译时传入的BL2_BASE;
  • 脚本退出码为0,并打印“全部一致”的结论。

若任一检查失败,说明链接脚本中的地址定义与编译参数不一致,需核对bl2.ld.S中BL2_BASE的赋值及平台内存布局。

TF-A 编译 make PLAT=fvp BL2_BASE=0x4020000 链接脚本 bl2.ld.S 定义 BL2_BASE 生成产物 bl2.elf + bl2.bin verify_bl2.py 解析ELF头/段/符号表 一致性比对 e_entry p_vaddr / p_paddr bl2_entrypoint 全部 == BL2_BASE ? 判定标准 四项全部相等 且等于 BL2_BASE → 验证通过