3、设备树编译与反编译:dtc工具的使用、dtb与dts的相互转换、反编译调试技巧
设备树源文件(.dts)写完后,不能直接给内核用。你得把它编译成二进制格式——也就是 .dtb 文件。反过来,拿到一个现成的 .dtb,你也得能把它还原成可读的 .dts,才能分析问题。
这个环节的核心工具就是 dtc(Device Tree Compiler)。别看它名字里带个「编译器」,其实它既能编译也能反编译。我这些年调试过的板子,从 ARM 到 RISC-V,几乎都离不开它。
3.1 dtc 工具基础
dtc 是设备树生态的基石。它把人类可读的文本格式转成内核能解析的扁平二进制格式。反过来,它也能把二进制还原成文本。
大部分 Linux 发行版都预装了 dtc。你可以先确认一下:
dtc --version
如果没装,安装也很简单:
# Debian/Ubuntu
sudo apt-get install device-tree-compiler
# Fedora/RHEL
sudo dnf install dtc
我个人习惯用源码编译最新版,尤其是遇到一些老版本 dtc 的 bug 时。从 dtc 官方仓库 拉下来,make 一下就行。
scripts/dtc/ 目录下也自带了一份 dtc。如果你在搞内核开发,直接用那套就行,版本匹配度最高。
3.2 dts 与 dtb 的相互转换
这是 dtc 最基础的两个操作。我把它列成一张表,方便你对照:
| 操作 | 命令 | 说明 |
|---|---|---|
| 编译(dts → dtb) | dtc -I dts -O dtb -o output.dtb input.dts |
将源文件编译成二进制 |
| 反编译(dtb → dts) | dtc -I dtb -O dts -o output.dts input.dtb |
将二进制还原为源文件 |
| 编译(带插件) | dtc -@ -I dts -O dtb -o output.dtb input.dts |
支持插件(overlay)的编译 |
举个例子。假设你有一个 myboard.dts:
/dts-v1/;
/ {
compatible = "mycompany,myboard";
#address-cells = <1>;
#size-cells = <1>;
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>;
};
chosen {
bootargs = "console=ttyS0,115200";
};
};
编译它:
dtc -I dts -O dtb -o myboard.dtb myboard.dts
反编译回来看看:
dtc -I dtb -O dts -o myboard_reverse.dts myboard.dtb
打开 myboard_reverse.dts,你会发现内容和原文件几乎一样,只是格式上可能多了一些对齐和注释。这说明 dtc 的反编译质量很高。
.dts 会丢失原始文件中的注释。因为注释在编译阶段就被丢弃了。所以,重要信息别只写在注释里。
3.3 反编译调试技巧
反编译不只是「把二进制转回文本」这么简单。在实际调试中,它有几个非常实用的场景。
3.3.1 查看内核实际使用的设备树
板子启动后,内核用的是哪个 .dtb?它和你的源文件一致吗?
从 /sys/firmware/devicetree 里可以直接导出:
dtc -I fs -O dts -o /tmp/current.dts /sys/firmware/devicetree/base
这个命令把内核当前解析的设备树导出为 .dts 文件。我经常用它来对比「我写的」和「内核实际用的」之间有没有差异。有一次我发现某个 GPIO 控制器的中断号对不上,就是靠这招定位的。
3.3.2 反编译时保留更多信息
默认反编译会省略一些细节。如果你需要看到完整的二进制内容,可以用 -s 选项:
dtc -I dtb -O dts -s -o full.dts input.dtb
-s 会保留所有属性,包括那些值为空的属性。这在调试一些奇怪的兼容性问题时很有用。
3.3.3 检查 dtb 文件是否损坏
拿到一个 .dtb,先反编译一下。如果 dtc 报错,说明文件有问题。常见的错误有:
- FDT_ERR_BADMAGIC:文件头魔数不对,可能根本不是 dtb 文件。
- FDT_ERR_TRUNCATED:文件被截断了,可能是传输过程中损坏。
- FDT_ERR_BADVERSION:版本不匹配,老内核可能不认识新格式。
我曾经遇到过一个问题:U-Boot 加载 dtb 后系统卡死。反编译一看,发现 dtb 文件在烧录时被截断了 4 个字节。重新烧录后问题解决。
3.3.4 使用 -O asm 输出汇编
dtc 还能把设备树编译成汇编代码,方便你嵌入到固件中:
dtc -I dts -O asm -o myboard.S myboard.dts
生成的 .S 文件里,每个节点和属性都对应一段汇编数据。这在一些没有文件系统的裸机环境中很有用。
3.4 知识体系图
下面这张图总结了设备树编译与反编译的核心流程和工具链:
3.5 常见问题与避坑
最后分享几个我踩过的坑:
- 版本不匹配: 老内核(比如 Linux 3.x)可能不认识新版 dtc 生成的 dtb。编译时加上
-b 0指定版本号可以解决。 - 大小端问题: dtc 默认使用大端格式。如果你的平台是小端,编译时加
-O dtb -E -l指定小端输出。 - 插件编译: 如果你在写设备树插件(overlay),记得加
-@选项,否则内核加载时会报错。
嗯,dtc 这个工具看着简单,但用好了能省不少时间。下次你拿到一个陌生的 .dtb,别急着看代码,先反编译出来再说。