3. 设备树编译与反编译:DTC工具使用、DTS到DTB的编译、DTB反编译为DTS、反编译实战
设备树源文件(.dts)是人类可读的文本描述,而内核引导时实际使用的是二进制格式的设备树二进制文件(.dtb)。设备树编译器(DTC) 是完成这两种格式之间转换的核心工具。掌握 DTC 的使用,是嵌入式 Linux 驱动开发者的基本功。
核心概念: DTC 不仅负责将
.dts 编译为 .dtb,还支持将 .dtb 反编译回 .dts,以及将 .dts 预处理为包含所有 #include 文件的单一文件。这一能力在调试、逆向分析、验证设备树正确性时极为关键。
3.1 DTC工具概述与安装
DTC 工具通常随内核源码一起发布,位于 scripts/dtc/ 目录下。大多数 Linux 发行版也提供独立的软件包。
| 平台 | 安装命令 | 说明 |
|---|---|---|
| Ubuntu/Debian | sudo apt-get install device-tree-compiler |
安装独立 DTC 包 |
| Fedora/CentOS | sudo dnf install dtc |
安装独立 DTC 包 |
| 内核源码编译 | make scripts 或 make dtc |
在内核根目录下编译生成 |
验证安装是否成功:
$ dtc --version
Version: DTC 1.6.1
版本注意: 不同版本 DTC 对设备树语法的检查严格程度不同。建议使用内核源码自带的 DTC 版本(通常与内核版本匹配),以避免因编译器版本差异导致的兼容性问题。
3.2 DTS到DTB的编译流程
将 .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
$ ls -lh myboard.dtb
-rw-rw-r-- 1 user user 8.2K Mar 15 10:30 myboard.dtb
编译过程中,DTC 会执行以下检查:
- 语法检查: 验证分号、花括号、引号是否匹配
- 节点唯一性检查: 确保同一层级没有重复的节点名
- 属性值类型检查: 验证
reg、interrupts等属性的格式 - 引用检查: 验证
&label引用的标签是否存在
3.3 DTB反编译为DTS
反编译是将二进制 .dtb 文件还原为可读的 .dts 文本。这在以下场景中非常有用:
- 逆向分析: 查看第三方板卡或闭源内核中的设备树配置
- 调试验证: 确认编译后的
.dtb是否包含预期的节点和属性 - 格式转换: 将
.dtb转换为.dts后进行修改再重新编译
反编译命令格式:
dtc -I dtb -O dts -o <输出文件.dts> <输入文件.dtb>
-I dtb:指定输入格式为 DTB 二进制-O dts:指定输出格式为 DTS 文本
实战示例:
$ dtc -I dtb -O dts -o myboard_reverse.dts myboard.dtb
$ head -30 myboard_reverse.dts
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
model = "My Custom Board";
compatible = "vendor,myboard";
...
};
反编译局限性:
- 反编译结果会丢失原始
.dts中的注释和宏定义 - 标签(label)名称可能被替换为自动生成的名称(如
__symbols__节点) - 二进制文件中不包含
#include信息,反编译结果是一个单一文件 - 某些属性值(如
phandle)可能以原始数值形式出现
3.4 反编译实战:从目标板提取设备树
在实际嵌入式开发中,经常需要从正在运行的目标板上提取设备树。以下是完整的操作流程:
| 步骤 | 操作 | 命令/说明 |
|---|---|---|
| 1 | 连接目标板 | 通过串口或 SSH 登录到嵌入式设备 |
| 2 | 定位 DTB 文件 | ls /sys/firmware/devicetree/base/ 或 find / -name "*.dtb" |
| 3 | 复制到主机 | scp root@target:/sys/firmware/fdt ./target.dtb |
| 4 | 反编译 | dtc -I dtb -O dts -o target.dts target.dtb |
| 5 | 查看结果 | less target.dts 或使用文本编辑器打开 |
从 /sys 文件系统直接读取:
# 在目标板上执行
# 将当前运行的设备树导出为二进制文件
cat /sys/firmware/fdt > /tmp/running.dtb
# 复制到开发主机后反编译
$ dtc -I dtb -O dts -o running.dts running.dtb
3.5 常用DTC选项速查表
| 选项 | 功能 | 使用场景 |
|---|---|---|
-I dts |
指定输入为 DTS 文本格式 | 编译源文件 |
-I dtb |
指定输入为 DTB 二进制格式 | 反编译二进制文件 |
-O dts |
指定输出为 DTS 文本格式 | 反编译输出 |
-O dtb |
指定输出为 DTB 二进制格式 | 编译输出 |
-@ |
生成符号节点(__symbols__) | 支持设备树覆盖(overlay) |
-H epapr |
生成 ePAPR 标准头部 | PowerPC 等架构 |
-V 17 |
指定 DTB 版本为 17 | 兼容旧版内核 |
-Wno-unit_address_vs_reg |
忽略单元地址与 reg 不匹配警告 | 处理遗留设备树 |
-E no |
禁用错误检查 | 快速编译(不推荐生产使用) |
3.6 常见编译错误与解决方案
错误1:
Error: myboard.dts:10.3-11 syntax error
原因: 第10行附近存在语法错误,常见于缺少分号、花括号不匹配、属性值格式错误。
解决: 检查对应行附近的语法,确保每个属性以分号结尾,节点以花括号包裹。
错误2:
FATAL ERROR: Unable to parse input tree
原因: 输入文件格式与 -I 参数不匹配,或文件损坏。
解决: 确认输入文件类型,使用 file 命令检查:file myboard.dtb。
错误3:
Warning (unit_address_vs_reg): /soc/gpio@... node has a unit name, but no reg property
原因: 节点名包含地址后缀(如 gpio@ff000000),但缺少对应的 reg 属性。
解决: 添加 reg 属性,或使用 -Wno-unit_address_vs_reg 忽略警告。
3.7 本章小结
设备树的编译与反编译是嵌入式开发中的基础操作。通过 DTC 工具,开发者可以:
- 将可读的
.dts源文件编译为内核可解析的.dtb二进制文件 - 从运行中的系统提取
.dtb并反编译为.dts,用于逆向分析或验证 - 利用 DTC 的语法检查功能,在开发早期发现设备树中的错误
熟练掌握这些操作,能够显著提高设备树开发和调试的效率,为后续的中断控制器配置、外设驱动匹配等高级主题打下坚实基础。