3、设备树编译器:DTC工具的使用与反编译
设备树写好了,怎么变成内核能认的东西?
答案就是 DTC——Device Tree Compiler。说白了,它就是把 .dts 文本文件,编译成 .dtb 二进制文件的工具。反过来,也能把 .dtb 反编译成 .dts,方便我们调试。
我最早接触 DTC 的时候,觉得这玩意儿不就是个编译器嘛,有啥好学的?后来踩了几个坑才发现,它的参数、选项、甚至版本差异,都能让你折腾半天。今天咱们就把它彻底搞明白。
3.1 DTC 是什么?
DTC 是内核源码里自带的工具。路径在 scripts/dtc/ 下面。你编译内核的时候,它会被自动编译出来。
它的工作流程很简单:
- 输入:.dts 文件(文本)
- 输出:.dtb 文件(二进制)
- 或者反过来:.dtb → .dts
嗯,这里要注意:DTC 还支持 .dtsi 文件,那是设备树的头文件,用 #include 包含进来。我见过有人把 .dtsi 直接当 .dts 编译,结果报错半天找不到原因。
核心命令格式:
dtc -I dts -O dtb -o output.dtb input.dts
参数说明:
-I:输入格式(dts/dtb/fs)-O:输出格式(dts/dtb)-o:输出文件名
3.2 编译:从 .dts 到 .dtb
假设你写了一个 myboard.dts,想编译成内核能加载的二进制文件。命令如下:
dtc -I dts -O dtb -o myboard.dtb myboard.dts
如果一切顺利,你会得到一个 .dtb 文件。但现实往往没那么美好。我遇到过最常见的错误是:
- 语法错误:少了个分号,或者花括号不匹配
- 引用错误:
&phandle引用的节点不存在 - 属性值类型错误:比如把
reg写成了字符串
我的调试习惯:
先用 -V 参数指定版本。比如 -V 17 是较新的格式。有些旧版 bootloader 只认老版本,这时候就得用 -V 16。我曾经在某个项目里,因为版本不匹配,uboot 死活加载不了设备树,折腾了两天才发现是版本问题。
3.3 反编译:从 .dtb 到 .dts
反编译是调试利器。你拿到一个 .dtb 文件,想看看里面到底定义了啥?直接反编译:
dtc -I dtb -O dts -o output.dts input.dtb
反编译出来的 .dts 文件,和原始 .dts 可能不完全一样。比如:
- 注释会被丢掉
- 节点顺序可能变化
- 一些宏定义会被展开
但这不影响你理解它的结构。我经常用反编译来验证:我写的 .dts 编译后,是不是我想要的样子。
注意:
反编译出来的 .dts 不能直接拿来再编译回去。因为丢失了注释和格式信息。如果你需要修改,建议还是改原始 .dts 文件。
3.4 常用选项与技巧
DTC 的选项不少,但常用的就这几个:
| 选项 | 说明 | 我的用法 |
|---|---|---|
-@ |
生成插件支持信息 | 做设备树插件时必加 |
-H |
指定 phandle 格式 | 默认就行,别乱改 |
-W |
启用警告 | 我习惯加 -Wno-unit_address_vs_reg 屏蔽一些无关警告 |
-E |
启用错误检查 | 调试时必开 |
举个例子,我常用的调试命令:
dtc -I dts -O dtb -o test.dtb test.dts -Wno-unit_address_vs_reg -@
为什么加 -Wno-unit_address_vs_reg?因为很多老设备树里,reg 属性和 unit-address 不匹配,内核其实能处理,但 DTC 会报 warning。看着烦,干脆屏蔽掉。
3.5 反编译实战:分析一个 .dtb
假设你拿到一个 kernel.dtb,想看看里面定义了哪些外设。反编译后,你会看到类似这样的结构:
/dts-v1/;
/ {
model = "MyBoard";
compatible = "vendor,myboard";
chosen {
bootargs = "console=ttyS0,115200";
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>;
};
uart@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
};
};
看到没?chosen 节点里定义了启动参数,memory 节点定义了内存范围,uart 节点定义了串口信息。这就是内核启动时,从设备树里读到的硬件信息。
我有个习惯:拿到一块新板子,第一件事就是反编译它的 .dtb,看看里面到底配了啥。这比看原理图快多了。
3.6 常见问题与避坑
- 编译出来的 .dtb 比预期大? 检查是不是包含了不必要的
#include。我曾经因为包含了一个巨大的 .dtsi,导致 .dtb 体积翻倍。 - 反编译后节点顺序乱了? 正常现象。DTC 内部会重新排序。不影响功能。
- 编译报错说 phandle 重复? 检查是不是两个节点用了相同的
label。每个label必须唯一。 - uboot 加载 .dtb 失败? 试试
-V 16编译。有些老版本 uboot 不兼容新格式。
我的工作流:
写 .dts → 编译 → 反编译验证 → 确认无误后烧录。反编译这一步,我几乎每次都做。它能帮你发现很多隐藏问题,比如属性值写错了、节点路径不对等等。
3.7 本章小结
DTC 工具,说白了就是设备树的翻译官。把人类可读的文本,翻译成机器可读的二进制。反过来也能把二进制翻译回文本,方便我们调试。
记住几个关键点:
- 编译用
-I dts -O dtb - 反编译用
-I dtb -O dts - 调试时加
-W和-E选项 - 注意版本兼容性
下一章,咱们会深入设备树的语法细节。到时候你会发现,DTC 只是第一步,真正的大头还在后面。
公众号:蓝海资料掘金营,微信deep3321