4、设备树编译与反编译:dtc工具使用、反编译查看硬件拓扑
设备树源文件(.dts / .dtsi)必须经过编译生成二进制设备树 Blob(.dtb),内核启动时才能解析。反之,从 .dtb 反编译回 .dts 是调试硬件拓扑、验证裁剪结果的关键手段。本节围绕 dtc(Device Tree Compiler)工具,系统讲解编译、反编译、查看与校验方法。
核心原则: 编译时保持源文件结构完整,反编译时关注节点层级与属性值,避免因格式差异导致内核解析失败。
4.1 dtc 工具概述
dtc 是设备树的官方编译器,集成在 Linux 内核源码 scripts/dtc/ 目录下,也可通过系统包管理器独立安装。其核心功能包括:
- 编译(Compile):
.dts→.dtb,支持预处理(cpp)和包含文件。 - 反编译(Decompile):
.dtb→.dts,还原可读的硬件描述。 - 校验(Check): 检查语法、节点唯一性、属性合法性。
- 输出格式控制: 支持
.dtb、.dts、.dtbo(overlay)等。
| 命令 | 功能 | 典型用法 |
|---|---|---|
dtc -I dts -O dtb |
编译 .dts 为 .dtb | dtc -I dts -O dtb -o output.dtb input.dts |
dtc -I dtb -O dts |
反编译 .dtb 为 .dts | dtc -I dtb -O dts -o output.dts input.dtb |
dtc -I dtb -O dts -V |
反编译并显示版本信息 | dtc -I dtb -O dts -V 17 -o output.dts input.dtb |
dtc -I dts -O dtb -@ |
编译时保留 overlay 符号 | dtc -I dts -O dtb -@ -o output.dtb input.dts |
安装提示: 在 Ubuntu/Debian 上执行
sudo apt install device-tree-compiler;在 Yocto/Buildroot 中通常已内置。
4.2 编译流程与参数详解
编译过程本质上是将文本化的设备树源文件解析为二进制结构,包含节点树、属性键值对、字符串表、内存保留映射等。以下是一个标准编译示例:
# 基本编译(无依赖)
dtc -I dts -O dtb -o myboard.dtb myboard.dts
# 带预处理(支持 #include 和宏)
cpp -nostdinc -I include -undef -x assembler-with-cpp myboard.dts myboard_preprocessed.dts
dtc -I dts -O dtb -o myboard.dtb myboard_preprocessed.dts
# 一步到位(推荐)
dtc -I dts -O dtb -o myboard.dtb -@ -W no-unit_address_vs_reg myboard.dts
常用编译选项说明:
-I/-O: 指定输入/输出格式(dts / dtb / fs)。-o: 输出文件路径。-@: 生成__symbols__节点,用于设备树 Overlay。-W/-E: 启用/禁用特定警告或错误检查。-V: 指定设备树 Blob 版本(默认 17)。-H: 指定 phandle 格式(legacy / epapr / both)。
常见编译错误: 节点名包含非法字符、reg 属性长度与父节点 #address-cells 不匹配、中断控制器引用缺失。建议编译时始终加上
-W error 将警告视为错误。
4.3 反编译:还原硬件拓扑
反编译是调试和逆向分析的核心技能。通过 dtc -I dtb -O dts 可将二进制 .dtb 还原为可读的 .dts,但反编译结果与原始源文件存在差异:
| 对比项 | 原始 .dts | 反编译 .dts |
|---|---|---|
| 注释 | 保留所有注释 | 全部丢失 |
| 宏/包含 | 保留 #include 和宏 |
展开为具体值 |
| 标签(label) | 保留标签引用 | 转换为 phandle 数字 |
| 节点顺序 | 按源文件顺序 | 按节点路径排序 |
| 属性格式 | 保留原始格式 | 标准化输出 |
反编译典型场景:
# 基本反编译
dtc -I dtb -O dts -o decompiled.dts board.dtb
# 反编译并显示 phandle 值(便于追踪引用)
dtc -I dtb -O dts -o decompiled.dts -@ board.dtb
# 反编译为“伪源文件”格式(保留更多原始信息)
dtc -I dtb -O dts -o decompiled.dts -H epapr board.dtb
调试技巧: 反编译后使用
diff 对比原始 .dts 和反编译 .dts,可快速发现编译阶段丢失的注释或格式问题。对于裁剪验证,反编译能直观展示哪些节点被保留、哪些被删除。
4.4 设备树编译/反编译流程 SVG 图
下图展示了从源文件到二进制、再到反编译还原的完整数据流,以及各阶段的关键操作与产物:
4.5 查看硬件拓扑:从反编译结果分析节点结构
反编译后的 .dts 文件以树形缩进展示所有节点。通过分析节点层级、compatible 属性、reg 地址范围、中断连接关系,可以快速掌握硬件拓扑。以下是一个典型 SoC 反编译片段:
/dts-v1/;
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
chosen {
stdout-path = &uart0;
};
ocp {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
uart0: serial@44e09000 {
compatible = "ti,am3352-uart", "ns16550a";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
clocks = <&clk_uart0>;
status = "okay";
};
i2c0: i2c@44e0b000 {
compatible = "ti,am3352-i2c", "ti,omap4-i2c";
reg = <0x44e0b000 0x1000>;
interrupts = <70>;
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
eeprom@50 {
compatible = "atmel,24c32";
reg = <0x50>;
};
};
};
};
拓扑分析要点:
- 根节点: 包含
model和compatible,用于内核匹配板级。 - 总线节点:
ocp作为简单总线,包含#address-cells、#size-cells、ranges定义地址映射。 - 外设节点:
uart0、i2c0等,通过reg描述基地址和大小,interrupts描述中断号。 - 子节点:
eeprom@50挂在 I2C 总线下,通过reg表示从设备地址。 - 引用:
&uart0是标签引用,反编译后保留为 phandle 或路径。
裁剪验证方法: 在裁剪前后分别反编译
.dtb,使用 diff -u before.dts after.dts 对比节点差异。重点关注 status = "disabled" 的节点是否被移除,以及 reg 地址范围是否因裁剪而改变。
4.6 设备树节点拓扑关系 SVG 图
下图以树形结构展示反编译后设备树的节点层级与连接关系,突出根节点、总线、外设和子设备之间的拓扑:
4.7 编译与反编译的常见问题与避坑
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 编译报错“syntax error” | 缺少分号、括号不匹配、属性值格式错误 | 使用 dtc -I dts -O dtb -o /dev/null input.dts 仅做语法检查 |
| 反编译后节点顺序混乱 | dtb 按路径排序存储,反编译按字母序输出 | 使用 dtc -I dtb -O dts -o output.dts -f 强制保留原始顺序(部分版本支持) |
| 反编译后 phandle 丢失 | 反编译默认不输出 __symbols__ 节点 |
加 -@ 参数保留符号表 |
| 编译后内核无法识别设备 | compatible 字符串拼写错误或大小写不匹配 |
反编译后检查 compatible 值是否与驱动匹配 |
裁剪后 .dtb 体积未减小 |
节点被 status = "disabled" 但未物理删除 |
使用 dtc -I dtb -O dtb -o trimmed.dtb -H epapr 重新打包压缩 |
重要提醒: 反编译结果不可直接用于重新编译!由于注释丢失、宏展开、标签转 phandle,反编译后的
.dts 通常无法通过编译。反编译仅用于查看和调试,修改硬件拓扑应始终基于原始 .dts 源文件。
4.8 实战:裁剪验证中的编译与反编译流程
在驱动模块化裁剪实践中,编译与反编译是验证裁剪正确性的最后关卡。推荐按以下步骤操作:
- 备份原始
.dtb:cp board.dtb board_original.dtb - 反编译原始
.dtb:dtc -I dtb -O dts -o board_original.dts board_original.dtb - 修改源文件进行裁剪: 删除或禁用不需要的节点。
- 编译裁剪后的
.dts:dtc -I dts -O dtb -o board_trimmed.dtb board_trimmed.dts - 反编译裁剪后的
.dtb:dtc -I dtb -O dts -o board_trimmed.dts board_trimmed.dtb - 对比差异:
diff -u board_original.dts board_trimmed.dts | less - 检查节点完整性: 确保关键外设(如串口、网卡、存储)未被误删。
- 部署测试: 将裁剪后的
.dtb替换到启动分区,重启验证。
效率提升: 编写 Makefile 或 shell 脚本自动化上述流程,每次裁剪后自动执行编译→反编译→diff 对比,快速定位差异。