rdk3 sdk 整理不完善的地方-->工具链
嵌入式BSP开发中最让人头疼的问题——同一套SDK内部不同组件要求不同的工具链第一部分 问题产生一、问题的本质不是“难搞”是“设计如此”RDK SDK 工具链设计哲学 │ ├── Bootloader (U-Boot) 用 aarch64-linux-gnu- │ ├── 来源Linaro / 芯片厂商提供 │ ├── 特点支持裸机程序编译 │ ├── 包含完整的 libgcc用于原子操作、浮点 │ └── 原因U-Boot 需要编译出可在硬件上直接运行的代码 │ ├── Kernel 用 aarch64-none-linux-gnu- │ ├── 来源ARM 官方嵌入式工具链 │ ├── 特点专为 Linux 内核和嵌入式系统优化 │ ├── 包含精简的 libc 头文件 │ └── 原因内核编译需要特定的 Linux 内核头文件 │ └── 用户态应用 (Ubuntu/Debian) 用 aarch64-linux-gnu- └── 原因需要完整的 glibc 支持二、为什么 xbuild.sh 不设置 CROSS_COMPILE关键发现xbuild.sh确实没有设置CROSS_COMPILE但mk_uboot.sh也没有设置这意味着地平线官方假设工具链已经在系统 PATH 中或者依赖用户提前设置好环境变量。验证方法# 检查环境中是否有 aarch64-linux-gnu-gcc which aarch64-linux-gnu-gcc # 如果没有需要添加到 PATH export PATH/opt/gcc-ubuntu-9.3.0-2020.03-x86_64-aarch64-linux-gnu/bin:$PATH export CROSS_COMPILEaarch64-linux-gnu- # 然后再尝试编译 cd source/bootloader/build ./mk_uboot.sh三、解决方案创建统一的环境脚本借鉴 RK 平台的思路创建一个顶层环境设置脚本set_build_env.sh#!/bin/bash # # RDK SDK 统一编译环境设置脚本 # 使用方式: source set_build_env.sh # # 检测并设置不同的工具链 setup_toolchain() { local component$1 # uboot, kernel, app # 工具链路径根据实际情况修改 local LINARO_TOOLCHAIN/opt/gcc-ubuntu-9.3.0-2020.03-x86_64-aarch64-linux-gnu local ARM_TOOLCHAIN/opt/gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu case $component in uboot|bootloader) # U-Boot 使用 Linaro 工具链 export PATH$LINARO_TOOLCHAIN/bin:$PATH export CROSS_COMPILEaarch64-linux-gnu- echo Setting U-Boot toolchain: $CROSS_COMPILE ;; kernel) # Kernel 使用 ARM 官方工具链 export PATH$ARM_TOOLCHAIN/bin:$PATH export CROSS_COMPILEaarch64-none-linux-gnu- echo Setting Kernel toolchain: $CROSS_COMPILE ;; app) # 应用使用 Linaro 工具链完整 glibc export PATH$LINARO_TOOLCHAIN/bin:$PATH export CROSS_COMPILEaarch64-linux-gnu- echo Setting Application toolchain: $CROSS_COMPILE ;; *) echo Unknown component: $component return 1 ;; esac export ARCHarm64 } # 编译 U-Boot build_uboot() { setup_toolchain uboot cd source/bootloader/build # 先选择板级配置 ./xbuild.sh lunch 0 # 根据实际情况选择 # 编译 U-Boot ./mk_uboot.sh # 打包 miniboot ./xbuild.sh cd - } # 编译 Kernel build_kernel() { setup_toolchain kernel ./mk_kernel.sh } # 使用说明 usage() { echo Usage: source set_build_env.sh echo Then run: build_uboot or build_kernel } # 根据参数执行 if [ ${BASH_SOURCE[0]} ! ${0} ]; then echo Environment ready. Available commands: echo build_uboot - Build U-Boot and miniboot echo build_kernel - Build Linux kernel echo setup_toolchain uboot|kernel|app - Set toolchain else usage fi四、针对错误的快速修复undefined reference to fdt_overlay_apply_verbose错误最快的解决办法是方法1直接修改源码cd source/bootloader/uboot # 编辑 common/fdt_support.c在文件末尾添加 cat common/fdt_support.c EOF #ifdef CONFIG_OF_LIBFDT_OVERLAY int fdt_overlay_apply_verbose(void *fdt, void *fdto) { return fdt_overlay_apply(fdt, fdto); } #endif EOF # 重新编译 make clean make -j$(nproc)方法2禁用 dtoverlay如果不需要cd source/bootloader/uboot # 修改配置 make menuconfig # 取消勾选: Device Tree Control - Enable DT overlay support # 取消勾选: Command line interface - dtoverlay # 重新编译 make clean make -j$(nproc)方法3设置正确的工具链后重试# 设置工具链 export PATH/opt/gcc-ubuntu-9.3.0-2020.03-x86_64-aarch64-linux-gnu/bin:$PATH export CROSS_COMPILEaarch64-linux-gnu- # 检查是否设置正确 echo $CROSS_COMPILE # 应该显示 aarch64-linux-gnu- # 进入 U-Boot 目录 cd source/bootloader/uboot # 完全清理 make distclean # 配置根据板子选择 make xj3_perf_ubuntu_defconfig # 编译 make -j$(nproc) 21 | tee build.log五、理解后的总结┌─────────────────────────────────────────────────────────────────────────────┐ │ 关键理解要点 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. 工具链不一致是设计不是 bug │ │ - U-Boot 需要裸机编译能力Linaro 工具链 │ │ - Kernel 需要 Linux 内核头文件ARM 官方工具链 │ │ │ │ 2. xbuild.sh 不设置 CROSS_COMPILE 的原因 │ │ - 地平线假设用户在系统 PATH 中已配置好 │ │ - 或者依赖用户在调用前手动 export │ │ │ │ 3. 当前错误的根本原因 │ │ - 不是工具链的问题 │ │ - 是 U-Boot 源码版本不一致dtoverlay.c 新libfdt 旧 │ │ │ │ 4. 最佳实践 │ │ - 创建顶层 build.sh 统一管理不同组件的工具链 │ │ - 或在每个编译脚本中显式设置 CROSS_COMPILE │ │ │ └─────────────────────────────────────────────────────────────────────────────┘“为什么官方不统一工具链”嵌入式 BSP 开发中接受这个现实然后建立自己的构建封装才是最高效的做法。创建一个顶层脚本统一管理以后只需要运行./build.sh uboot即可。第二部分fdt_overlay_apply_verbose函数深度解析一、这个函数的作用一个包装函数wrapper function它的作用是fdt_overlay_apply_verbose (详细版) │ ├── 输入两个设备树二进制文件 │ ├── fdt - 主设备树base device tree │ └── fdto - 覆盖设备树overlay device tree │ ├── 处理什么都不做只是转发调用 │ └── return fdt_overlay_apply(fdt, fdto) │ └── 输出返回覆盖应用的结果成功0失败负数简单说这个函数就是调用了另一个函数fdt_overlay_apply然后把结果原样返回。二、为什么要写这样一个多余的函数为了向后兼容backward compatibility和API设计。2.1 历史原因U-Boot 设备树覆盖功能的演进 │ ├── 早期版本2017年前 │ └── 只有 fdt_overlay_apply() - 静默失败不输出错误信息 │ ├── 2017年9月Pantelis Antoniou 提交补丁 │ ├── 新增 fdt_overlay_apply_verbose() │ ├── 功能在失败时打印详细错误信息 │ └── 原因开发者调试 overlay 时不知道哪里出错 │ └── 现状 ├── 新代码调用 fdt_overlay_apply_verbose()要错误信息 ├── 旧代码继续调用 fdt_overlay_apply()保持兼容 └── 如果 CONFIG_OF_LIBFDT_OVERLAY_VERBOSE 未开启 则 fdt_overlay_apply_verbose fdt_overlay_apply2.2 这段代码的作用这段代码实际上是手动实现了这个兼容层// 原本缺失的函数 int fdt_overlay_apply_verbose(void *fdt, void *fdto) { // 简单转发到实际存在的函数 return fdt_overlay_apply(fdt, fdto); }效果链接器现在能找到fdt_overlay_apply_verbose的实现了编译可以通过功能上等价于没有详细错误信息的版本三、设备树覆盖DT Overlay是什么为了理解这个函数的用途需要先了解什么是设备树覆盖设备树覆盖概念 │ ├── 主设备树base.dtb │ └── 描述板子的基本硬件如 CPU、内存、UART │ ├── 覆盖设备树overlay.dtbo │ └── 描述扩展硬件如插入的摄像头、显示屏 │ └── 动态应用 ├── 系统运行时可以动态加载/卸载 overlay ├── 不需要重新编译内核或重启 └── 常用于HAT 板、PCIe 设备、USB 转接板实际应用场景# 例如在树莓派上动态加载一个 RTC 模块 sudo dtoverlay ds1307.dtbo # 内核会调用 fdt_overlay_apply() 来合并设备树 # 如果失败verbose 版本会打印 # Error: Fragment 1: Node name /soc/i2c1 not found四、完整的 fdt_overlay_apply_verbose 实现官方完整版本应该是这样的int fdt_overlay_apply_verbose(void *fdt, void *fdto) { int ret; // 1. 检查参数有效性 if (!fdt || !fdto) { printf(ERROR: NULL pointer in fdt_overlay_apply\n); return -1; } // 2. 检查 FDT 魔术字验证是否是有效的设备树 if (fdt_magic(fdt) ! FDT_MAGIC) { printf(ERROR: Invalid magic in main FDT (0x%08x)\n, fdt_magic(fdt)); return -2; } if (fdt_magic(fdto) ! FDT_MAGIC) { printf(ERROR: Invalid magic in overlay FDT (0x%08x)\n, fdt_magic(fdto)); return -3; } // 3. 尝试应用覆盖 ret fdt_overlay_apply(fdt, fdto); // 4. 如果失败打印详细错误信息这就是 verbose 的精髓 if (ret 0) { printf(ERROR: fdt_overlay_apply failed: %s (err%d)\n, fdt_strerror(ret), ret); // 进一步打印哪个 fragment 失败了 if (ret -FDT_ERR_NOTFOUND) { printf( Hint: A node or property referenced in overlay was not found\n); } } return ret; }五、简化版本 vs 完整版本特性简化版本完整版本官方参数检查❌ 无✅ 有魔术字验证❌ 无✅ 有错误信息打印❌ 无✅ 详细输出调试帮助❌ 无✅ 提示可能原因功能正确性✅ 可以工作✅ 可以工作代码大小很小较大六、uboot整理思路fdt_overlay_apply_verbose是 U-Boot 中用于应用设备树覆盖Device Tree Overlay的函数它的verbose后缀表示会在失败时打印详细错误信息。添加的这段代码是一个简化实现直接调用底层fdt_overlay_apply函数。之所以需要这个函数是因为地平线 SDK 中的cmd/dtoverlay.c调用了它但libfdt库中没有提供实现导致链接失败。这段代码的作用解决链接错误undefined reference提供函数入口让程序能够正常链接功能上等价于不打印详细错误信息的普通版本更根本的理解这个函数的存在体现了设备树覆盖机制的设计——允许系统在运行时动态修改硬件描述。这对于需要支持热插拔外设的嵌入式系统非常有用。为什么官方代码会缺失这通常是 SDK 整合时的版本不一致导致的。dtoverlay.c来自较新的 U-Boot但libfdt来自旧版本。添加这个 wrapper 是最小侵入性的修复方案。