高通学习24--XBL_CONFIG(TODO)

1 编译

下次只需一条 wsl bash /mnt/d/tmp2/run_build10.sh。若环境被重置导致 wrapper 丢失,先跑 wsl bash /mnt/d/tmp2/install_armlink_v3.sh 再编译。

run_build10.s

#!/bin/bash
set -x
ROOT=/home/jc/ar1/aliso-la-1-0_amss_standard_oem/BOOT.MXF.2.2
PYBIN=/home/jc/py3bin
mkdir -p $PYBIN
if [ -e "$PYBIN/python" ] || [ -L "$PYBIN/python" ]; then rm -f "$PYBIN/python"; fi
ln -s /usr/bin/python3.8 "$PYBIN/python"
export PATH="$PYBIN:$PATH"
echo "python now resolves to: $(which python)"
python --version
cd $ROOT/boot_images
if [ ! -e /home/jc/bin/sectools ] && [ ! -L /home/jc/bin/sectools ]; then
  ln -s /home/jc/bin/sectools_wrapper /home/jc/bin/sectools
fi
ls -la /home/jc/bin/sectools
python -u boot_tools/buildex.py -t Aliso -v LAA -r RELEASE > /mnt/d/tmp2/xbl_build_run10.log 2>&1
echo "BUILD EXIT CODE: $?"

install_armlink_v3.sh

#!/bin/bash
# Install arm-link v3 (comment-aware ASSERT stripping) and verify against
# both GccBase.lds (multi-line comments, no ASSERT) and generated DevPrgD.ld
# (orphan trailing */ on ASSERT line + multi-line ASSERT).
set -e
BIN=/pkg/qct/software/llvm/release/arm/14.0.0/bin
SRC=/mnt/d/tmp2/new_arm-link_v3.sh
ROOT=/home/jc/ar1/aliso-la-1-0_amss_standard_oem/BOOT.MXF.2.2

echo "=== backup current arm-link as arm-link.bak.v2 ==="
if [ -f "$BIN/arm-link.bak.v2" ]; then
  echo "arm-link.bak.v2 already exists, overwriting"
fi
cp -f "$BIN/arm-link" "$BIN/arm-link.bak.v2"

echo "=== install v3 (strip CR from Windows drive copy) ==="
tr -d '\r' < "$SRC" > /tmp/arm-link.v3.lf
cp -f /tmp/arm-link.v3.lf "$BIN/arm-link"
chmod +x "$BIN/arm-link"

echo "=== verify line endings (must be ASCII text, LF only) ==="
file "$BIN/arm-link"
if grep -q $'\r' "$BIN/arm-link"; then
  echo "ERROR: CR still present in installed arm-link"
  exit 1
else
  echo "OK: no CR characters"
fi

echo "=== direct python test of v3 preprocess logic ==="
cat > /tmp/preprocess_v3.py <<'PYEOF'
import sys
src, dst = sys.argv[1], sys.argv[2]
out = []
in_comment = False
in_assert = False

def strip_trailing_comment_close(s):
    while s.rstrip().endswith('*/'):
        s = s.rstrip()[:-2].rstrip()
    return s

for ln in open(src, 'r').read().splitlines():
    s = ln
    if in_assert:
        out.append('/* stripped ASSERT cont: ' + s.strip() + ' */')
        code = strip_trailing_comment_close(s)
        if ');' in code:
            in_assert = False
        continue
    parts = []
    i = 0
    n = len(s)
    while i < n:
        if in_comment:
            j = s.find('*/', i)
            if j == -1:
                parts.append(('comment', s[i:]))
                i = n
            else:
                parts.append(('comment', s[i:j + 2]))
                i = j + 2
                in_comment = False
        else:
            j = s.find('/*', i)
            if j == -1:
                parts.append(('code', s[i:]))
                i = n
            else:
                parts.append(('code', s[i:j]))
                parts.append(('comment', s[j:j + 2]))
                i = j + 2
                in_comment = True
    code_str = ''.join(t for k, t in parts if k == 'code')
    comments_str = ''.join(t for k, t in parts if k == 'comment')
    if 'ASSERT' in code_str:
        code_c = strip_trailing_comment_close(code_str)
        out.append('/* stripped ASSERT: ' + code_c.strip() + ' */')
        if comments_str:
            out.append(comments_str)
        if not code_c.rstrip().endswith(');'):
            in_assert = True
        continue
    out.append(''.join(t for k, t in parts))

open(dst, 'w').write('\n'.join(out) + '\n')
PYEOF

echo "--- Test 1: GccBase.lds (multi-line comments) ---"
GCC=$ROOT/boot_images/edk2/BaseTools/Scripts/GccBase.lds
python3.8 /tmp/preprocess_v3.py "$GCC" /tmp/GccBase.v3.test.ld
sed -n '15,30p' /tmp/GccBase.v3.test.ld
echo "..."
echo "--- verify no stray directives / balanced comments ---"
grep -n 'stripped orphan\|unknown' /tmp/GccBase.v3.test.ld && echo "PROBLEM" || echo "OK: no orphan marks"

echo
echo "--- Test 2: generated DevPrgD.ld (ASSERT + orphan */) ---"
LD=$ROOT/boot_images/Build/AlisoLAA/DevprogD/RELEASE_CLANG140LINUX/AARCH64/DevPrgD.ld
python3.8 /tmp/preprocess_v3.py "$LD" /tmp/DevPrgD.v3.test.ld
echo "--- lines 44-66 ---"
sed -n '44,66p' /tmp/DevPrgD.v3.test.ld
echo
echo "--- check Image\$\\\$DEVPRG_DATA_RO\\\$\\\$Base present ---"
grep -n 'DEVPRG_DATA_RO.*Base' /tmp/DevPrgD.v3.test.ld || echo "MISSING!"

echo "=== DONE ==="

问题原因:

这套构建工具链 CLANG140LINUX 实际指向的是 clang/LLD 16.1.2。EDK2 构建系统调用 arm-link 链接(本应是 ARM 的 armlink),但系统里安装的是一个把它翻译成 GNU ld.lld 的 wrapper 脚本。这个 wrapper 是问题根源,因为 ld.lld 与 ARM 工具链行为不同,构建脚本喂给它的参数和链接脚本内容它都处理不了。


问题 1:DevprogD 链接失败(第一次 build 的硬错误)

报错ld.lld: error: /tmp/DevPrgD.ld.noassert.*.ld:60: symbol not found: Image$$DEVPRG_DATA_RO$$Base

排查过程

  • 我先写了 check_noassert.sh 检查 wrapper 生成的中间脚本,定位到根因。
  • 生成的 DevPrgD.ld 第 50 行是一条被注释掉的 ASSERT,格式为 ASSERT(...); */ —— clang -E 预处理时把开头的 /* 剥掉了,只留下孤儿的 */
  • v2 wrapper 的 in_assert 状态机逻辑是:进入 ASSERT 后,检查行尾是否以 ); 结束来退出断言状态。但这一行以 */ 结尾而不是 );,导致 in_assert 一直卡在 True,把后面紧跟的 DEVPRG_DATA_RO 段整段吞掉,于是 Image$$DEVPRG_DATA_RO$$Base 符号缺失。

解决:重写为 v3 注释感知 wrapperarm-link,备份为 arm-link.bak.v2)。核心逻辑:

  • 用 strip_trailing_comment_close() 先剥掉行尾孤儿 */,再做 ); 完整性判断 → 不再误吞后续 section。
  • 同时覆盖单行、多行(第 107-108 行)、带孤儿 */ 三种 ASSERT 形态。

问题 2:Core Sec.dll 链接失败(修完问题 1 后暴露的下一个错误)

报错ld.lld: error: /tmp/GccBase.lds.noassert.651580.ld:23: unknown directive: */

排查过程

  • 这发生在 Core.dsc 的 Sec 模块链接,用的是 EDK2 标准脚本 GccBase.lds
  • 关键发现:GccBase.lds 里有真实的多行注释(第 18 行 /* 打开,第 23 行 */ 闭合),里面没有 ASSERT
  • 我 v3 第一版的“孤儿 */ 处理”写得太激进:只要检测到 */ 就剥掉。结果把合法的闭合 */ 也剥了,留下悬空的 /*,导致第 23 行出现 unknown directive: */

解决:把 v3 重写为完整的注释感知状态机

  • 用标志位 in_comment 跟踪 /* */ 块注释(含多行跨行),合法的注释原样透传、保持顺序;
  • 只在非注释代码中剥离 ASSERT(...)
  • 分别用 GccBase.lds(验证注释完整保留)和生成的 DevPrgD.ld(验证 ASSERT 剥离 + DEVPRG_DATA_RO 保留)做了单元验证通过后,才重新跑 build。

问题 3:Windows 换行符(CRLF)破坏 wrapper

现象:从 Windows 盘(D:\tmp2)拷贝 wrapper 到 WSL 工具链目录后,脚本无法执行(shebang 被 \r 污染)。

解决:安装时统一用 tr -d '\r' 去除 CR,安装脚本里还带自检(grep -q $'\r' 检查并报错)。这是后续所有脚本安装的固定套路。


问题 4:xbl_config 签名警告(非致命)

现象:构建日志出现 No Sectools Folder provided. SKIPPING IMAGE SIGNINGERROR: Could not generate Signed XBL Config

判定非致命 —— 构建继续执行并最终报告全部 8 个镜像成功(含 xbl_config)。属于开发环境签名路径配置为空的行为,走的是 unsigned 路径。不影响产物,无需修复。


最终结果

修复完问题 1+2(即 v3 注释感知 wrapper)后,run10 完整编译成功,0:36mins 产出全部 8 个镜像:XBL_S、XBL_RAMDUMP、UEFI、XBL_S_DEVPRG_NS、JtagProgrammer、xbl_config、SHRM、ImageFv

关键经验总结

报错根因修复
symbol not found: Image$$DEVPRG_DATA_RO$$Basev2 状态机被“孤儿 */ 结尾的注释 ASSERT”卡死,吞掉后续 sectionv3 加 strip_trailing_comment_close() 先剥 */ 再判断
unknown directive: */v3 第一版把合法的多行注释闭合符 */ 也剥掉了v3 重写为完整注释感知状态机,合法注释透传
wrapper 无法执行Windows CRLF 污染 shebangtr -d '\r' + 自检
SKIPPING IMAGE SIGNING开发环境无签名路径非致命,忽略

本次改动完整总结

一、GPIO 修改(产品功能改动)

文件pinctrl.dtsi(WSL 路径 .../boot_images/boot/Settings/Soc/Aliso/Core/SocInfra/TLMM/pinctrl.dtsi

改动内容:把 GPIO 150–153(SSC_QUP_2 的 SSC_4–7)从原配置改为 Hi-Z(高阻/浮空)

(GPIO_INPUT | GPIO_NO_PULL | GPIO_OUT_LOW | GPIO_PRG_YES) /* PIN 150 */
(GPIO_INPUT | GPIO_NO_PULL | GPIO_OUT_LOW | GPIO_PRG_YES) /* PIN 151 */
(GPIO_INPUT | GPIO_NO_PULL | GPIO_OUT_LOW | GPIO_PRG_YES) /* PIN 152 */
(GPIO_INPUT | GPIO_NO_PULL | GPIO_OUT_LOW | GPIO_PRG_YES) /* PIN 153 */

  • 关键区别:原来 150–153 和 148–149 一样是 GPIO_PRG_NO;现在改为 GPIO_PRG_YES(程序可配置 = Hi-Z/浮空)。
  • 数值 0x151 = INPUT(0x1) | NO_PULL(0x10) | OUT_LOW(0x40) | PRG_YES(0x100)
  • 该配置编译进 post-ddr dtb,最终打入 xbl_config.elf,已反复验证(源码 → dtb → ELF 三段链路全部确认 150–153 = 0x151)。

二、为了编译通过所做的修改

(这些是本地工具链替换带来的问题,不是高通源码问题)

问题原因解决
DevprogD ASSERT 失败GNU LLD 链接器不支持 ARM armlink 的 ASSERT() 语法修改链接脚本,去掉/改写 ASSERT
GccBase.lds 注释报错源码注释跨了 token,GNU 语法解析不了调整注释写法
CRLF 行尾问题从 Windows 拷文件导致行尾混乱统一用 tr -d '\r' 转 LF
sectools 找不到/home/jc/bin/sectools 不存在建软链指向 wrapper

核心背景:高通原版用 ARM armlink 编译,原生支持 -marchASSERT() 等语法。本地换成 GNU clang/LLD(CLANG140LINUX 16.1.2)后,需要通过一个注释感知的 arm-link v3 wrapper 来适配,所以才会出现这些"原版不该有"的问题。

三、启动失败问题(本次重点修复)

现象:烧写后 SBL1 加载完 APDP 后报 Error code 9c001008 at boot_elf_driver.c Line 363

根因(完整链条):

  1. 0x9C001008 = BL_ERROR_GROUP_XBL_CONFIG | INITIALIZATION_ERROR —— SBL1 加载 xbl_config.elf 初始化失败;
  2. 构建日志出现 SKIPPING IMAGE SIGNING,签名被跳过;
  3. buildconfig.json 引用 $SECTOOLSROOT/sectools_dir,但 /home/jc/bin/sectools_dir 当时不存在 → GenConfigImage.py 的 isdir 检查失败 → 不签名;
  4. 而签名时 pboot_gen_elf()elf_gen_tools.py)才会补上 头部 NULL 段 + 尾部 hash NULL 段(并把 e_phnum += 2);
  5. 未签名版本只有 9 个 LOAD 段、没有 NULL 头段(135604 字节)→ SBL1 加载器初始化失败。

修复

  1. 永久修复(建好缺失目录,以后编译自动签名):
    ln -sfn /pkg/sectools/v2/latest/Linux/sectools_dir /home/jc/bin/sectools_dir
    

    已确认软链存在且指向正确。
  2. 手动重新签名本次产物,得到可烧写文件:
    • 大小 143544 字节,11 段(NULL 头 + 9 LOAD + NULL hash 尾),与已知可用版 xbl_config_fp.elf 结构完全一致;
    • post-ddr dtb 段中 GPIO 150–153 = 0x151(Hi-Z)确认保留
    • 已复制到 D:\tmp\xbl_config.elf(md5 653a3d14f12c51cf01b044589e27fd16),可直接烧写。

四、下次重新编译步骤

前置/home/jc/bin/sectools_dir 软链已建好,无需再手动签名。

  1. 在 Windows 打开 D:\tmp2\run_build10.sh,按需确认顶部路径(当前为 BOOT.MXF.2.2):

    ROOT=/home/jc/ar1/aliso-la-1-0_amss_standard_oem/BOOT.MXF.2.2
    

  2. 在 WSL 执行:

    bash /mnt/d/tmp2/run_build10.sh
    

    脚本会自动:建 /home/jc/py3bin/python 软链(→ python3.8)、把 py3bin 加进 PATH、确保 /home/jc/bin/sectools 软链存在、然后跑

    python -u boot_tools/buildex.py -t Aliso -v LAA -r RELEASE
    

    完整日志写到 D:\tmp2\xbl_build_run10.log

  3. 编译后校验(可选但建议):

    • 确认日志中 xbl_config 步骤没有 SKIPPING IMAGE SIGNING
    • 检查产物程序头数:
      readelf -l .../Build/AlisoLAA/xblconfig/auto_gen/elf_files/create_cli/xbl_config.elf
      

      应为 11 段LOAD segments: 9 + NULL segments: 2);
    • 产物路径:.../boot_images/Build/AlisoLAA/xblconfig/auto_gen/elf_files/create_cli/xbl_config.elf
  4. 如果是从 Windows 拷贝/编辑过任何 .dtsi/.py/.c 源文件再拷回 WSL,务必先 tr -d '\r' 去掉 CR,避免 CRLF 编译问题。

现在就可以先用 D:\tmp\xbl_config.elf(已签名修复版)重新烧写验证启动。

2 Bootloader

Bootloader 本身当然是可执行程序。这里容易混淆的点是:xbl_config.elf 名字里虽然有 xbl.elf,但它不是完整的 XBL Bootloader 主程序,而是一个由 XBL 加载的配置容器。

高通平台通常把它们拆成两类:

  • xbl.elf:真正执行的 XBL 代码
  • xbl_config.elf:XBL 使用的板级配置数据

大致启动关系是:

Boot ROM
   ↓
加载并验证 XBL
   ↓
执行 xbl.elf
   ↓
XBL 加载 xbl_config.elf
   ↓
解析 CFGL 目录
   ├─ DDR DCB 配置
   ├─ CPR/电压配置
   ├─ pre-DDR DTB
   ├─ post-DDR DTB
   └─ DTBO
   ↓
根据硬件平台初始化 DDR、电源、时钟等
   ↓
继续加载后续启动阶段

3 ELF结构

# `xbl_config.elf` 静态结构分析报告

## 1. 报告目的

本文分析 `D:\tmp\xbl_config.elf` 的文件结构,重点解释:

- 它为什么是 ELF,而不是普通 DTB 或压缩包;
- “9 个部分”具体指什么;
- ELF Header、Program Header、CFGL 目录、配置载荷和安全元数据之间的关系;
- 文件如何被 XBL 加载和解析;
- 哪些结论是从文件中直接验证的,哪些属于基于 Qualcomm 格式的推断。

本文不讨论单个 GPIO 的配置效果。

## 2. 样本信息

| 属性 | 值 |
|---|---|
| 文件名 | `xbl_config.elf` |
| 文件大小 | 143,544 字节(`0x230B8`) |
| SHA-256 | `3C07ADF54086649A679B4D798F52D99148B975B424EC12D5B2BDD7E3ACD8C032` |
| 格式 | ELF64、Little-endian |
| ELF 类型 | `ET_EXEC` |
| Entry Point | `0x1494E000` |
| Program Header 数量 | 11 |
| Section Header 数量 | 0 |

SHA-256 可以用来确认后续分析或刷写的文件是否仍是同一份样本。

## 3. 最重要的结论

所谓“9 个部分”,准确含义是:

> ELF 中有 9 个 `PT_LOAD` Program Segment。

它们由以下内容组成:

1. 一个 `CFGL` 配置目录;
2. 两个 DDR DCB 二进制;
3. 一个 CPR 二进制;
4. 两个主设备树 DTB;
5. 三个平台设备树 Overlay DTBO。

完整 ELF 实际有 11 个 Program Header:

```text
1 个 ELF/Program Header 描述段
+ 9 个 PT_LOAD 配置载荷
+ 1 个 Qualcomm 哈希/签名元数据段
= 11 个 Program Header
```

因此,“9 个部分”作为有效载荷数量是正确的,但不能说整个文件总共只有 9 个 segment,也不能说它有 9 个 ELF section。该文件根本没有 Section Header Table。

## 4. ELF Header 分析

ELF 魔数为:

```text
7F 45 4C 46
```

即 ASCII 的:

```text
\x7FELF
```

主要字段如下:

| ELF 字段 | 值 | 说明 |
|---|---:|---|
| `EI_CLASS` | 2 | ELF64 |
| `EI_DATA` | 1 | Little-endian |
| `e_type` | 2 | `ET_EXEC` |
| `e_machine` | 1 | 工具显示为 `WE32100`,在此类 Qualcomm 容器中不应按普通 CPU 可执行文件理解 |
| `e_entry` | `0x1494E000` | 第一个可加载配置段的虚拟地址 |
| `e_phoff` | `0x40` | Program Header Table 紧跟 64 字节 ELF Header |
| `e_shoff` | 0 | 没有 Section Header Table |
| `e_ehsize` | `0x40` | ELF Header 长度 64 字节 |
| `e_phentsize` | `0x38` | 每个 Program Header 56 字节 |
| `e_phnum` | `0x0B` | 共 11 个 Program Header |
| `e_shnum` | 0 | 没有 section |

### 为什么没有 section

普通编译生成的 ELF 常有 `.text`、`.data`、`.rodata`、`.symtab` 等 section,主要服务于链接、调试和静态分析。

`xbl_config.elf` 是启动阶段消费的打包容器。加载器只需要 Program Header 描述“从文件哪个偏移加载多少字节到哪个地址”,不需要链接器 section。因此:

```text
Section Header Table = 0
Program Header Table = 存在
```

这是一种面向加载器而不是面向调试器的 ELF。

## 5. 物理文件布局

文件布局如下:

```text
0x00000 ┌─────────────────────────────────────┐
        │ ELF Header + 11 Program Headers    │ 0x2A8 bytes
0x002A8 ├─────────────────────────────────────┤
        │ CFGL directory                     │ 0x1EC
0x00494 ├─────────────────────────────────────┤
        │ A006_7_0100_0_dcb.bin              │ 0x8804
0x08C98 ├─────────────────────────────────────┤
        │ A006_7_0100_1_dcb.bin              │ 0x8804
0x1149C ├─────────────────────────────────────┤
        │ cpr.bin                            │ 0x1108
0x125A4 ├─────────────────────────────────────┤
        │ pre-ddr-aliso-1.0.dtb              │ 0x2E4C
0x153F0 ├─────────────────────────────────────┤
        │ post-ddr-aliso-1.0.dtb             │ 0xBA08
0x20DF8 ├─────────────────────────────────────┤
        │ pre-ddr-sxr...overlay.dtbo         │ 0x164
0x20F5C ├─────────────────────────────────────┤
        │ pre-ddr-ssg...overlay.dtbo         │ 0x164
0x210C0 ├─────────────────────────────────────┤
        │ pre-ddr-ssg2...overlay.dtbo        │ 0x164
0x21224 ├─────────────────────────────────────┤
        │ Padding                            │ 0xDDC
0x22000 ├─────────────────────────────────────┤
        │ Qualcomm hash/signature metadata   │ 0x10B8
0x230B8 └─────────────────────────────────────┘
```

9 个载荷从 `0x2A8` 到 `0x21224` 完全连续,中间没有空洞。随后填充 `0xDDC` 字节,使安全元数据从 `0x22000` 边界开始。

## 6. 11 个 Program Header

| PH | 类型 | Flags | 文件偏移 | 加载地址 | 文件大小 | 含义 |
|---:|---|---:|---:|---:|---:|---|
| 0 | `PT_NULL` | `0x07000000` | `0x00000` | 0 | `0x2A8` | ELF 和 Program Header 描述区域 |
| 1 | `PT_LOAD` | `0x01000007` | `0x002A8` | `0x1494E000` | `0x1EC` | CFGL 目录 |
| 2 | `PT_LOAD` | `0x01000007` | `0x00494` | `0x1494E1EC` | `0x8804` | DCB 0 |
| 3 | `PT_LOAD` | `0x01000007` | `0x08C98` | `0x149569F0` | `0x8804` | DCB 1 |
| 4 | `PT_LOAD` | `0x01000007` | `0x1149C` | `0x1495F1F4` | `0x1108` | CPR |
| 5 | `PT_LOAD` | `0x01000007` | `0x125A4` | `0x149602FC` | `0x2E4C` | Pre-DDR DTB |
| 6 | `PT_LOAD` | `0x01000007` | `0x153F0` | `0x14963148` | `0xBA08` | Post-DDR DTB |
| 7 | `PT_LOAD` | `0x01000007` | `0x20DF8` | `0x1496EB50` | `0x164` | SXR Overlay |
| 8 | `PT_LOAD` | `0x01000007` | `0x20F5C` | `0x1496ECB4` | `0x164` | SSG Overlay |
| 9 | `PT_LOAD` | `0x01000007` | `0x210C0` | `0x1496EE18` | `0x164` | SSG2 Overlay |
| 10 | `PT_NULL` | `0x02000000` | `0x22000` | 0 | `0x10B8` | 哈希、签名和证书链相关数据 |

所有 `PT_LOAD` 均满足:

```text
p_filesz == p_memsz
```

说明它们没有类似 ELF `.bss` 的“内存大小大于文件大小”区域,载荷内容全部明确存储在文件中。

所有 9 个载荷的加载地址也是连续的:

```text
0x1494E000 ~ 0x1496EF7C
```

这表明打包器将它们组织成一块连续内存映像,CFGL 目录位于映像起始位置。

## 7. CFGL 目录

第一个载荷开头为:

```text
43 46 47 4C
```

对应 ASCII:

```text
CFGL
```

紧随其后的字段包含:

```text
version/count word = 0x00080002
directory size     = 0x000001EC
```

按 16 位观察:

```text
version = 2
count   = 8
```

目录中确实有 8 个文件名:

```text
/A006_7_0100_0_dcb.bin
/A006_7_0100_1_dcb.bin
/cpr.bin
pre-ddr-aliso-1.0.dtb
post-ddr-aliso-1.0.dtb
pre-ddr-sxr-aliso-1.0-overlay.dtbo
pre-ddr-ssg-aliso-1.0-overlay.dtbo
pre-ddr-ssg2-aliso-1.0-overlay.dtbo
```

它们按顺序对应 PH[2] 到 PH[9]。因此 CFGL 本身不是第九个“配置文件”,而是管理其余 8 个配置对象的索引或目录。

从载荷地址关系可推断,目录中的对象定位可以通过相对偏移、加载地址或打包器记录完成。完整私有字段语义需要 Qualcomm 对应版本的 XBL Config 源码确认,不应仅凭数值强行命名。

## 8. 八个配置对象

### 8.1 两个 DCB 文件

```text
A006_7_0100_0_dcb.bin
A006_7_0100_1_dcb.bin
```

两者大小均为:

```text
0x8804 = 34,820 bytes
```

可读字符串包含:

```text
PLL Pre-Cal
PLL DCC Monitor
Write Level
WCK2CK
DQ PBIT
Global Vref CDC-2D
RDQS CDC CF
Periodic Training
DRAM Vref
```

可以确认它们是 DDR 初始化、校准和训练相关的 DCB 数据。两个文件内容不同,SHA-256 也不同,可能对应不同 DDR 通道、颗粒、训练配置或平台变体;具体含义需结合 Qualcomm DDR 构建输入文件判断。

### 8.2 `cpr.bin`

大小:

```text
0x1108 = 4,360 bytes
```

它是原始二进制数据,没有明显文本描述。根据文件名和 Qualcomm 启动体系,它通常与 CPR(Core Power Reduction)或电压/性能闭环配置有关。此处属于基于名称和平台架构的解释,而非仅靠载荷内容可以完全证明的字段级解析。

### 8.3 Pre-DDR DTB

```text
pre-ddr-aliso-1.0.dtb
```

魔数:

```text
D0 0D FE ED
```

这是 Flattened Device Tree 的大端魔数 `0xD00DFEED`。

该 DTB 包含 DDR 完成前可用的基础平台配置,例如:

```text
PlatformInfo
pinctrl
SDCC
I2C
PMIC/button
boot temperature
DDR common/target data
```

Pre-DDR 阶段可用内存和驱动环境有限,因此该 DTB 相对较小。

### 8.4 Post-DDR DTB

```text
post-ddr-aliso-1.0.dtb
```

它同样是标准 FDT,但大小达到:

```text
0xBA08 = 47,624 bytes
```

明显大于 Pre-DDR DTB。其内容包括更完整的启动平台描述,例如:

```text
内存映射
IORT/SMMU
显示
存储
PIL/子系统加载
TLMM/pinctrl
平台安全和启动服务配置
```

这符合“DDR 建立之后可以加载更完整平台配置”的启动阶段划分。

### 8.5 三个 DTBO

```text
pre-ddr-sxr-aliso-1.0-overlay.dtbo
pre-ddr-ssg-aliso-1.0-overlay.dtbo
pre-ddr-ssg2-aliso-1.0-overlay.dtbo
```

每个文件仅:

```text
0x164 = 356 bytes
```

三者都是标准设备树 Overlay,含有:

```text
fragment@0
__overlay__
display_info
panel_0
width
height
display_panel_type
```

因此它们主要为不同产品形态或显示方案提供很小的差异补丁,而不是完整平台设备树。

三个 DTBO 虽然大小相同、结构相似,但 SHA-256 不同,说明属性值存在差异。

## 9. 安全元数据段

最后一个 Program Header:

```text
offset = 0x22000
size   = 0x10B8
flags  = 0x02000000
```

它不是 `PT_LOAD`,也没有加载地址。内容可见:

```text
SECTOOLS SECP384R1 CURVE TEST ROOT
QUALCOMM
General Use Test Key (for testing only)
SecTools Test User
```

说明该文件经过 Qualcomm SecTools 安全封装,尾部包含哈希、数字签名和证书链类数据。

证书时间字符串包含:

```text
260814130117Z
```

即 UTC 格式的 2026-08-14 13:01:17,与样本生成时间一致。

证书名称明确标注 `TEST` 和 `for testing only`,说明它不是正式量产密钥签名。结合板子的启动日志:

```text
Secure Boot: Off
```

该测试签名可以用于当前开发板,但不能据此推断能在启用 Secure Boot 的量产设备上通过认证。

### 为什么签名区从 `0x22000` 开始

最后一个配置载荷结束于:

```text
0x21224
```

到 `0x22000` 之间有:

```text
0xDDC bytes
```

填充后安全区域对齐到 `0x1000` 边界。这种布局便于启动链对代码/数据区和安全元数据区分别处理。

## 10. 可能的加载流程

根据文件布局和启动日志,可以建立如下模型:

```text
Boot ROM / 前级 XBL
        │
        ▼
读取 xbl_config ELF Header
        │
        ▼
解析 11 个 Program Headers
        │
        ├── 校验尾部 hash/signature/certificate metadata
        │
        ▼
把 9 个 PT_LOAD 连续载荷映射到 0x1494E000 起始内存
        │
        ▼
解析映像开头的 CFGL version 2 directory
        │
        ├── 读取 DCB,进行 DDR 初始化/训练
        ├── 读取 CPR 配置
        ├── 应用 Pre-DDR DTB
        ├── DDR 可用后应用 Post-DDR DTB
        └── 根据平台类型选择相应 DTBO
```

启动日志中出现:

```text
sbl1_xblconfig_init
XBL Config - Image Load, Start
DTB Found: [pre-ddr]...
```

与上述模型一致。

## 11. ELF Flags 的理解边界

标准 ELF 只定义低位的 `PF_R/PF_W/PF_X`:

```text
0x1 = Execute
0x2 = Write
0x4 = Read
```

9 个载荷的 flags 是:

```text
0x01000007
```

低三位 `0x7` 表示 R/W/X 全部置位;高位 `0x01000000` 是 Qualcomm 工具链使用的私有 segment 属性。

首段:

```text
0x07000000
```

尾部安全段:

```text
0x02000000
```

这些高位通常用于 Qualcomm MBN/ELF 封装中区分 PHDR、HASH 等 segment 类型。准确宏名称应以生成该文件的同版本 SecTools/XBL 源码为准。

不能因为 `readelf` 把载荷显示为 `RWE`,就认为这些配置数据会被 CPU 当作代码执行。这里的 ELF 主要是载荷容器,最终如何使用由 XBL Config 解析器决定。

## 12. 每段 SHA-256

| PH | 内容 | SHA-256 |
|---:|---|---|
| 1 | CFGL | `1d1b7833d649e4d2c539d856006c33d279592ce9dff60103d32e910fffbb6de0` |
| 2 | DCB 0 | `e6e1d2c29f6d797efb0ad90dafde3e5dc0a267819c89c8d574c890f386f29062` |
| 3 | DCB 1 | `162cfa2189c66bf022994147d802de64f56f196b4bbdc857e5292e0591c76280` |
| 4 | CPR | `e70e37b34c2462bad1d55946ca977a87058cad6de3f59dd5c51d77442c60ba4e` |
| 5 | Pre-DDR DTB | `75406e269ada50ea2900bf47a884f0f2f8ef8ca13d08423cc225dd3541e7f359` |
| 6 | Post-DDR DTB | `91e70fbeb6b590859cb3d715f5fba2aa91adeef1ffcf5ceffec9449fc44cf385` |
| 7 | SXR DTBO | `e2e0df96079dd96baf7dd4fc103143944acdb98526ea2e31e0cb2b3c32f6107c` |
| 8 | SSG DTBO | `478cd26bf380b59da603f6ffc0028fc4e3a7c59a4f86efdc124b59d5f8117ba5` |
| 9 | SSG2 DTBO | `dec1c181aaaf4d6949f10eb7c511498bdcd572b62fb507eaa949d37eb1b88629` |

这些哈希适合做二进制差异定位:修改某个源配置并重新生成 ELF 后,可以逐段计算哈希,快速判断究竟哪一个载荷发生变化。

## 13. 推荐的学习和对比方法

### 查看 ELF Header 和 Program Headers

```powershell
readelf.py -h -l xbl_config.elf
```

重点观察:

```text
e_phnum
e_shnum
p_type
p_offset
p_vaddr
p_filesz
p_flags
```

### 识别 DTB

FDT 魔数是:

```text
D0 0D FE ED
```

反编译:

```powershell
dtc -I dtb -O dts -o output.dts input.dtb
```

### 比较修改前后 ELF

建议不要直接对整文件做十六进制比较,因为重新签名会让尾部安全段变化。更有效的方法是:

1. 比较 ELF Program Header;
2. 分别提取 9 个 `PT_LOAD`;
3. 对每段计算 SHA-256;
4. 只对哈希变化的载荷做二进制或 DTS 级比较;
5. 单独检查签名段变化。

这样可以区分:

```text
配置内容变化
载荷重新排列
签名/证书时间变化
纯填充变化
```

## 14. 分析结论

`xbl_config.elf` 是一个使用 ELF64 作为外层容器、由 Qualcomm XBL Config 解析器消费的启动配置集合。它不是常规应用程序 ELF,也不是只有一个设备树的简单镜像。

其核心设计是:

```text
ELF 提供加载布局
CFGL 提供配置对象目录
DCB/CPR/DTB/DTBO 提供实际平台数据
SecTools 元数据提供完整性和签名信息
```

“9 个部分”准确指 9 个 `PT_LOAD`:一个 CFGL 目录加八个配置对象。整个文件共有 11 个 Program Header,并且没有 ELF section。

这种格式的优势是:

- 启动加载器可沿用成熟的 ELF 装载逻辑;
- 多个配置对象可被一次签名和认证;
- 不同启动阶段可选择不同 DTB;
- 产品变体可以通过小型 DTBO 覆盖;
- 配置载荷与安全元数据边界清晰;
- 修改后可通过逐 segment 哈希快速定位变化。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值