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二进制文件的完整编译链路:
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文件结构正确:
- 文件大小检查: 有效的
.dtbo文件通常不小于几百字节。如果文件大小仅为几十字节,很可能编译过程中发生了错误或警告被忽略。 - 反编译验证: 使用
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部署到目标设备。