4、设备树编译器:dtc 工具的使用与反编译技巧

设备树写好了,怎么变成内核能用的二进制文件?

答案就是 dtc——设备树编译器。

说实话,我刚接触设备树那会儿,以为这玩意儿多神秘。后来发现,它就是个翻译官,把 .dts 文本翻译成 .dtb 二进制。反过来也能干,把 .dtb 还原成 .dts。今天我就带你把它玩透。

4.1 dtc 是什么?

dtc 全称 Device Tree Compiler。它属于设备树工具链的核心。

它的工作很简单:

  • 编译:.dts → .dtb(文本转二进制)
  • 反编译:.dtb → .dts(二进制转文本)
  • 转储:.dtb → .dts 带偏移信息

我在项目中遇到过一位同事,死活找不到设备树里的某个节点。我让他反编译一下 .dtb,他愣了半天:「还能反编译?」嗯,从那以后他再也不敢只发 .dtb 给别人了。

4.2 基本编译命令

先看最简单的用法:

dtc -I dts -O dtb -o output.dtb input.dts

参数说明:

  • -I dts:输入格式是 .dts
  • -O dtb:输出格式是 .dtb
  • -o output.dtb:指定输出文件名

我习惯把常用参数记成「I-O-O」:输入、输出、输出文件。好记。

如果你只想检查语法,不生成文件:

dtc -I dts -O dtb input.dts > /dev/null

没报错就说明语法没问题。我经常用这招快速验证。

4.3 反编译:把 .dtb 变回 .dts

反编译才是真正的宝藏技能。

你想想看,有时候拿到一块开发板,厂商只给了 .dtb。你想看看里面到底配了哪些外设?反编译一下全出来了。

dtc -I dtb -O dts -o output.dts input.dtb

就这么简单。

但有个坑——反编译出来的 .dts 会丢失注释。为什么?因为 .dtb 是二进制格式,注释在编译时就被扔掉了。所以,永远不要只靠反编译来维护设备树。源文件 .dts 才是真理。

⚠️ 我曾经踩过的坑: 有一次我反编译了 .dtb,修改后再编译回去。结果某个节点死活不生效。查了半天才发现,反编译时丢失了 #include 的头文件信息。从那以后,我再也不拿反编译结果当源文件用了。

4.4 常用选项详解

dtc 的选项不少,但常用的就这几个:

选项 说明 我常用的场景
-I 指定输入格式 dts、dtb、fs(文件系统)
-O 指定输出格式 dts、dtb、asm(汇编)
-o 输出文件名 必填,除非你只想检查语法
-@ 生成插件节点 做设备树叠加层时用
-H 指定 phandle 格式 兼容旧内核时用
-V 指定 .dtb 版本 默认用最新版,老内核需要降级

举个例子,生成带插件支持的 .dtb:

dtc -I dts -O dtb -@ -o output.dtb input.dts

这个 -@ 选项,我在做设备树叠加层(overlay)时几乎必用。不加的话,编译出来的 .dtb 没法动态加载。

4.5 反编译的高级技巧

反编译不只是 -I dtb -O dts 这么简单。有几个实用技巧:

  • 保留偏移信息:加 -f 选项,会输出每个节点的偏移地址。调试时很有用。
  • 反编译成汇编-O asm 可以生成汇编代码。我很少用,但做底层分析时能派上用场。
  • 只反编译部分节点:用 -s 选项可以指定起始节点。不过说实话,我更喜欢全量反编译然后用 grep 过滤。
# 带偏移信息的反编译
dtc -I dtb -O dts -f -o debug.dts input.dtb

# 反编译成汇编
dtc -I dtb -O asm -o output.S input.dtb

4.6 常见错误与避坑

用 dtc 时,我遇到过不少报错。挑几个典型的说说:

错误1: "FATAL: Input has errors, output not generated"

说白了就是语法有错。检查括号是否匹配、分号是否漏了、引号是否成对。

错误2: "WARNING: node xxx has a phandle but no reg property"

节点有 phandle 但没有 reg 属性。这通常是个警告,但最好补上 reg

错误3: "ERROR: label or path 'xxx' not found"

引用了不存在的节点或标签。检查 &xxx 是否拼写正确。

我个人习惯在编译时加 -Wno-error 选项,把警告降级。但注意,这只是临时方案,最终还是要消除所有警告。

4.7 实战:从 .dtb 反编译到调试

给你一个完整的实战流程:

  1. 拿到厂商的 .dtb 文件
  2. 反编译成 .dts:dtc -I dtb -O dts -o board.dts board.dtb
  3. 用文本编辑器打开 board.dts,查看节点配置
  4. 发现问题后,修改 board.dts
  5. 重新编译:dtc -I dts -O dtb -o board_new.dtb board.dts
  6. 用新的 .dtb 替换旧的,重启测试

嗯,这里要注意:第4步修改时,一定要保留原有的结构。我曾经手滑删了一个 status = "okay",结果某个外设死活不工作。查了半天才发现是这个问题。

4.8 知识体系图

下面这张图总结了 dtc 的核心用法:

dtc 工具核心用法 dtc 编译器 编译:.dts → .dtb -I dts -O dtb -o output.dtb 反编译:.dtb → .dts -I dtb -O dts -o output.dts 常用选项 -@ 生成插件 | -f 保留偏移 | -V 指定版本 | -H 指定 phandle 格式
💡 我的小技巧: 在 .bashrc 里加个别名:alias dtc-dts='dtc -I dts -O dtb'alias dtc-dtb='dtc -I dtb -O dts'。省得每次敲那么长一串。

好了,dtc 的基本用法就这些。说白了就是两个方向:编译和反编译。记住源文件 .dts 才是根本,.dtb 只是产物。下次拿到 .dtb 别慌,反编译一下,什么都清楚了。


公众号:蓝海资料掘金营,微信deep3321