第3章 设备树编译器:DTC工具安装、编译与反编译

设备树写好了,怎么变成机器能懂的东西?

这就得靠DTC——Device Tree Compiler。说白了,它就是个翻译官,把咱们写的.dts文本文件,转成二进制.dtb,让内核能直接解析。反过来,也能把.dtb还原成.dts,方便调试。

我个人习惯把DTC比作「菜刀」——切菜、剁肉、拍蒜都靠它。你用熟了,后面所有设备树工作都会顺手很多。

3.1 DTC工具安装

不同平台安装方式不一样。我列个表,你对照自己的环境来。

操作系统 安装命令 备注
Ubuntu/Debian sudo apt install device-tree-compiler 最省事,推荐
CentOS/RHEL sudo yum install dtc EPEL源可能需要先配好
Arch Linux sudo pacman -S dtc 滚动更新,版本较新
macOS (Homebrew) brew install dtc 记得先装Xcode Command Line Tools
源码编译 make && sudo make install 从内核源码scripts/dtc/目录编译
我的建议:新手直接用apt或yum装。我刚开始学的时候非要自己编译源码,结果依赖没配好,折腾了一下午。后来发现apt装完就能用,白费功夫。

装完之后,验证一下:

dtc --version
# 输出类似:Version DTC 1.6.1

看到版本号,说明装好了。

3.2 编译:从.dts到.dtb

编译是DTC最常用的功能。命令格式很简单:

dtc -I dts -O dtb -o 输出.dtb 输入.dts

参数说明:

  • -I dts:输入格式是.dts文本
  • -O dtb:输出格式是.dtb二进制
  • -o 输出.dtb:指定输出文件名
  • 最后一个参数:输入文件名

举个例子。假设你写了个my-board.dts,想编译成my-board.dtb:

dtc -I dts -O dtb -o my-board.dtb my-board.dts

运行完,当前目录下就会多出my-board.dtb文件。

注意:我曾经犯过一个低级错误——输入文件路径写错了,DTC报错说找不到文件。我还以为是语法问题,查了半天。后来才发现是路径少了个斜杠。所以,先确认文件存在,再跑命令。

3.2.1 常用编译选项

实际项目中,光用基本命令不够。我整理了几个常用选项:

选项 作用 示例
-@ 生成插件节点(overlay)支持 dtc -@ -I dts -O dtb -o out.dtb in.dts
-H 指定phandle格式(epapr或linux) dtc -H epapr -I dts -O dtb ...
-W 启用特定警告 dtc -W no-unit_address_vs_reg ...
-E 启用特定错误检查 dtc -E no_property_name ...
-O 输出格式(dtb/dts/asm) -O dtb
-I 输入格式(dts/dtb/fs) -I dts

避坑指南:我曾经在一个项目里,编译出来的dtb加载后系统直接panic。后来发现是忘了加-@选项,导致overlay节点没被正确处理。如果你用设备树插件(overlay),-@必须加上。

3.2.2 编译过程中的常见错误

编译报错是家常便饭。我遇到过最多的几种:

  1. 语法错误:少了个分号、括号不匹配。DTC会提示行号,直接去改就行。
  2. 引用未定义节点:比如&uart0引用了不存在的节点。检查label定义。
  3. 属性值类型错误:比如reg属性写了字符串而不是数字。DTC会报Bad property value。
  4. 文件包含路径问题:#include找不到头文件。用-i选项指定搜索路径。

举个例子,一个典型的语法错误:

// 错误写法:少了分号
/ {
    compatible = "my-board"
    model = "test";
};

// 正确写法
/ {
    compatible = "my-board";
    model = "test";
};

DTC会报:ERROR: Input tree missing ';' at line 3。看到这个,直接去第3行加个分号就行。

3.3 反编译:从.dtb到.dts

反编译就是把二进制.dtb还原成文本.dts。这在调试时特别有用——比如你拿到一个别人编译好的.dtb,想看看里面到底配了什么。

命令格式:

dtc -I dtb -O dts -o 输出.dts 输入.dtb

举个例子:

dtc -I dtb -O dts -o my-board-reverse.dts my-board.dtb

运行完,my-board-reverse.dts就是反编译出来的文本。

小技巧:反编译出来的.dts可能和原始文件不完全一样。比如注释会丢失,节点顺序可能调整。但核心内容——节点、属性、值——是完整的。我经常用反编译来验证编译过程有没有「偷工减料」。

3.3.1 反编译的实用场景

  • 调试内核加载的dtb:从/sys/firmware/devicetree/base复制出来,反编译看看内核实际用了什么配置。
  • 逆向别人的设备树:拿到一个闭源板子的.dtb,反编译后学习它的硬件布局。
  • 验证编译结果:编译完再反编译,对比原始.dts,确保没有信息丢失。

我记得有一次,客户给了一个.dtb文件,说板子启动有问题。我反编译一看,发现GPIO的gpio-hog节点配错了——输出电平写反了。改完重新编译,问题解决。反编译在这类场景下就是救命稻草。

3.4 DTC工作流程总览

下面这张图,把DTC的编译、反编译、以及它在整个设备树开发流程中的位置,画清楚了。

DTC编译与反编译工作流程 DTC 编译与反编译工作流程 .dts 源文件 (文本格式,人类可读) DTC 编译 dtc -I dts -O dtb .dtb 二进制文件 (机器可读,内核加载) DTC 反编译 dtc -I dtb -O dts 应用场景 编译:将设备树源文件转为内核可加载的二进制格式 反编译:调试、逆向、验证编译结果 日常开发:编辑 .dts → 编译 → 测试 → 反编译验证 → 迭代

从这张图能看出来:

  • 正向流程:.dts → 编译 → .dtb → 内核加载
  • 反向流程:.dtb → 反编译 → .dts → 调试/验证
  • 迭代开发:编辑 → 编译 → 测试 → 反编译验证 → 再编辑

嗯,这个循环就是设备树开发的日常。你想想看,没有DTC,咱们连第一步都迈不出去。

3.5 实战:完整编译与反编译示例

光说不练假把式。咱们拿一个最简单的.dts文件,走一遍完整流程。

先创建一个test.dts:

/dts-v1/;

/ {
    compatible = "test-board";
    model = "Test Board v1.0";

    cpu@0 {
        compatible = "arm,cortex-a7";
        reg = <0x00>;
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x40000000>;
    };
};

然后编译:

dtc -I dts -O dtb -o test.dtb test.dts

没有报错的话,反编译看看:

dtc -I dtb -O dts -o test-reverse.dts test.dtb

打开test-reverse.dts,内容应该和原始文件基本一致,只是注释没了,节点顺序可能微调。核心数据——compatible、reg、model——都在。

验证技巧:用diff命令对比原始文件和反编译文件,看差异在哪里。

diff test.dts test-reverse.dts

如果只有注释和空行的差异,说明编译/反编译过程没问题。

3.6 本章小结

DTC是设备树开发的基础工具。装好它,学会编译和反编译,你就能在.dts和.dtb之间自由切换。后面章节里,我们会频繁用到这两个操作——写设备树、编译、加载、调试、反编译验证,循环往复。

我个人觉得,反编译这个功能被很多人低估了。它不只是「逆向」,更是「验证」和「学习」的利器。你拿到一个陌生的.dtb,反编译一看,里面的硬件布局、地址分配、中断配置,全都清清楚楚。比看文档还直观。