3、设备树编译与反编译:dtc工具使用、编译为dtb、反编译为dts、常见编译错误

设备树写好了,怎么变成机器能读懂的二进制?反过来,拿到一个别人的dtb,怎么看看里面到底配了啥?

这就得靠 dtc 这个工具了。说实话,我刚开始接触AOSP时,觉得这玩意儿就是个黑盒。后来踩了几次坑,才摸清楚它的脾气。

3.1 dtc工具:设备树的“翻译官”

dtc全称是Device Tree Compiler。它负责把人类可读的.dts文件,编译成二进制.dtb文件。反过来也能做。

在AOSP源码里,dtc的位置一般在:

prebuilts/misc/linux-x86/dtc/dtc

我个人习惯把它加到环境变量里,省得每次敲路径。你想想看,一天编译几十次,少敲几个字符也是好的。

小提示: 如果你用的是AOSP的完整编译环境,make的时候会自动调用dtc。但单独调试时,还是得手动敲命令。

3.2 编译为dtb:从文本到二进制

.dts编译成.dtb,命令很简单:

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

参数说明:

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

我在项目中遇到过一个问题:编译出来的dtb比预期大很多。后来发现是.dts里引用了很多没用的.dtsi文件。嗯,这里要注意,#include不要乱加,加多了编译出来的dtb会膨胀。

还有一个常用选项是-@,用来生成插件(overlay)支持:

dtc -@ -I dts -O dtb -o your_device.dtb your_device.dts

这个-@参数,说白了就是给dtb加个“元数据”,让内核能动态加载设备树片段。我调试外设时经常用这个。

3.3 反编译为dts:从二进制回到文本

拿到一个别人的dtb,或者从boot分区里提取出来的dtb,怎么看看里面配了什么?反编译呗。

dtc -I dtb -O dts -o decompiled.dts your_device.dtb

反编译出来的dts,结构基本和原始dts一样。但有个坑——注释全没了

注意: 反编译出来的dts,所有注释都会被丢掉。因为注释在编译时就被dtc忽略了。所以,如果你指望反编译后看到别人写的注释,那是不可能的。

我曾经调试一个第三方提供的dtb,反编译后发现一堆phandlelabels,看得我头大。后来我学乖了,反编译时加上-f参数强制输出,再用-s参数排序节点,至少能看清结构:

dtc -I dtb -O dts -f -s -o sorted.dts your_device.dtb

3.4 常见编译错误与避坑指南

dtc编译时,错误信息有时候挺“抽象”的。我整理了几个最常见的,你遇到了别慌。

错误信息 原因 解决方法
Error: ... syntax error dts文件语法错误,比如少了个分号 检查对应行,看看是不是漏了;{}不匹配
Warning: unit address mismatch 节点名和reg属性里的地址不匹配 比如节点名叫uart@1000,但reg = <0x2000>,这就不对
FATAL ERROR: Unable to parse input tree 输入文件格式不对,或者路径错了 检查-I参数是否正确,文件是否存在
Error: phandle references ... out of range <&xxx>引用的节点不存在 检查xxx这个label是否定义过

避坑指南: 我曾经因为一个syntax error折腾了半小时。最后发现是dtsi文件里多了一个}。dtc报错的行号,有时候是展开#include之后的行号,不是原始文件的行号。所以,建议先用cpp预处理一下,看看展开后的文件长什么样:

cpp -P -x assembler-with-cpp your_device.dts your_device_preprocessed.dts

然后再编译预处理后的文件,错误定位会准很多。

还有一个常见问题:dtc版本不一致。AOSP里自带的dtc版本可能比较老,而内核社区用的dtc版本比较新。新版本dtc对语法检查更严格。我遇到过在AOSP里编译通过的dts,拿到新版本dtc下一编译就报错。解决办法是统一dtc版本,或者用-Wno-xxx参数关掉某些警告。

# 忽略某些警告
dtc -Wno-unit_address_vs_reg -I dts -O dtb -o output.dtb input.dts

3.5 调试技巧:用fdtdump快速查看dtb

有时候你不想反编译整个dtb,只想快速看看里面有哪些节点。可以用fdtdump工具:

fdtdump your_device.dtb | less

这个命令会以人类可读的格式,把dtb的结构打印出来。虽然不如反编译详细,但胜在速度快。我调试时经常用它来确认某个节点有没有被编译进去。

我的习惯: 每次修改dts后,先编译成dtb,再用fdtdump看一眼关键节点。确认没问题了,再刷到板子上。这样能省下不少重启时间。

好了,dtc的基本用法就这些。说白了,编译和反编译就是来回倒腾。但真正考验人的,是那些莫名其妙的编译错误。记住我上面说的几个坑,能帮你少走不少弯路。