3. 设备树编译与反编译:DTC工具使用、dtb与dts互转、反编译调试技巧

设备树源文件(.dts)是文本格式的硬件描述,但内核引导时加载的是二进制设备树块(.dtb)。DTC(Device Tree Compiler)是两者之间转换的核心工具。掌握编译与反编译操作,是调试设备树配置、验证语法正确性、逆向分析现有系统硬件布局的必备技能。

3.1 DTC工具概述与安装

DTC 是设备树编译器的标准实现,通常随内核源码一起发布,也可独立安装。

获取方式 命令/路径 说明
内核源码 scripts/dtc/dtc 编译内核后自动生成,版本与内核匹配
独立安装(Ubuntu) sudo apt install device-tree-compiler 系统级安装,方便快捷
独立安装(Fedora) sudo dnf install dtc Red Hat 系发行版
从源码编译 make(dtc 目录下) 可获取最新特性与修复
调试建议: 在开发板上调试时,优先使用内核源码自带的 DTC 版本,避免因版本差异导致编译警告或错误。

3.2 dts 编译为 dtb:基础流程

将文本格式的 .dts 转换为二进制 .dtb,是设备树开发中最频繁的操作。

3.2.1 基本编译命令

# 基本编译
dtc -I dts -O dtb -o output.dtb input.dts

# 常用参数说明
# -I dts   : 指定输入格式为 dts
# -O dtb   : 指定输出格式为 dtb
# -o       : 指定输出文件名
# -@       : 生成 __symbols__ 节点,支持插件覆盖
# -H epapr : 生成 ePAPR 标准 dtb(默认)

3.2.2 编译过程中的常见警告与处理

警告信息 含义 处理方式
Warning (unit_address_vs_reg) 节点名中的地址与 reg 属性不匹配 检查节点名格式,确保与 reg 首地址一致
Warning (simple_bus_reg) simple-bus 子节点缺少 reg 或 ranges 为子节点添加正确的 reg 属性
Warning (avoid_unnecessary_addr_size) 不必要的 #address-cells / #size-cells 移除无总线子节点的地址单元声明
Error (syntax) 语法错误,如缺少分号、括号不匹配 检查行号附近,修正语法
生产环境建议: 在 Makefile 或构建脚本中添加 -Wno-unit_address_vs_reg 等选项,屏蔽已知无害的警告,保持编译输出干净。

3.3 dtb 反编译为 dts:逆向分析与调试

反编译是将二进制 .dtb 还原为可读的 .dts 文本,常用于分析现有系统、验证编译结果、提取硬件配置。

3.3.1 基本反编译命令

# 基本反编译
dtc -I dtb -O dts -o output.dts input.dtb

# 保留所有信息(包括 phandle 和 labels)
dtc -I dtb -O dts -o output.dts -@ input.dtb

# 输出到标准输出(快速查看)
dtc -I dtb -O dts input.dtb | less

3.3.2 反编译输出结构解析

反编译后的 .dts 文件会包含原始 .dtsi 展开后的所有内容,以及编译器自动生成的 phandle 和 linux,phandle 属性。

/dts-v1/;

/ {
    #address-cells = <2>;
    #size-cells = <2>;

    soc {
        #address-cells = <2>;
        #size-cells = <2>;
        compatible = "simple-bus";
        ranges;

        uart0: serial@ff000000 {
            compatible = "arm,pl011", "arm,primecell";
            reg = <0x0 0xff000000 0x0 0x1000>;
            interrupts = <0 99 4>;
            clocks = <&uart_clk>;
            status = "okay";
        };
    };

    uart_clk: clock@100 {
        compatible = "fixed-clock";
        #clock-cells = <0>;
        clock-frequency = <24000000>;
    };
};
注意: 反编译输出的 phandle 值是编译器自动分配的,与原始 .dts 中的 &label 引用方式不同。反编译结果主要用于调试分析,不建议直接修改后重新编译。

3.4 设备树编译与反编译流程

设备树编译与反编译流程 .dts / .dtsi 文本格式源文件 编译 DTC 编译器 dtc -I dts -O dtb 输出 .dtb 二进制设备树块 反编译 反编译 .dts dtc -I dtb -O dts 调试用途 • 验证语法正确性 • 分析现有系统硬件 对比验证 引导加载 Linux 内核 解析 device_node

3.5 高级反编译调试技巧

3.5.1 保留符号信息

使用 -@ 参数反编译时,会保留 __symbols__ 节点,便于追踪原始标签引用关系。

dtc -I dtb -O dts -@ -o debug.dts board.dtb

# 输出中会包含类似以下内容
__symbols__ {
    uart0 = "/soc/serial@ff000000";
    uart_clk = "/clock@100";
};

3.5.2 仅输出特定节点

通过 -s 参数可以只输出节点结构,不输出属性值,快速查看拓扑。

dtc -I dtb -O dts -s -o skeleton.dts board.dtb

3.5.3 比较两个 dtb 的差异

反编译后使用 diff 工具对比,快速定位配置变更。

# 反编译两个版本
dtc -I dtb -O dts -o v1.dts v1.dtb
dtc -I dtb -O dts -o v2.dts v2.dtb

# 对比差异
diff -u v1.dts v2.dts

3.5.4 从运行系统中提取 dtb

在目标板上运行时,可以从 /sys/firmware/devicetree 或 /proc/device-tree 提取当前使用的设备树。

# 方法一:从 /sys 导出
dtc -I fs -O dtb -o extracted.dtb /sys/firmware/devicetree/base

# 方法二:从 /proc 导出(某些内核版本)
cat /proc/device-tree > extracted.dtb

# 然后反编译查看
dtc -I dtb -O dts -o extracted.dts extracted.dtb
实用技巧: 在开发板上执行 dtc -I fs -O dts /sys/firmware/devicetree/base 可以直接输出当前内核使用的完整设备树文本,无需中间 dtb 文件。

3.6 常见编译错误与排查

错误类型 典型信息 排查方向
语法错误 syntax error before '...' 检查分号、花括号、引号是否匹配
引用错误 Reference to non-existent node or label 确认 &label 对应的节点已定义
属性值错误 Bad cell value 检查 reg、interrupts 等数组长度
包含文件错误 Couldn't open "xxx.dtsi" 确认 #include 路径正确,使用 -i 指定搜索路径
版本不兼容 FATAL: Input has errors 检查 /dts-v1/; 版本声明,更新 DTC 版本

3.7 编译与反编译的调试工作流

设备树调试工作流 1. 编写 .dts 编辑硬件描述 2. 编译为 dtb dtc -I dts -O dtb 3. 加载到内核 uboot / 直接加载 4. 验证 功能测试 失败 5. 反编译分析 dtc -I dtb -O dts 定位问题后修改 成功 6. 调试完成 提交最终 dtb

3.8 实战案例:反编译分析现有系统

假设你拿到一块开发板,需要分析其 I2C 控制器配置。

# 步骤1:从运行系统提取 dtb
dtc -I fs -O dtb -o current.dtb /sys/firmware/devicetree/base

# 步骤2:反编译为可读文本
dtc -I dtb -O dts -o current.dts current.dtb

# 步骤3:搜索 I2C 相关节点
grep -n "i2c\|I2C" current.dts

# 步骤4:提取特定节点内容
sed -n '/i2c@/,/^[a-z]/p' current.dts | head -50
调试要点: 反编译后的 current.dts 中,所有 phandle 值都是数字形式。要理解引用关系,可以查找 phandle = <数字> 的定义位置,或者使用 -@ 参数保留符号表。

通过本节的学习,你已经掌握了 DTC 工具的核心用法,能够熟练进行 .dts 与 .dtb 之间的双向转换,并利用反编译技术进行设备树调试与逆向分析。这些技能将在后续的 platform 驱动开发中发挥关键作用。