第3章:设备树编译与反编译
设备树文件写好了,怎么变成机器能懂的东西?
说白了,DTS(设备树源文件)是给人看的,DTB(设备树二进制文件)是给内核和硬件用的。中间这个转换过程,就是我们今天要聊的核心——编译与反编译。
3.1 DTS到DTB的编译过程
我在做第一个STM32MP1项目时,就犯过一个低级错误。当时手写了一个DTS文件,直接扔给内核编译,结果死活起不来。后来才发现,我连最基本的编译命令都没跑对。
DTS到DTB的编译,靠的是dtc工具。这个工具全称叫Device Tree Compiler,是设备树生态里的核心工具。
基本命令长这样:
dtc -I dts -O dtb -o output.dtb input.dts
参数解释一下:
-I dts:指定输入格式是DTS-O dtb:指定输出格式是DTB-o output.dtb:输出文件名input.dts:源文件
嗯,就这么简单。但实际项目中,我们很少直接这么用。
实际项目中的编译方式
在STM32MP1的SDK里,设备树编译通常集成在Makefile中。你只需要执行:
make dtbs
系统会自动找到所有DTS文件并编译成DTB。我个人习惯在修改设备树后,单独编译目标文件:
make stm32mp157d-atk.dtb
3.2 DTB到DTS的反编译
反编译,就是把二进制DTB还原成可读的DTS。这招在调试时特别有用。
我记得有一次,客户给了一个DTB文件,说他们的板子跑不起来。我第一件事就是反编译,看看里面到底配了啥。
反编译命令:
dtc -I dtb -O dts -o output.dts input.dtb
参数和编译基本对称:
-I dtb:输入是DTB-O dts:输出是DTS
小技巧
反编译时加个 -@ 参数,可以保留overlay信息。我在调试动态设备树时经常用这招。
dtc -I dtb -O dts -@ -o output.dts input.dtb
3.3 dtc工具的高级用法
dtc不只是简单的编译反编译。它还有很多实用功能,我挑几个常用的说说。
3.3.1 检查语法错误
你想想看,写了几百行的DTS,万一有个括号没配对怎么办?dtc提供了语法检查功能:
dtc -I dts -O dtb -o /dev/null input.dts 2>&1
这个命令会把编译结果扔到黑洞,只保留错误信息。我曾经用这招快速定位过一个逗号缺失的问题,省了半小时的排查时间。
3.3.2 输出依赖关系
大型项目里,一个DTS可能包含多个头文件。用 -H 参数可以查看依赖:
dtc -I dts -O dtb -H -o output.dtb input.dts
3.3.3 生成汇编代码
这个功能我很少用,但确实存在。用 -O asm 可以生成汇编版本的设备树:
dtc -I dts -O asm -o output.S input.dts
3.4 编译过程中的常见错误
我整理了一个表格,把最常见的编译错误列出来:
| 错误信息 | 原因 | 解决方法 |
|---|---|---|
| syntax error | 语法错误,比如少了个分号 | 检查DTS文件,确保每个属性以分号结尾 |
| label not found | 引用了未定义的标签 | 检查 &label 是否正确定义 |
| phandle is not unique | 多个节点使用了相同的phandle | 确保每个节点的phandle唯一 |
| unit name mismatch | 节点名和reg属性不匹配 | 检查节点名中的地址是否和reg一致 |
避坑指南
我曾经在编译时遇到一个诡异的问题:DTS文件明明没错,但编译出来的DTB就是不对。折腾了半天才发现,是文件编码问题。DTS文件必须用UTF-8编码,不能用带BOM的格式。嗯,这个坑我踩过,你们就别再踩了。
3.5 设备树编译的完整流程
为了让你更直观地理解整个过程,我画了一张流程图:
从这张图你能看到,完整的编译流程其实分两步:
- 预处理:CPP把DTS和引用的DTSI头文件合并成一个完整的源文件
- 编译:dtc把合并后的源文件转换成DTB二进制
反编译就简单多了,一步到位,直接从DTB还原成DTS。
3.6 实战:手动编译一个设备树
光说不练假把式。我们来个实际例子。
假设你有一个简单的DTS文件 test.dts:
/dts-v1/;
/ {
compatible = "st,stm32mp157";
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
reg = <0x0>;
};
memory@c0000000 {
device_type = "memory";
reg = <0xc0000000 0x20000000>;
};
};
编译它:
dtc -I dts -O dtb -o test.dtb test.dts
反编译回来看看:
dtc -I dtb -O dts -o test_back.dts test.dtb
对比一下 test.dts 和 test_back.dts,你会发现内容基本一致,但反编译后的文件会多出一些自动生成的属性,比如 #address-cells 和 #size-cells。这是因为dtc在编译时会自动补全这些信息。
我的建议
刚开始学设备树时,多试试反编译。拿一个成熟的DTB文件反编译成DTS,看看别人是怎么写的。我当年就是这么学的,比看文档快多了。
好了,设备树的编译和反编译就聊到这儿。记住一句话:编译是常态,反编译是调试利器。两个都得会用。