4. 编译Overlay

设备树Overlay源文件(.dts)必须经过编译生成二进制格式的.dtbo文件,才能被内核的Overlay机制动态加载。本章详细讲解使用dtc(Device Tree Compiler)进行编译的标准流程、关键选项以及必须避开的常见陷阱。

4.1 标准编译命令与输出

编译Overlay与编译普通设备树(.dtb)在命令上存在关键区别:必须显式指定-@选项以保留符号(symbols)和叠加目标(target)信息。基本命令格式如下:

dtc -@ -I dts -O dtb -o <output.dtbo> <input.dts>

其中各选项含义:

选项 全称 说明
-@ --symbols 必须。生成符号节点(__symbols__)和叠加目标节点(__fixups__),这是Overlay动态加载的基础。
-I dts --in-format=dts 指定输入格式为文本格式的设备树源文件。
-O dtb --out-format=dtb 指定输出格式为二进制设备树块。虽然输出为.dtbo,但格式仍为DTB。
-o --out 指定输出文件名。约定使用.dtbo后缀以区别于普通.dtb。
工程实践提示: 在Makefile或构建脚本中,建议将-@选项放在最前面,避免因选项顺序导致解析错误。同时,输出文件名应与源文件名保持一致,仅替换后缀,便于后续加载时通过文件名匹配。

4.2 编译选项详解与对比

除了核心的-@选项,dtc还提供多个辅助选项用于调试、优化和格式控制。下表对比了Overlay编译中常用选项的用途与风险:

选项 用途 Overlay编译建议
-@ 生成符号与修复信息 必须启用,否则生成的.dtbo无法被内核识别为有效Overlay。
-H epapr 生成ePAPR标准兼容的DTB 通常默认启用,无需额外指定。
-W no-unit_address_vs_reg 关闭reg属性与单元地址不匹配的警告 Overlay中常省略reg或使用占位地址,建议添加此选项避免误报。
-W no-gpios_property 关闭GPIO属性格式警告 Overlay中GPIO描述可能不完整,建议添加。
-O asm 输出汇编格式(用于调试) 仅用于逆向分析或教学演示,不用于生产。
-V 17 指定DTB版本为17 内核4.0+默认使用版本17,通常无需手动指定。
常见错误: 如果编译时忘记添加-@选项,dtc不会报错,但生成的.dtbo文件缺少__fixups__节点。内核在加载时会返回-EINVAL(无效参数)错误,且/sys/kernel/config/device-tree/overlays/目录下不会出现对应条目。这是Overlay调试中最容易忽略的问题之一。

4.3 编译流程与符号生成机制

理解dtc在编译Overlay时的内部处理流程,有助于定位编译错误和符号缺失问题。下图展示了从.dts源文件到.dtbo二进制文件的完整编译链路:

dtc 编译 Overlay 内部流程 .dts 源文件 词法/语法分析 抽象语法树(AST) 符号解析 & 修复生成 __fixups__ 节点 __symbols__ 节点 DTB 二进制编码 .dtbo 输出文件 关键:-@ 选项触发符号解析与修复生成阶段,缺少该选项则跳过此步骤,直接输出不含 __fixups__ 的普通 DTB

4.4 编译注意事项与避坑指南

在实际工程中,Overlay编译环节存在多个容易引发问题的细节,以下按优先级列出:

  • 符号冲突: 如果多个Overlay定义了相同名称的节点标签(label),编译时不会报错,但加载时会导致符号覆盖或加载失败。建议在标签命名时加入模块前缀,例如mydev_i2c0而非i2c0。
  • 路径引用必须使用绝对路径: Overlay中的target属性或&label引用必须指向基板设备树中存在的绝对路径或标签。使用相对路径(如../soc)会导致编译通过但加载时修复失败。
  • 属性覆盖规则: 同一个节点中,如果多次出现相同属性名,dtc默认使用最后一次出现的值,不会报错。这可能导致非预期的属性覆盖。
  • 包含文件路径: 使用/include/或#include时,dtc的搜索路径默认包含当前目录和标准内核头文件路径。建议在编译命令中使用-i <path>显式指定包含路径,避免找不到头文件。
  • 版本兼容性: 不同版本的dtc对Overlay语法的支持程度不同。建议使用内核源码树中的scripts/dtc/dtc,或确保系统dtc版本不低于1.4.7。

4.5 编译验证与反编译检查

编译完成后,建议立即进行两项验证,确保生成的.dtbo文件结构正确:

  1. 文件大小检查: 有效的.dtbo文件通常不小于几百字节。如果文件大小仅为几十字节,很可能编译过程中发生了错误或警告被忽略。
  2. 反编译验证: 使用dtc将.dtbo反编译为.dts格式,检查__fixups__节点是否存在且内容正确:
dtc -I dtb -O dts -o output_decompiled.dts your_overlay.dtbo
grep -A 20 "__fixups__" output_decompiled.dts
反编译输出示例(正确):
/ {
    __fixups__ {
        main_i2c0 = "/soc/i2c@ff000000";
        main_gpio1 = "/soc/gpio@ff001000";
    };
    ...
};

如果__fixups__节点为空或不存在,说明编译时未使用-@选项,或源文件中没有引用任何外部标签。

4.6 自动化编译脚本模板

在工程实践中,建议将编译命令封装为Makefile或Shell脚本,统一管理编译选项和输出路径。以下是一个适用于多Overlay项目的Makefile片段:

# Makefile 片段 - Overlay 编译
DTC_FLAGS = -@ -W no-unit_address_vs_reg -W no-gpios_property
DTC = dtc
SRC_DIR = dts
OUT_DIR = dtbo

OVERLAYS = $(patsubst $(SRC_DIR)/%.dts,$(OUT_DIR)/%.dtbo,$(wildcard $(SRC_DIR)/*.dts))

all: $(OVERLAYS)

$(OUT_DIR)/%.dtbo: $(SRC_DIR)/%.dts | $(OUT_DIR)
	$(DTC) $(DTC_FLAGS) -I dts -O dtb -o $@ $<

$(OUT_DIR):
	mkdir -p $(OUT_DIR)

clean:
	rm -rf $(OUT_DIR)

.PHONY: all clean
工程建议: 在CI/CD流水线中,建议在编译步骤后增加反编译检查步骤,自动验证__fixups__节点非空。这可以提前捕获因-@选项遗漏导致的加载失败问题,避免将无效的.dtbo部署到目标设备。