3. 设备树编译器(DTC):从DTS到DTB的编译过程

设备树编译器,简称DTC,是Linux内核中一个不起眼但极其重要的工具。说白了,它的工作就是把我们人类能读懂的DTS文件,转换成机器能高效解析的DTB二进制文件。我在做嵌入式开发的头两年,几乎每天都在跟它打交道。

3.1 DTC工具的基本使用方法

DTC工具通常随内核源码一起发布,路径在 scripts/dtc/ 目录下。我个人习惯直接使用内核编译系统来调用它,但有时候也需要手动操作。

先看看最基本的用法:

# 编译DTS到DTB
dtc -I dts -O dtb -o output.dtb input.dts

# 反编译DTB到DTS
dtc -I dtb -O dts -o output.dts input.dtb

这里有几个关键参数:

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

小技巧:我建议你编译时加上 -@ 选项。这个选项会保留符号信息,方便后续调试。我在调试一个i.MX6的板子时,就因为没加这个选项,浪费了半天时间定位问题。

3.2 从DTS到DTB的编译过程

编译过程其实不复杂,但背后有几个关键步骤。我把它拆解成三个阶段:

  1. 预处理阶段:处理 #include/include/ 指令,展开宏定义
  2. 解析阶段:将DTS文本解析成抽象语法树
  3. 生成阶段:将语法树序列化成二进制DTB格式

来看一个实际例子。假设我们有这样一个简单的DTS文件:

/dts-v1/;

/ {
    compatible = "myboard,imx6ul";
    #address-cells = <1>;
    #size-cells = <1>;

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x20000000>;
    };

    chosen {
        bootargs = "console=ttymxc0,115200";
    };
};

编译命令很简单:

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

编译完成后,可以用 hexdumpfdtdump 查看生成的DTB文件结构。DTB文件头部有一个固定格式的header,包含了魔数、总大小、各区域偏移等信息。

核心要点:DTB文件采用大端字节序。这一点在跨平台开发时特别容易踩坑。我曾经在x86主机上编译DTB,然后放到ARM板子上用,结果因为字节序问题折腾了一整天。

3.3 反编译技巧

反编译DTB到DTS,是调试时最常用的操作。很多时候,我们拿到的只有DTB文件,或者想看看内核实际解析出来的设备树是什么样子。

基本反编译命令:

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

但这里有个坑——反编译出来的DTS可能和原始DTS不完全一样。为什么?因为编译过程中,一些注释、空行、格式信息会丢失。另外,属性值的表示方式也可能变化。

举个例子,原始DTS中可能有这样的写法:

reg = <0x80000000 0x20000000>;

反编译后可能变成:

reg = <0x80000000 0x20000000>;

看起来一样?嗯,但如果你原始文件里用了 #include 包含的头文件,反编译后这些头文件会被展开成具体的内容。我曾经遇到过这样的情况:反编译后得到一个巨大的DTS文件,完全找不到原来的结构层次。

注意:反编译时加上 -s 选项可以输出更简洁的格式,去掉一些冗余信息。但如果你需要保留所有细节,就别加这个选项。

3.4 高级用法与调试技巧

除了基本的编译和反编译,DTC还有一些高级功能。我个人最常用的是 -H 选项,它可以指定输出格式的版本:

# 生成旧版本格式的DTB
dtc -I dts -O dtb -H epapr -o old.dtb input.dts

另外,-V 选项可以查看DTC的版本信息。不同版本的DTC对语法的检查严格程度不同。我记得有一次,一个同事的DTS文件在旧版本DTC上编译通过,但换到新版本就报错了。原因是新版本对属性值的类型检查更严格。

还有一个很实用的功能是 -W-E 选项,用于控制警告和错误:

# 关闭所有警告
dtc -W no-* -I dts -O dtb -o output.dtb input.dts

# 将警告视为错误
dtc -E -I dts -O dtb -o output.dtb input.dts

3.5 知识体系图

下面这张图展示了DTC工具的核心工作流程和关键知识点:

DTC工具知识体系 DTS源文件 DTC编译器 预处理→解析→生成 DTB二进制 反编译 关键参数:-I (输入格式) | -O (输出格式) | -o (输出文件) | -@ (保留符号) | -H (版本) 编译操作 dtc -I dts -O dtb -o output.dtb input.dts 反编译操作 dtc -I dtb -O dts -o output.dts input.dtb 调试工具 fdtdump / hexdump -W / -E 控制警告 注意事项 大端字节序 | 注释丢失 | 头文件展开 | 版本兼容性 反编译结果 ≠ 原始DTS

3.6 实战经验总结

最后,分享几个我在项目中积累的经验:

  • 版本匹配很重要:内核版本和DTC版本要匹配。不同版本对设备树语法的支持程度不同。
  • 善用make dtbs:在内核源码根目录下执行 make dtbs,会自动编译所有设备树文件。
  • 检查DTB有效性:用 dtc -I dtb -O dts /dev/null 可以快速检查DTB文件是否损坏。
  • 保留原始DTS:反编译只是辅助手段,不要完全依赖它来还原原始文件。

嗯,说到检查DTB有效性,我想起一个案例。有次客户反馈板子启动到一半就卡住了。我远程一看,发现是DTB文件在传输过程中被截断了。用fdtdump一查,文件大小不对,头部校验失败。从那以后,我每次烧录前都会先校验DTB的完整性。

DTC工具虽然简单,但用好它能让你的设备树开发效率提升不少。记住,工具是死的,经验是活的。多动手、多踩坑,自然就熟练了。


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