第3章:设备树编译器与反编译器——dtc工具使用、编译与反编译实战
设备树源文件(.dts)写好了,怎么变成机器能识别的二进制文件(.dtb)?反过来,拿到一个现成的.dtb,怎么看看里面到底配了啥?
这就得靠我们今天的主角——dtc(Device Tree Compiler)。
说实话,我刚入行那会儿,觉得这工具就是个黑盒子。敲个命令,.dts变.dtb,完事。后来有一次调试一个外设死活不工作,我把.dtb反编译一看,才发现是某个引脚配置被覆盖了。嗯,从那以后,我再也不敢小看dtc了。
3.1 dtc工具简介
dtc是设备树的编译器和反编译器。它负责把人类可读的.dts文本,转换成内核能解析的二进制.dtb。反过来也一样。
它通常包含在device-tree-compiler这个软件包里。你可以在Linux主机上直接安装:
sudo apt-get install device-tree-compiler # Debian/Ubuntu
sudo yum install dtc # CentOS/RHEL
我个人习惯在开发机上装一个最新版本。因为有些老版本对某些语法支持不够好,比如/omit-if-no-ref/这种标签,老版本直接报错。
3.2 编译:从.dts到.dtb
最基本的编译命令长这样:
dtc -I dts -O dtb -o output.dtb input.dts
参数说明:
-I dts:输入格式是dts(源文件)-O dtb:输出格式是dtb(二进制)-o output.dtb:指定输出文件名
举个例子,我经常这样编译:
dtc -I dts -O dtb -o myboard.dtb myboard.dts
如果.dts文件里还包含了其他.dtsi文件(通过#include),dtc会自动展开。所以编译前,确保所有头文件路径都对。
-i 参数指定搜索路径。比如:dtc -I dts -O dtb -i ./include -o output.dtb input.dts。我在项目中经常这么干,省得把一堆头文件拷来拷去。
3.3 反编译:从.dtb到.dts
反编译就是把二进制文件还原成文本。命令格式几乎一样,只是输入输出格式互换:
dtc -I dtb -O dts -o output.dts input.dtb
比如,你想看看某个开发板自带的.dtb里到底配了哪些设备:
dtc -I dtb -O dts -o board.dts board.dtb
反编译出来的.dts文件,结构跟你手写的几乎一样。但有一点要注意——注释会丢失。因为.dtb里不保存注释信息。所以反编译出来的代码,看起来会比较“干”,没有解释说明。
3.4 常用选项与实战技巧
除了基本的编译反编译,dtc还提供了一些实用选项。我挑几个常用的说说:
| 选项 | 说明 | 我的用法 |
|---|---|---|
-@ |
生成插件(overlay)支持信息 | 做动态设备树时必加 |
-H |
指定phandle格式(legacy或epapr) | 默认epapr,兼容性最好 |
-V |
显示版本信息 | 先看看版本,避免踩坑 |
-S |
指定.dtb文件的最小尺寸(bytes) | 很少用,除非有特殊对齐要求 |
举个例子,如果你在做设备树插件(overlay),编译时一定要加-@:
dtc -I dts -O dtb -@ -o overlay.dtbo overlay.dts
不加的话,内核加载插件时会报错,说找不到符号表。我刚开始做overlay时就忘了加,折腾了半天才发现是这个问题。
3.5 编译错误与常见问题
dtc编译时,最常见的错误有两类:
- 语法错误:比如漏了分号、括号不匹配、属性值格式不对。dtc会告诉你具体在哪一行。
- 引用错误:比如
&uart1引用的节点不存在,或者phandle重复。
举个例子,这种错误很常见:
Error: myboard.dts:42:1 - syntax error
看到这种提示,先检查第42行附近有没有少分号或大括号。我个人的排查习惯是:先用dtc -I dts -O dts -o /dev/null input.dts做一次“空编译”,只检查语法,不生成文件。这样能快速定位问题。
- 编译:
dtc -I dts -O dtb -o xxx.dtb xxx.dts - 反编译:
dtc -I dtb -O dts -o xxx.dts xxx.dtb - 做overlay时加
-@ - 反编译会丢失注释,节点顺序也可能变化
3.6 实战:完整编译与反编译流程
假设你有一个简单的设备树文件test.dts:
/dts-v1/;
/ {
compatible = "myboard,test";
#address-cells = <1>;
#size-cells = <1>;
uart0: serial@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
};
};
编译它:
dtc -I dts -O dtb -o test.dtb test.dts
然后反编译看看:
dtc -I dtb -O dts -o test_reverse.dts test.dtb
打开test_reverse.dts,你会发现内容基本一样,但注释没了,节点顺序可能微调。这就是反编译的“原汁原味”——只保留数据,不保留格式。
好了,dtc的基本用法就这些。说白了,它就是设备树世界的“翻译官”。你写得好不好,它只管编译过不过。但真正的高手,是能从反编译的.dts里看出门道的——比如某个外设的寄存器地址对不对、中断号配没配错。这些,我们后面章节再细聊。