3、设备树编译与反编译:dtc工具使用、编译为dtb、反编译为dts、常见编译错误
设备树写好了,怎么变成机器能读懂的二进制?反过来,拿到一个别人的dtb,怎么看看里面到底配了啥?
这就得靠 dtc 这个工具了。说实话,我刚开始接触AOSP时,觉得这玩意儿就是个黑盒。后来踩了几次坑,才摸清楚它的脾气。
3.1 dtc工具:设备树的“翻译官”
dtc全称是Device Tree Compiler。它负责把人类可读的.dts文件,编译成二进制.dtb文件。反过来也能做。
在AOSP源码里,dtc的位置一般在:
prebuilts/misc/linux-x86/dtc/dtc
我个人习惯把它加到环境变量里,省得每次敲路径。你想想看,一天编译几十次,少敲几个字符也是好的。
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一样。但有个坑——注释全没了。
我曾经调试一个第三方提供的dtb,反编译后发现一堆phandle和labels,看得我头大。后来我学乖了,反编译时加上-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的结构打印出来。虽然不如反编译详细,但胜在速度快。我调试时经常用它来确认某个节点有没有被编译进去。
fdtdump看一眼关键节点。确认没问题了,再刷到板子上。这样能省下不少重启时间。
好了,dtc的基本用法就这些。说白了,编译和反编译就是来回倒腾。但真正考验人的,是那些莫名其妙的编译错误。记住我上面说的几个坑,能帮你少走不少弯路。