第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 设备树编译的完整流程

为了让你更直观地理解整个过程,我画了一张流程图:

设备树编译与反编译流程 DTS源文件 DTSI头文件 CPP预处理器 dtc编译器 DTB二进制 反编译DTS 编译:DTS → DTB(正向流程) 反编译:DTB → DTS(逆向流程)

从这张图你能看到,完整的编译流程其实分两步:

  1. 预处理:CPP把DTS和引用的DTSI头文件合并成一个完整的源文件
  2. 编译: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,看看别人是怎么写的。我当年就是这么学的,比看文档快多了。

好了,设备树的编译和反编译就聊到这儿。记住一句话:编译是常态,反编译是调试利器。两个都得会用。

可可精品资料库