3. 设备树编译与反编译

设备树源文件(.dts/.dtsi)本身只是文本描述,内核启动时真正读取的是二进制设备树块(DTB)。因此,从源码到可被内核解析的 DTB,中间必须经过 dtc(Device Tree Compiler) 的编译过程。理解这条链路,是设备树调试与故障定位的基本功:只有知道 DTB 是怎么生成的,才能在“内核没识别到设备”时判断问题出在源码、编译参数还是反编译验证环节。

一、核心原理:dtc 与 DTB 生成流程

dtc 是设备树社区维护的编译/反编译工具,源码位于内核 scripts/dtc/ 目录,也可独立安装。它把人类可读的 DTS 文本编译为扁平的二进制结构,内核在启动早期解析该结构并展开为 device_node 树。整个生成流程可概括为:

  1. 预处理:C 预处理器处理 #include、宏、条件编译,把 .dtsi 头文件展开进 .dts。
  2. 语法解析:dtc 解析节点、属性、标签、引用(&label),构建内部树。
  3. 检查与合并:处理 /delete-node/、/delete-property/、覆盖(overlay)等操作。
  4. 输出 DTB:按 FDT 规范序列化为二进制,包含结构块、字符串块和内存保留块。

典型编译命令如下,其中 -I dts -O dtb 指定输入输出格式,-o 指定输出文件:

# 直接编译单个 dts
dtc -I dts -O dtb -o board.dtb board.dts

# 内核构建系统中通常由 Makefile 调用
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

# 带符号信息,便于后续反编译定位
dtc -I dts -O dtb -@ -o board.dtb board.dts
编译时加上 -@ 会生成 __symbols__ 节点,保留标签与路径映射,对 overlay 和反编译调试非常有用。

二、反编译验证:把 DTB 还原成可读文本

编译成功不等于内容正确。设备树调试中最有效的手段之一是反编译验证:把生成的 DTB 再还原为 DTS,检查节点、属性、地址是否与预期一致。这能发现“源码写了但被覆盖”“地址写错”“节点被 delete 掉”等隐蔽问题。

# 反编译 DTB 为 DTS
dtc -I dtb -O dts -o board_check.dts board.dtb

# 查看反编译结果中的关键节点
grep -n "ethernet\|reg =\|compatible" board_check.dts

反编译结果会显示最终生效的树结构,而不是源码的原始文本。若发现某个属性缺失,应回到源码检查是否被 /delete-property/ 删除,或被后包含的 .dtsi 覆盖。对于多级包含的板级文件,反编译是确认“最终态”的唯一可靠方式。

三、常见编译错误与修复方法

dtc 报错通常分为语法类、引用类和语义类。下表汇总了高频错误、原因与修复方式:

错误信息常见原因修复方法
syntax error, expected ...缺少分号、括号不匹配、属性值格式错误检查报错行附近,属性值字符串需加引号,单元地址需用 <>
Label or path not found引用了未定义的标签,或 .dtsi 未包含确认 #include 路径正确,标签拼写一致
Duplicate node name同一层级出现同名节点合并节点或使用不同 unit-address 区分
reg property has invalid lengthreg 的地址/长度单元数与父节点 #address-cells 不匹配核对父节点 cells 设置,调整 reg 元组数量
Warning: unit_address_vs_reg节点名带 unit-address 但无 reg 属性补充 reg 或去掉节点名中的地址部分
很多“编译通过但运行异常”的问题,根源是 #address-cells/#size-cells 与子节点 reg 不匹配。dtc 可能只给 warning,但内核解析出的地址会错位,导致驱动 probe 失败。

四、案例与复盘:一次网口不识别问题的定位

某 ARM 板卡编译 DTB 后,内核日志显示以太网控制器未注册。排查过程如下:

  • 先确认 DTB 已更新:ls -l /boot/*.dtb 与编译时间对比。
  • 反编译 DTB,搜索 ethernet 节点,发现 status 为 "disabled"。
  • 回到源码,发现板级 .dts 中该节点被后包含的 .dtsi 覆盖为 disabled。
  • 在板级文件中显式设置 status = "okay"; 并重新编译,问题解决。

复盘要点:设备树是“后定义覆盖先定义”的合并模型,源码顺序和包含顺序直接影响最终结果。调试时不要只看自己写的那个文件,而要看反编译后的最终 DTB。建议把“编译 → 反编译 → diff 关键节点”作为固定验证步骤,能显著缩短定位时间。

设备树编译与反编译验证链路 .dts 板级源码 board.dts .dtsi 头文件 soc.dtsi / board.dtsi C 预处理器 #include / 宏展开 dtc 编译 -I dts -O dtb board.dtb 二进制 FDT dtc 反编译 -I dtb -O dts check.dts 最终生效树 对比关键节点:compatible / reg / status 一致 → 编译正确;不一致 → 回查覆盖与 cells 配置 常见错误 语法 / 引用 / cells 不匹配 源码 预处理 编译 DTB 反编译验证

掌握 dtc 的编译与反编译,本质上是掌握“源码 → 二进制 → 最终态”的双向可观测能力。遇到设备树相关故障时,先确认 DTB 是否为最新、再反编译核对关键属性,往往比盲目修改源码更高效。