第4章 编译与构建环境:U-Boot源码获取与版本选择、交叉编译工具链配置、常用构建命令与配置选项
说实话,很多做嵌入式开发的朋友,第一步就栽在环境搭建上。源码下载错了版本,工具链配不对,make 命令敲下去报一堆看不懂的错误。我当年刚入行时也踩过这些坑,有一次折腾了整整两天,最后发现是工具链的 glibc 版本不匹配。
这一章,我就把编译环境这块掰开了讲清楚。你跟着走一遍,基本不会再出问题。
4.1 U-Boot 源码获取与版本选择
U-Boot 的源码托管在 https://source.denx.de/u-boot/u-boot。你也可以从 GitHub 的镜像仓库拉取,但官方源最靠谱。
版本选择上,我个人的经验是:不要追新,也不要太旧。选 LTS 版本或者你板子厂商推荐的版本最稳。
版本选择建议:
- 官方 LTS 版本:如 v2023.04、v2024.01,稳定性好,社区支持强
- 板级 SDK 配套版本:NXP、TI、Rockchip 等厂商会提供定制版,适配性最好
- 主线最新版:适合尝鲜或需要新功能,但可能有回归问题
我建议你这样做:先查板子的 defconfig 文件在哪个版本引入的。比如你想用 imx8mp_evk_defconfig,就去源码里搜一下,看看它最早出现在哪个 tag 里。这样选出来的版本,配置最全。
小技巧:用 git tag | grep 2024 可以快速列出所有 2024 年的版本标签。然后 git checkout v2024.01 切过去就行。
4.2 交叉编译工具链配置
交叉编译工具链,说白了就是让你的 x86 电脑能编译出 ARM 或 RISC-V 架构的二进制文件。没有它,U-Boot 就编译不出来。
工具链的选择,取决于你的目标架构。我整理了一张表,你对着选就行:
| 目标架构 | 推荐工具链 | 下载方式 |
|---|---|---|
| ARM (32-bit) | arm-linux-gnueabihf- |
Linaro / 发行版包管理器 |
| AArch64 (ARM64) | aarch64-linux-gnu- |
Linaro / 发行版包管理器 |
| RISC-V (64-bit) | riscv64-linux-gnu- |
SiFive / 发行版包管理器 |
| MIPS | mips-linux-gnu- |
Codescape / 发行版包管理器 |
我个人习惯用 Linaro 的工具链,因为它更新及时,而且对 U-Boot 的兼容性最好。安装方法很简单:
# 下载 ARM64 工具链
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
# 解压到 /opt
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz -C /opt
# 添加环境变量
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH
export CROSS_COMPILE=aarch64-linux-gnu-
export ARCH=arm64
注意:环境变量 CROSS_COMPILE 和 ARCH 必须设置正确。我见过有人把 CROSS_COMPILE 设成了 arm-linux-gnueabihf- 但 ARCH 却写 arm64,结果编译出来的东西根本跑不起来。
验证工具链是否可用:
aarch64-linux-gnu-gcc --version
# 输出类似:aarch64-linux-gnu-gcc (Linaro GCC 7.5-2019.12) 7.5.0
嗯,看到版本号就说明配好了。
4.3 常用构建命令与配置选项
U-Boot 的构建流程,我总结为三步:配置、编译、打包。每一步都有对应的命令。
4.3.1 配置阶段
配置的核心是 make <board_name>_defconfig。这个命令会根据 configs/ 目录下的默认配置文件,生成 .config。
# 以 i.MX8MP EVK 为例
make imx8mp_evk_defconfig
# 如果你想微调配置,用 menuconfig
make menuconfig
menuconfig 是基于 ncurses 的图形界面,可以方便地开关功能。我在项目中经常用它来调试,比如临时打开某个调试打印。
4.3.2 编译阶段
配置好之后,直接 make 就行。但有几个参数你最好记住:
# 全量编译
make -j$(nproc)
# 只编译某个模块(加快调试速度)
make drivers/mmc/built-in.o
# 编译并生成最终的 u-boot.bin
make u-boot.bin
-j$(nproc) 是并行编译,能充分利用多核 CPU。我一般用 -j8 或 -j16,看机器配置。
提速技巧:如果你只改了某个驱动文件,不要重新 make 全部。用 make drivers/net/built-in.o 只编译那个目录,然后手动链接。能省不少时间。
4.3.3 常用配置选项
U-Boot 的配置选项非常多,但常用的就这几个:
| 配置选项 | 作用 | 典型值 |
|---|---|---|
CONFIG_SYS_TEXT_BASE |
U-Boot 在内存中的加载地址 | 0x40200000 |
CONFIG_BOOTDELAY |
启动倒计时秒数 | 3 |
CONFIG_BAUDRATE |
串口波特率 | 115200 |
CONFIG_BOOTCOMMAND |
默认启动命令 | "run distro_bootcmd" |
CONFIG_EXTRA_ENV_SETTINGS |
额外的环境变量 | 板级定制内容 |
这些选项可以在 menuconfig 里改,也可以直接编辑 .config 文件。但我建议你用 make menuconfig,因为它会帮你处理依赖关系。我曾经手改 .config 导致依赖没更新,编译出来功能异常,查了半天才发现。
4.4 构建流程全景图
下面这张图,把整个构建流程串起来了。你一看就明白:
4.5 常见问题与避坑指南
编译环境这块,我踩过的坑不少。挑几个典型的说说:
坑1:工具链版本太新或太旧
我曾经用 GCC 12 编译 U-Boot v2020.04,结果链接阶段报了一堆 relocation truncated to fit 错误。后来换成 Linaro GCC 7.5 就没事了。U-Boot 对工具链版本有要求,太新的 GCC 可能会引入不兼容的链接行为。
坑2:环境变量没导出
很多人直接在终端里设 CROSS_COMPILE=...,但没加 export。结果 make 的时候找不到工具链。你想想看,make 是在子进程里跑的,不 export 的话子进程根本看不到这个变量。
我的习惯:在项目根目录下放一个 env.sh 脚本,里面写好所有环境变量。每次打开新终端,先 source env.sh。这样不会漏。
坑3:.config 被意外覆盖
如果你先 make xxx_defconfig,然后又 make distclean,.config 会被删掉。我建议你备份一份 .config,或者用 make savedefconfig 生成精简版配置。
4.6 快速验证:编译一个最小系统
说了这么多,我们来动手验证一下环境是否配好了。选一个最简单的板子——QEMU 的 ARM 虚拟板:
# 1. 配置
make qemu_arm_defconfig
# 2. 编译
make -j$(nproc)
# 3. 检查产物
ls -lh u-boot.bin
# 应该能看到一个几百 KB 的文件
如果这一步能顺利通过,说明你的工具链和环境变量都没问题。接下来就可以针对具体板子进行定制了。