3. U-Boot源码获取与目录结构

说实话,很多初学者在U-Boot移植上栽跟头,十有八九是编译环境没搞对。我见过太多人花了两天时间配置环境,结果make的时候报一堆莫名其妙的错误——其实说白了,就是工具链版本不匹配或者路径没配好。

这一章咱们就把编译环境彻底捋清楚。你跟着我的步骤走,保证一次过。

3.1 交叉编译工具链的选择与安装

先回答一个关键问题:为什么非得用交叉编译工具链?

你想想看,U-Boot是要跑在ARM、RISC-V或者MIPS这类嵌入式处理器上的。你的开发机是x86架构,编译出来的二进制文件x86 CPU能直接跑,但ARM CPU读不懂。交叉编译器的作用,就是在x86机器上生成目标架构的机器码。

我个人习惯用Linaro提供的GCC工具链,稳定、社区支持好。当然你也可以用芯片厂商提供的SDK里的工具链,比如ST、NXP、TI都有自己的版本。但如果你刚开始学,我建议先用Linaro的,通用性强,踩坑少。

3.1.1 下载与解压

以ARM64(aarch64)为例,我常用的版本是gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu。下载后解压到/opt/toolchains目录:

sudo mkdir -p /opt/toolchains
sudo tar xf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt/toolchains/

解压完成后,检查一下目录结构:

ls /opt/toolchains/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/

你应该能看到aarch64-none-linux-gnu-gcc、aarch64-none-linux-gnu-ld等可执行文件。嗯,看到这些就说明工具链本体没问题。

注意: 不要用系统自带的gcc来编译U-Boot。我见过有人图省事直接make CROSS_COMPILE=gcc,结果编译出来的U-Boot在目标板上跑不起来——因为那是x86的机器码。交叉编译器的前缀一定要带目标架构标识。

3.1.2 验证工具链

验证工具链能不能正常工作,跑一行代码就够了:

/opt/toolchains/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc --version

输出应该类似:

aarch64-none-linux-gnu-gcc (GCC Toolchain for Arm) 10.3.1 20210621
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.

看到版本号就说明工具链安装成功了。我曾经遇到过解压后权限不对,导致gcc无法执行的情况——chmod +x一下就好,小问题。

3.2 环境变量配置

工具链装好了,但每次编译都要敲一长串路径,太烦了。我们需要把工具链路径加到PATH里,再设置CROSS_COMPILE前缀。

3.2.1 临时配置(当前终端有效)

如果你只是临时编译一次,直接在终端里export就行:

export PATH=/opt/toolchains/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH
export CROSS_COMPILE=aarch64-none-linux-gnu-
export ARCH=arm64

注意CROSS_COMPILE后面有个短横线,这个不能漏。U-Boot的Makefile会在这个前缀后面自动拼接gcc、ld、objcopy等工具名。

3.2.2 永久配置(推荐)

我个人习惯把这些配置写到~/.bashrc里,这样每次打开终端就自动生效:

echo 'export PATH=/opt/toolchains/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH' >> ~/.bashrc
echo 'export CROSS_COMPILE=aarch64-none-linux-gnu-' >> ~/.bashrc
echo 'export ARCH=arm64' >> ~/.bashrc
source ~/.bashrc
小技巧: 如果你同时维护多个项目(比如ARM32和ARM64),我建议不要在.bashrc里写死CROSS_COMPILE和ARCH。更好的做法是:在.bashrc里只加PATH,然后在每个项目的根目录下写一个setenv.sh脚本,编译前source一下。这样切换项目时不会串环境。

3.2.3 验证环境变量

配置完成后,用下面几条命令确认一下:

echo $PATH | grep aarch64
echo $CROSS_COMPILE
echo $ARCH

如果都能正确输出,说明环境变量已经生效了。

3.3 基础编译命令:make 与 make menuconfig

环境搭好了,咱们来编译一个实际的U-Boot。这里我用qemu_arm64_defconfig做演示,这个配置可以在QEMU模拟器上跑,不需要真实硬件。

3.3.1 获取U-Boot源码

从官方仓库克隆最新稳定版:

git clone https://github.com/u-boot/u-boot.git
cd u-boot
git checkout v2024.01

我建议用tag版本,不要直接用master分支。master分支可能包含未稳定测试的代码,我在项目中吃过这个亏——编译没问题,但跑起来就挂。

3.3.2 make menuconfig:配置内核

先加载默认配置,再进入图形化配置界面:

make qemu_arm64_defconfig
make menuconfig

make menuconfig会打开一个基于ncurses的菜单界面。你可以在里面选择要支持的驱动、文件系统、命令等。对于初学者,我建议先不要乱改,直接退出保存就行。等后面学到具体模块时,再回来调整。

核心概念: make menuconfig实际上是在编辑.config文件。这个文件决定了U-Boot最终编译哪些功能进镜像。你手动改.config也行,但用menuconfig更安全,因为它会帮你检查依赖关系。

3.3.3 make:编译

配置完成后,直接执行make:

make -j$(nproc)

-j$(nproc)表示用所有CPU核心并行编译,能大幅加快速度。我第一次编译U-Boot时不知道这个参数,单核编译等了快10分钟。后来加了-j4,两分钟就搞定了。

编译成功后,你会在当前目录下看到u-boot.bin和u-boot(ELF格式)两个文件。前者是最终的二进制镜像,可以烧写到板子上;后者带调试信息,可以用GDB调试。

3.3.4 常见编译错误与解决

我整理了几个最常见的编译错误,你遇到了可以直接对照排查:

错误信息 原因 解决方法
arm-none-linux-gnueabi-gcc: command not found 工具链不在PATH中 检查export PATH是否正确
*** Your GCC is too old 编译器版本低于U-Boot要求 升级到GCC 10.x以上
*** Can't find libgcc 工具链安装不完整 重新解压或安装完整版工具链
make: *** No rule to make target 'menuconfig' 缺少ncurses库 sudo apt install libncurses-dev
避坑指南: 我曾经在Ubuntu 20.04上用系统自带的apt install gcc-arm-none-eabi安装工具链,结果编译U-Boot时报__aeabi_uldivmod未定义。后来查了半天才发现,系统源里的工具链版本太旧,不支持某些内建函数。从那以后,我再也不用apt装交叉编译器了,一律从Linaro官网下载。

3.4 本章知识体系

下面这张图把编译环境搭建的整个流程串起来了,你可以对照着检查自己哪一步还没做:

U-Boot编译环境搭建流程 U-Boot编译环境搭建流程 1. 下载工具链 2. 解压到 /opt/toolchains 3. 验证 gcc --version 4. 配置 PATH 5. 设置 CROSS_COMPILE 6. 设置 ARCH 7. make defconfig 8. make menuconfig 9. make -j$(nproc) 阶段一:工具链安装 阶段二:环境变量 阶段三:编译 输出产物 u-boot.bin / u-boot(ELF) 提示:如果编译报错,优先检查工具链版本和PATH配置 90%的编译失败都是这两个原因造成的

嗯,到这里编译环境就全部搭好了。你可以在终端里跑一遍make qemu_arm64_defconfig && make -j$(nproc),如果顺利通过,恭喜你——你已经迈出了U-Boot移植的第一步。