2. 设备树编译与反编译:DTC工具的使用
设备树源文件(.dts / .dtsi)必须经过编译生成二进制设备树 Blob(.dtb),才能被引导加载程序(如 U-Boot)或内核解析。DTC(Device Tree Compiler)是完成这一转换的标准工具,同时它也支持将 .dtb 反编译回可读的 .dts,用于调试、逆向或验证编译结果。
2.1 DTC 工具概述
DTC 是 Linux 内核源码树中维护的设备树编译器,通常位于 scripts/dtc/ 目录下。它支持三种主要操作模式:
| 操作模式 | 输入 | 输出 | 典型用途 |
|---|---|---|---|
| 编译 | .dts / .dtsi |
.dtb |
将可读的源文件转换为内核可解析的二进制格式 |
| 反编译 | .dtb |
.dts |
将二进制 blob 还原为可读源文件,用于调试或验证 |
| 输出 DTS 源码 | .dts |
.dts(格式化) |
检查预处理后的完整设备树,包含所有 #include 展开 |
提示:在嵌入式 Linux 项目中,DTC 通常由内核构建系统(Kbuild)自动调用。但手动使用 DTC 进行编译和反编译,是排查设备树问题的必备技能。
2.2 编译:从 DTS 到 DTB
编译设备树源文件的基本命令格式如下:
dtc -I dts -O dtb -o <输出文件.dtb> <输入文件.dts>
其中:
-I dts:指定输入格式为 DTS 源文件-O dtb:指定输出格式为 DTB 二进制-o:指定输出文件名
实际示例:
# 基本编译
dtc -I dts -O dtb -o myboard.dtb myboard.dts
# 启用空格优化(减小 DTB 体积)
dtc -I dts -O dtb -o myboard.dtb -@ myboard.dts
# 输出编译过程的详细信息
dtc -I dts -O dtb -o myboard.dtb -V myboard.dts
注意:如果源文件使用了
#include 预处理指令(如 #include "skeleton.dtsi"),必须先通过 C 预处理器(cpp)展开,再送入 DTC。内核构建系统会自动处理这一步骤,但手动编译时需使用 -E 选项或先运行 cpp。
2.3 反编译:从 DTB 到 DTS
反编译用于将二进制 DTB 还原为可读的 DTS 源文件,这在以下场景中非常有用:
- 验证编译结果是否符合预期
- 从已部署的设备中提取设备树配置
- 分析第三方或闭源设备树的布局
基本命令格式:
dtc -I dtb -O dts -o <输出文件.dts> <输入文件.dtb>
实际示例:
# 基本反编译
dtc -I dtb -O dts -o myboard_reverse.dts myboard.dtb
# 反编译并保留所有标签(label)
dtc -I dtb -O dts -o myboard_reverse.dts -@ myboard.dtb
# 反编译时禁用符号表展开(更接近原始源文件)
dtc -I dtb -O dts -o myboard_reverse.dts -s myboard.dtb
关键区别:反编译生成的 DTS 文件会丢失原始源文件中的注释、宏定义和部分格式信息,但节点结构、属性值和寄存器地址完全保留。反编译结果适合用于分析,不适合直接作为源文件回编。
2.4 编译与反编译流程
下图展示了 DTC 在设备树开发流程中的核心位置,以及编译与反编译之间的双向转换关系:
2.5 常用 DTC 选项速查
以下表格列出了 DTC 最常用的命令行选项,帮助你在不同场景下快速选择正确的参数:
| 选项 | 说明 | 使用场景 |
|---|---|---|
-I <格式> |
指定输入格式:dts、dtb、fs(文件系统) |
编译时用 -I dts,反编译时用 -I dtb |
-O <格式> |
指定输出格式:dts、dtb、asm(汇编) |
编译时用 -O dtb,反编译时用 -O dts |
-o <文件> |
指定输出文件名 | 所有操作模式 |
-@ |
生成符号表(symbols),支持设备树覆盖 | 需要动态覆盖功能时必须启用 |
-s |
反编译时简化输出,不展开符号引用 | 反编译时保留原始节点引用结构 |
-V |
显示详细的编译/反编译过程信息 | 调试编译错误或验证文件路径 |
-E |
仅执行预处理(展开 #include),不编译 |
检查预处理后的完整源文件 |
-H |
指定 phandle 格式:legacy、epapr |
兼容旧版内核或特定引导程序 |
2.6 编译与反编译的常见问题
常见错误 1:未启用覆盖支持
如果编译时未使用
解决方案:在编译主 DTB 和覆盖 DTS 时,始终添加
如果编译时未使用
-@ 选项,生成的 DTB 将不包含符号表。后续加载设备树覆盖(DT Overlay)时,内核无法解析覆盖目标,导致覆盖失败。解决方案:在编译主 DTB 和覆盖 DTS 时,始终添加
-@ 选项。
常见错误 2:反编译结果与源文件不一致
反编译生成的 DTS 文件会丢失原始源文件中的注释、宏定义(如
解决方案:反编译结果仅用于分析节点结构和属性值,不要直接作为源文件回编。
反编译生成的 DTS 文件会丢失原始源文件中的注释、宏定义(如
#define)和部分格式排版。这是正常现象,因为 DTB 二进制格式不保留这些信息。解决方案:反编译结果仅用于分析节点结构和属性值,不要直接作为源文件回编。
最佳实践:在嵌入式项目中,建议将 DTC 集成到 Makefile 或构建脚本中,并统一使用
-@ 选项。同时,保留原始 .dts 源文件作为唯一真相来源,反编译仅用于调试和验证。
2.7 编译与反编译的对比总结
下表从多个维度对比了 DTC 编译与反编译操作的核心差异:
| 对比维度 | 编译(DTS → DTB) | 反编译(DTB → DTS) |
|---|---|---|
| 主要用途 | 生成内核可解析的二进制设备树 | 调试、验证、逆向分析 |
| 信息保留 | 保留所有节点、属性、标签、符号表 | 保留节点和属性,丢失注释和宏定义 |
| 输出可读性 | 二进制,不可直接阅读 | 文本格式,可直接阅读和编辑 |
| 依赖预处理 | 需要先展开 #include |
不需要,DTB 已包含完整信息 |
| 覆盖支持 | 需加 -@ 生成符号表 |
反编译时 -@ 保留符号引用 |
| 典型文件大小 | 几 KB 到几十 KB | 几十 KB 到几百 KB |