3、设备树编译器:DTC工具安装、编译与反编译、反汇编技巧
设备树写好了,怎么变成机器能懂的东西?
答案就是 DTC——Device Tree Compiler。说白了,它就是把 .dts 文本文件,编译成 .dtb 二进制文件的工具。反过来,也能把 .dtb 反编译回 .dts,方便调试。
我个人习惯,每次拿到一块新板子,第一件事就是反编译它的 .dtb,看看厂商到底配了啥。这比看文档快多了。
3.1 DTC 工具的安装
DTC 是设备树开发的基础工具。大部分 Linux 发行版都自带,但版本可能比较老。我建议自己编译最新版,因为新版本支持更多语法检查,能帮你提前发现坑。
3.1.1 从源码编译安装
这是最推荐的方式。你想想看,嵌入式开发环境千奇百怪,源码编译最可控。
# 下载 DTC 源码
git clone git://git.kernel.org/pub/scm/utils/dtc/dtc.git
cd dtc
# 编译
make
# 安装到系统路径(需要 root 权限)
sudo make install
安装完成后,验证一下:
dtc --version
# 输出类似:Version: DTC 1.7.0-g8c5a0220
3.1.2 通过包管理器安装
如果你只是临时用一下,或者不想折腾源码,包管理器也能装:
| 发行版 | 命令 |
|---|---|
| Ubuntu/Debian | sudo apt-get install device-tree-compiler |
| Fedora/RHEL | sudo dnf install dtc |
| Arch Linux | sudo pacman -S dtc |
/delete-node/)支持不好。我曾经因为这个排查了半天,最后发现是 DTC 版本太老,不认新语法。
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 -@ -H epapr myboard.dts
-@:生成符号表,方便后续 overlay 加载。这个在模块化设计中特别有用。-H epapr:指定 phandle 格式为 ePAPR 标准。大部分 ARM 平台都用这个。
-@ 选项,结果后面做设备树 overlay 时,内核死活加载不上。查了两天才发现是符号表没生成。嗯,从那以后,我写 Makefile 都会默认加上 -@。
3.3 反编译:从 .dtb 回到 .dts
反编译是调试利器。拿到一块新板子,或者想看看内核实际加载了啥,反编译一下全明白了。
dtc -I dtb -O dts -o output.dts input.dtb
举个例子,假设你有一个 kernel.dtb:
dtc -I dtb -O dts -o kernel.dts kernel.dtb
打开 kernel.dts,你就能看到完整的设备树结构。包括那些被 #include 展开后的内容,以及所有默认值。
3.4 反汇编:查看二进制布局
反汇编比反编译更底层。它直接展示 .dtb 文件的二进制结构,包括每个 token、每个字符串、每个数值的原始表示。
dtc -I dtb -O dts -o output.dts -H both input.dtb
或者用 fdtdump 工具(DTC 自带):
fdtdump input.dtb
输出会显示类似这样的内容:
// magic: 0xd00dfeed
// totalsize: 0x1a4 (420)
// off_dt_struct: 0x48
// off_dt_strings: 0x180
// off_mem_rsvmap: 0x28
// version: 17
// last_comp_version: 16
// boot_cpuid_phys: 0x0
// size_dt_strings: 0x24
// size_dt_struct: 0x138
这些字段的含义:
| 字段 | 含义 |
|---|---|
| magic | 魔数,固定为 0xd00dfeed,用于识别 .dtb 文件 |
| totalsize | 文件总大小(字节) |
| off_dt_struct | 结构体块偏移 |
| off_dt_strings | 字符串块偏移 |
| version | 设备树版本号 |
3.5 常用编译选项速查
这里整理了我常用的 DTC 选项,方便你快速查阅:
| 选项 | 说明 | 使用场景 |
|---|---|---|
-I |
指定输入格式(dts/dtb/fs) | 编译或反编译 |
-O |
指定输出格式(dts/dtb/asm) | 编译或反编译 |
-o |
指定输出文件 | 所有场景 |
-@ |
生成符号表(phandle 信息) | 需要 overlay 支持时 |
-H |
指定 phandle 格式(epapr/legacy/both) | 兼容不同内核版本 |
-W |
启用特定警告 | 代码检查 |
-E |
启用特定错误检查 | 严格模式编译 |
dtc -I dts -O dtb -o $@ -@ -W no-unit_address_vs_reg -W no-gpios_property $<这样既生成了符号表,又屏蔽了一些不太重要的警告,让编译输出更清爽。
3.6 知识体系总览
下面这张图,帮你理清 DTC 工具的核心逻辑:
这张图展示了 DTC 的完整工作流。你从左边写 .dts,经过 DTC 编译成 .dtb,内核直接加载。调试时,你可以从 .dtb 反编译回 .dts,或者反汇编查看二进制结构。
嗯,掌握了这些,设备树的编译和调试你就基本入门了。实际项目中,我建议你把 DTC 的常用命令写成 Makefile 目标,一键编译、一键反编译,能省不少事。