3. 设备树编译与反编译
设备树源文件(.dts/.dtsi)本身只是文本描述,内核启动时真正读取的是二进制设备树块(DTB)。因此,从源码到可被内核解析的 DTB,中间必须经过 dtc(Device Tree Compiler) 的编译过程。理解这条链路,是设备树调试与故障定位的基本功:只有知道 DTB 是怎么生成的,才能在“内核没识别到设备”时判断问题出在源码、编译参数还是反编译验证环节。
一、核心原理:dtc 与 DTB 生成流程
dtc 是设备树社区维护的编译/反编译工具,源码位于内核 scripts/dtc/ 目录,也可独立安装。它把人类可读的 DTS 文本编译为扁平的二进制结构,内核在启动早期解析该结构并展开为 device_node 树。整个生成流程可概括为:
- 预处理:C 预处理器处理
#include、宏、条件编译,把 .dtsi 头文件展开进 .dts。 - 语法解析:dtc 解析节点、属性、标签、引用(
&label),构建内部树。 - 检查与合并:处理
/delete-node/、/delete-property/、覆盖(overlay)等操作。 - 输出 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 length | reg 的地址/长度单元数与父节点 #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 关键节点”作为固定验证步骤,能显著缩短定位时间。
掌握 dtc 的编译与反编译,本质上是掌握“源码 → 二进制 → 最终态”的双向可观测能力。遇到设备树相关故障时,先确认 DTB 是否为最新、再反编译核对关键属性,往往比盲目修改源码更高效。