2. 设备树编译与反编译

设备树源文件(.dts)是给人阅读和编辑的文本格式,而内核引导时实际加载的是二进制设备树 Blob(.dtb)。DTC(Device Tree Compiler)是完成这一转换的核心工具。掌握 DTC 的编译、反编译及常用选项,是设备树开发的基本功。

2.1 DTC 工具概述

DTC 是设备树编译器的标准实现,通常随内核源码一起发布,路径为 scripts/dtc/dtc。在嵌入式开发环境中,也可通过包管理器单独安装:

# Ubuntu / Debian
sudo apt-get install device-tree-compiler

# CentOS / RHEL / Fedora
sudo yum install dtc

# 验证安装
dtc --version
# 输出示例:Version DTC 1.6.1
提示:建议使用与内核版本匹配的 DTC 版本。较新的内核特性(如 overlay 语法、#include 预处理)需要 DTC 1.4.7 以上版本支持。

2.2 编译 .dts 为 .dtb

最基本的编译命令将 .dts 源文件转换为 .dtb 二进制文件:

dtc -I dts -O dtb -o output.dtb input.dts

参数说明:

选项 含义 说明
-I dts 输入格式 指定源文件为文本格式 .dts
-O dtb 输出格式 指定目标文件为二进制 .dtb
-o 输出文件 指定输出的 .dtb 文件名
-@ 生成符号节点 为 overlay 编译时必须添加,生成 __symbols__ 节点

实际示例:

# 普通编译
dtc -I dts -O dtb -o myboard.dtb myboard.dts

# 为 overlay 编译(生成符号)
dtc -I dts -O dtb -@ -o myoverlay.dtbo myoverlay.dts
注意:overlay 文件通常使用 .dtbo 扩展名,但 DTC 本身不强制扩展名。使用 -@ 选项是 overlay 编译的关键,缺少该选项会导致 overlay 加载时无法解析符号。

2.3 反编译 .dtb 为 .dts

反编译用于从二进制 .dtb 恢复可读的 .dts 源文件,常用于调试、逆向分析或验证编译结果:

dtc -I dtb -O dts -o output.dts input.dtb

实际示例:

# 反编译内核使用的 dtb
dtc -I dtb -O dts -o decompiled.dts /boot/dtb/am335x-boneblack.dtb

# 反编译并保留 phandle 原始值
dtc -I dtb -O dts -f -o full.dts board.dtb

反编译时常用选项:

选项 作用 适用场景
-f 强制输出,忽略错误 处理损坏或不完整的 .dtb
-s 按地址排序节点 便于对比不同版本的 .dtb
-R 保留原始 phandle 值 需要精确还原引用关系时

2.4 常用编译选项详解

DTC 提供丰富的选项来控制编译行为,以下为最常用的选项:

选项 全称 功能描述
-@ symbols 生成 __symbols__ 节点,overlay 编译必需
-H phandle format 指定 phandle 格式:epapr(默认)、linux、both
-W warnings 启用特定警告,如 -W no-unit_address_vs_reg
-E errors 将警告升级为错误,如 -E unit_address_vs_reg
-b boot cpu 设置引导 CPU 编号,多核系统使用
-p padding 在 .dtb 末尾添加填充字节
-S size 指定 .dtb 最小尺寸,不足时填充
-v version 显示 DTC 版本信息

高级编译示例:

# 启用所有警告,并将地址/大小不匹配视为错误
dtc -I dts -O dtb -o safe.dtb -W all -E unit_address_vs_reg input.dts

# 指定 phandle 格式为 linux 兼容模式
dtc -I dts -O dtb -H linux -o linux_phandle.dtb input.dts

# 生成最小 4KB 的 dtb,末尾填充 256 字节
dtc -I dts -O dtb -S 4096 -p 256 -o padded.dtb input.dts

2.5 编译与反编译流程

DTC 编译与反编译流程 .dts 源文件 文本格式,可读可编辑 -I dts -O dtb .dtb 二进制文件 内核引导时加载 -I dtb -O dts .dts 反编译结果 用于调试/逆向分析 .dtsi 包含文件 头文件/公共定义 #include 预处理 .dtbo overlay 需 -@ 选项生成符号 -@ 编译 💡 关键点: • 编译方向:.dts → .dtb(生产);反编译方向:.dtb → .dts(调试) • Overlay 编译必须加 -@;反编译时 -f 可强制处理损坏文件

2.6 预处理与编译联合使用

实际项目中,.dts 文件通常通过 #include 引用 .dtsi 头文件。DTC 本身不处理 #include,需要先经过 C 预处理器(cpp)展开:

# 手动预处理 + 编译
cpp -nostdinc -I include -undef -x assembler-with-cpp \
    input.dts preprocessed.dts
dtc -I dts -O dtb -o output.dtb preprocessed.dts

# 一步完成(推荐)
dtc -I dts -O dtb -o output.dtb -i include input.dts

其中 -i 选项指定头文件搜索路径,可多次使用:

dtc -I dts -O dtb -o output.dtb \
    -i include \
    -i arch/arm/boot/dts \
    -i arch/arm/boot/dts/include \
    input.dts

2.7 编译检查与调试

DTC 提供丰富的警告和错误检查机制,帮助发现设备树中的问题:

检查类别 典型警告/错误 说明
地址/大小 unit_address_vs_reg 节点地址与 reg 属性不匹配
中断 interrupt_provider 中断控制器声明不一致
GPIO gpio_cells GPIO 描述符单元数错误
时钟 clock_provider 时钟提供者/消费者不匹配
语法 syntax 分号、括号、引号等语法错误

调试示例:

# 启用所有警告
dtc -I dts -O dtb -o test.dtb -W all test.dts

# 将特定警告升级为错误
dtc -I dts -O dtb -o test.dtb -E unit_address_vs_reg test.dts

# 只显示错误,不输出 dtb
dtc -I dts -O dtb -o /dev/null test.dts 2>&1 | grep ERROR
实战建议:在提交设备树代码前,使用 dtc -W all 进行完整检查。对于 overlay 文件,额外使用 dtc -@ -W all 确保符号生成正确。将警告数控制在 0 是设备树开发的最佳实践。

2.8 常用操作速查表

操作 命令 说明
编译 .dts → .dtb dtc -I dts -O dtb -o out.dtb in.dts 基本编译
编译 overlay dtc -I dts -O dtb -@ -o out.dtbo in.dts 必须加 -@
反编译 .dtb → .dts dtc -I dtb -O dts -o out.dts in.dtb 基本反编译
强制反编译 dtc -I dtb -O dts -f -o out.dts in.dtb 忽略错误
带预处理编译 dtc -I dts -O dtb -i include_dir -o out.dtb in.dts 自动处理 #include
完整检查 dtc -I dts -O dtb -W all -o out.dtb in.dts 启用所有警告
查看版本 dtc -v 确认工具版本