EDK2 平台 Lauterbach T32 调试脚本解析:DXE 与 SEC/PEI 阶段 UEFI 固件符号加载指南

发布时间:2026/10/4 1:43:14
EDK2 平台 Lauterbach T32 调试脚本解析:DXE 与 SEC/PEI 阶段 UEFI 固件符号加载指南
固件操作系统驱动开发嵌入式【免费下载链接】edk2EDK II项目地址https://gitcode.com/gh_mirrors/ed/edk2点击查看免费下载导读本文围绕 EDK2 仓库中 EmbeddedPkg/Scripts/LauterbachT32 目录下的 TRACE32T32调试脚本展开讲解如何在 Lauterbach TRACE32 调试器环境中为 UEFI 固件加载符号既能在 DXE 阶段利用系统表System Table与调试信息表自动发现并加载各 DXE 模块的调试符号也能在 SEC/PEI 阶段通过显式指定 Firmware Volume 基址手动加载符号。读完本文你将掌握do EfiLoadDxe、do EfiLoadFv两条核心命令的用法、配套脚本EFI.CMM、T32.CMM、EfiProcessPeImage.cmm、EfiProcessTeImage.cmm的底层实现原理以及如何将脚本封装为工具栏按钮以提升日常调试效率。一、脚本集概览为 UEFI 固件调试准备的 TRACE32 工具链EmbeddedPkg/Scripts/LauterbachT32目录为 EDK2 固件开发者提供了一整套与 Lauterbach TRACE32 调试器配合使用的脚本文件清单如下文件作用Readme.md使用说明定义 DXE 阶段与 SEC/PEI 阶段两种调试流程EfiLoadDxe.cmm根据 EFI 系统表地址自动扫描 Debug Image Info Table 并逐个加载模块符号EfiLoadFv.cmm根据 Firmware Volume 基址解析 FFS 文件与 Section为 SEC/PEI 镜像加载符号EfiProcessPeImage.cmm解析 PE32 镜像头提取 DWARF/CodeView 调试信息并执行data.load.elfEfiProcessTeImage.cmm处理 TETerse Executable镜像修正去除 PE 头后的 RVA 偏移后加载符号EFI.CMMEFI 调试环境的启动配置脚本注册 Reset/Load Symbols 等工具栏按钮T32.CMMTRACE32 全局启动脚本模板可拷贝到C:\T32目录负责定位工作目录并调用 EFI 配置脚本从脚本的版权注释可以看出这套脚本最早由 Hewlett-Packard 于 2011 年贡献2024 年由 Ampere Computing 更新见 EfiLoadDxe.cmm属于 EDK2 中跨厂商沉淀的调试基础设施。两种调试阶段的划分Readme 明确将调试流程分为两个阶段这也是后续所有脚本的设计依据DXE 阶段系统已启动到 DXE 核心初始化完成此时内存中已存在 System Table 和 Debug Information Table因此可以自动探测调试信息表的位置并加载符号SEC/PEI 阶段镜像尚未进入 DXE 环境不存在可自动发现的调试信息表因此必须手动传入存放 SEC/PEI 代码的内存映射 Firmware Volume 地址。下面分别展开这两条路径。二、DXE 阶段调试EfiLoadDxe 自动符号加载2.1 使用方式按 Readme 的说明让系统启动到 DXE 核心初始化完成的时刻System Table 与 Debug Information Table 已驻留内存然后在 TRACE32 的命令区执行do EfiLoadDxe 0xGST_ADDRESS其中GST_ADDRESS是EFI_SYSTEM_TABLE全局系统表的地址可通过调试器中的全局变量gST查看。也可以像 Readme 提到的那样把该命令绑定到工具栏按钮实现一键加载全部 DXE 符号。2.2 底层原理从系统表到调试信息表EfiLoadDxe.cmm的核心逻辑在FindDebugInfo子程序中EfiLoadDxe.cmm它完全不依赖编译器符号表而是直接从内存中的 EFI 数据结构出发读取系统表字段脚本对传入的系统表地址SystemTable做两个固定偏移读取——0x68处读取NumberOfTableEntries配置表项数0x70处读取ConfigurationTable数组指针。对照 UefiSpec.h 中EFI_SYSTEM_TABLE的结构定义Hdr、FirmwareVendor、ConsoleInHandle、ConIn、ConOut、StdErr、RuntimeServices、BootServices、NumberOfTableEntries、ConfigurationTable这两个偏移正是 64 位架构下系统表末尾两个字段的位置。搜索调试信息表 GUID脚本逐项遍历EFI_CONFIGURATION_TABLE每项 0x18 字节16 字节 GUID 8 字节指针按小端序拆分为四个 DWORD 与固定值比对匹配值对应 GUID 字节序0x49152E7749152e77-…0x47641ADA…-1ada-4764-…0xFE7AA2B7…-b7a2-7afe-fed9-5e8b0x8B5ED9FE…该 GUID 正是 UEFI 规范定义的EFI_DEBUG_IMAGE_INFO_TABLE_GUID其完整定义位于 MdePkg/Include/Guid/DebugImageInfoTable.h。解析调试信息表头命中后从配置表项的0x10处读取表指针dbghdr随后读取dbghdr4得到TableSize条目数、dbghdr8得到EfiDebugImageInfoTable数组指针。这与 DebugImageInfoTable.h 中EFI_DEBUG_IMAGE_INFO_TABLE_HEADER的字段布局完全一致UpdateStatus、TableSize、EfiDebugImageInfoTable。遍历并加载每个镜像对数组中的每一项读取其ImageInfoType若为0x01EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL定义见 DebugImageInfoTable.h则从8处取LoadedImageProtocolInstance指针再从0x40偏移读取ImageBase对应EFI_LOADED_IMAGE_PROTOCOL结构中的ImageBase字段最后调用do ~~~~/EfiProcessPeImage.cmm imagebase处理该 PE 镜像。TRACE32 脚本中的~~~~前缀表示当前脚本所在的目录因此EfiLoadDxe.cmm无论被放在哪个路径都能正确找到同目录下的EfiProcessPeImage.cmm。2.3 DXE 符号加载调用链整个 DXE 阶段符号加载形成一条清晰的调用链do EfiLoadDxe 0xgST └─ FindDebugInfo: 系统表 → 配置表 → Debug Image Info Table └─ do EfiProcessPeImage ImageBase 每个模块 └─ data.load.elf ELF路径 基址 /NOCODE /NOCLEAR其中data.load.elf是 TRACE32 加载 ELF 符号的命令/NOCODE /NOCLEAR参数表示只加载符号而不烧写代码、不清空已有内容适合目标已在内存中运行时的纯符号附着场景。三、SEC/PEI 阶段调试EfiLoadFv 手动指定 Firmware Volume3.1 为什么需要手动指定地址Readme 明确指出SEC/PEI 镜像没有可自动探测的途径因为这些镜像驻留在内存映射的 Firmware VolumeFV中而 FV 尚未被 DXE 核心的配置表机制登记。因此必须把存放 SEC/PEI 代码的 FV 基址显式告诉调试器do EfiLoadFv \addr\其中\addr\是包含 SEC 或 PEI 代码的 Firmware Volume 基址。为了使用方便Readme 建议封装一个板级脚本例如MyBoardLoadSec.cmm内部固定调用EfiLoadFv传入该板子的 FV 地址再把这个脚本映射到 T32 菜单或工具栏按钮实现快捷访问——这一封装 按钮的模式正是 T32.CMM 与 EFI.CMM 中menu.rp / toolbar / toolitem机制所演示的用法。3.2 FV 解析流程EfiLoadFv.cmmEmbeddedPkg/Scripts/LauterbachT32/EfiLoadFv.cmm按 Firmware Volume 的规范布局逐层解析校验 FV 签名读取fvbase0x28处的 4 字节必须等于0x4856465F即 ASCII_FVHEFI_FIRMWARE_VOLUME_HEADER.Signature否则打印FV does not have proper signature, exiting并退出读取 FV 长度与首文件偏移fvbase0x20处是FvLengthfvbase0x30的低 16 位是HeaderLength首个 FFS 文件就从fvbase HeaderLength开始遍历 FFS 文件每个 FFS 文件头的0x14处为大小 类型组合字段低 24 位是文件大小高 8 位是文件类型文件按8 字节边界对齐当读到大小为0xFFFFFF的哨兵值时结束遍历遍历 SectionFFS 文件数据从0x18处开始FFS 文件头长度每个 Section 头同样是低 24 位大小 高 8 位类型Section 按4 字节边界对齐按类型分发EFI_SECTION_PE32类型0x10调用EfiProcessPeImageEFI_SECTION_TE类型0x12调用EfiProcessTeImage其他类型打印unknown section type。这一解析路径与 EDK2 固件卷规范一致FV → FFS 文件 → Section → PE32/TE 镜像只是把固件代码在 PEI 阶段的遍历逻辑用 TRACE32 脚本重新实现了一遍属于典型的用调试器脚本复刻固件引导逻辑的做法。四、镜像调试信息提取EfiProcessPeImage 与 EfiProcessTeImage无论是 DXE 阶段的自动加载还是 SEC/PEI 阶段的手动加载最终都要落到对单个镜像的处理上由两个处理器脚本完成。4.1 EfiProcessPeImage解析 PE32 镜像EfiProcessPeImage.cmm 的执行流程定位 PE 头通过 DOS 头0x3C处的e_lfanew找到PE\0\0签名位置filehdrstart读取调试目录 RVA在脚本约定的 PE 头偏移0xf10处读取调试目录Debug Directory的 RVA若为 0 则打印no debug dir并结束校验调试类型读取调试目录项0xc处的Type字段只接受0x02CodeView与0xdf两种取值否则提示debug type is not dwarf读取调试数据 RVA 与 DWARF 签名调试目录项0x14处是AddressOfRawData调试数据的 RVA在该位置读取签名0x66727764小端 ASCIIdwrf→ 路径偏移0xc0x3031424E小端 ASCIINB10即 CodeView NB10 记录→ 路径偏移0x10 两种签名分别对应 DWARF 与 CodeView 两种调试信息格式读取 ELF 路径并计算加载基址从调试数据中读出 ELF 符号文件的路径字符串elfpath基址取BaseOfCode与BaseOfData中较小且非零者baseofcode、baseofdata分别取自 PE 头0x28、0x2c处的 RVA 加镜像基址因为符号文件可能按代码段或数据段地址排布加载符号data.load.elf elfpath elfbase /NOCODE /NOCLEAR并包裹ON ERROR GOSUB / ON error容错保证单个镜像失败不影响整个遍历。4.2 EfiProcessTeImage处理去除 PE 头的 TE 镜像TETerse Executable镜像为节省空间去掉了部分 PE 头因此所有 RVA 都需要减去被剥离的头部字节数。EfiProcessTeImage.cmm 的处理要点从 TE 头0x4处的高 16 位读出StrippedSizeTE 头中记录被剥离的 PE 头大小再减去 0x28TE 头自身 40 字节得到实际需修正的偏移量strippedsize调试目录 RVA 从0x20读取后同样减去strippedsize修正符号加载基址取imgstart BaseOfCode(imgstart0xc) - StrippedSize注释说明 TE 镜像假定符号以代码段基址为准其余 DWARF/CodeView 签名校验、路径读取与data.load.elf /NOCODE /NOCLEAR逻辑与 PE32 版本一致。两个脚本一前一后覆盖了 EDK2 固件中最常见的两种可执行镜像格式且与 EfiLoadFv.cmm 中0x10PE32/0x12TE两种 Section 类型一一对应。五、环境配置EFI.CMM 与 T32.CMM 的接线方式5.1 EFI.CMMEFI 调试会话的工具栏入口EFI.CMM 是面向 EFI 调试会话的配置脚本主要动作radix hex将输入/显示进制切换为十六进制通过menu.rp向工具栏注册三个工具项Reset Target快捷键RS→sys.ResetTargetLoad EFI DXE Symbols快捷键DX→do EfiLoadDxeLoad EFI Runtime Symbols快捷键RT→do EfiLoadRuntimeDxesystem.config.debugaccessport 0、system.config.corebase 0x80001000、system.attach配置调试访问端口、设置核基址并附着目标break.sel.program onchip选择片内程序断点setup.var %hex.on / %decimal.OFF变量显示强制十六进制。需要说明的是从脚本调用看EfiLoadRuntimeDxe被期望用于运行时Runtime驱动符号加载但该脚本并未随本目录一同发布目录中只有EfiLoadDxe.cmm等五个 .cmm 文件属于留待开发者按 EfiLoadDxe.cmm 模式自行补充的扩展点。5.2 T32.CMMTRACE32 全局启动脚本T32.CMM 是 TRACE32 的默认启动程序模板注释要求拷贝到C:\T32目录使用要点包括GLOBAL wcdir并预设wcdirD:\bios注释明确提示必须修改为实际的工作目录注册 Source/List、Memory Dump、Register、Watch、Stack、Breakpoints、Symbols、System Settings 等常用调试窗口的工具栏按钮按语言加载~~/t32语言.men菜单文件autostore , history bookmark记录并恢复历史书签最后chdir wcdir\Platform\T32_Scripts切换到脚本目录并do EFI执行 EFI 配置脚本——这就是 EFI.CMM 被接入 TRACE32 启动流程的默认路径约定。六、调试工作流总结与注意事项把两部分串联起来一套完整的 TRACE32 调试 EDK2 固件的工作流为将T32.CMM部署到 TRACE32 安装目录并修改wcdir指向本地工作目录启动 TRACE32由T32.CMM自动进入 EFI 配置脚本完成目标附着与工具栏注册SEC/PEI 阶段在命令行执行do EfiLoadFv FV基址或通过MyBoardLoadSec.cmm封装后点击按钮为引导早期代码加载符号DXE 阶段等 DXE 核心初始化完成、gST可用后执行do EfiLoadDxe 0xgST或直接点击Load EFI DXE Symbols按钮自动完成全部 DXE 模块的符号加载之后即可在 TRACE32 中进行源码级单步、断点、变量查看等常规调试。使用时有几点值得注意阶段前提DXE 自动加载依赖EFI_DEBUG_IMAGE_INFO_TABLE已发布到系统配置表这是 MdePkg/Include/Guid/DebugImageInfoTable.h 所定义的 GUID 机制在运行时的产物SEC/PEI 阶段则无此前提但需要开发者准确掌握 FV 基址架构敏感偏移脚本中以固定偏移读取系统表、调试信息表、LoadedImageProtocol 等结构如0x68/0x70/0x40这些数值与 64 位架构下的结构布局匹配移植到 32 位目标时需重新核对结构体偏移符号文件路径脚本最终依赖镜像调试目录中记录的 ELF 符号文件路径data.load.elf因此构建固件时需确保调试信息指向的目标文件在调试主机上可访问可扩展入口EfiLoadFv.cmm的分发逻辑PE32/TE 类型分发与EfiProcessPeImage.cmm/EfiProcessTeImage.cmm形成了清晰的插件式结构若要支持新镜像格式或新的调试信息签名可以在这三个脚本的基础上扩展。总而言之这套脚本把 UEFI 规范的运行时数据结构系统表、配置表、调试信息表、固件卷、FFS、Section转化为 TRACE32 可执行的符号加载逻辑是调试 EDK2 平台固件早期启动流程时一份可直接复用的实战资产。赞分享固件操作系统驱动开发嵌入式【免费下载链接】edk2EDK II项目地址https://gitcode.com/gh_mirrors/ed/edk2点击查看免费下载相关推荐EDK2 UEFI固件项目针对高通骁龙平台EDK2 UEFI固件项目针对高通骁龙平台 1. 项目基础介绍及主要编程语言 本项目名为 edk2 msm 是EDK2EFI Development Kit固件嵌入式驱动开发操作系统革命性UEFI固件项目edk2-msm解锁高通平台多系统启动的终极指南革命性UEFI固件项目edk2 msm解锁高通平台多系统启动的终极指南 想要在 高通平台 上实现 多系统启动 吗edk2 msm项目为你打开了通往多系统启动固件嵌入式驱动开发操作系统OvmfPkg 平台 CI 指南使用 edk2 Pytools 本地构建与验证 OVMF 固件OvmfPkg 平台 CI 指南使用 edk2 Pytools 本地构建与验证 OVMF 固件 OvmfPkg 是 EDK II 中面向 QEMU/KVM 虚固件操作系统驱动开发嵌入式上一篇Remote Developer jobs directory专家分享5位成功远程开发者的求职经验与建议下一篇解决PHP容器日志难题webdevops/Dockerfile stdout重定向最佳实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考