3. U-Boot 配置与编译:从 menuconfig 到生成镜像
好,到了真正动手的环节了。配置和编译 U-Boot,说白了就是告诉它「你要跑在哪个板子上,用哪些功能」。这一步要是搞错了,后面调试起来会非常痛苦。我刚开始移植 U-Boot 的时候,就因为交叉编译链没选对,折腾了整整一个下午。
3.1 使用 menuconfig 进行配置
U-Boot 的配置系统继承自 Linux 内核,核心就是 make menuconfig。它会读取 arch/../Kconfig 和各个子目录下的 Kconfig 文件,生成一个图形化的配置菜单。
核心流程:
- 选择默认配置:
make加载板级默认配置_defconfig - 定制功能:
make menuconfig打开图形界面调整 - 保存配置:自动写入
.config文件
我个人习惯先跑一遍 make xxx_defconfig,再用 make menuconfig 微调。为什么?因为默认配置已经包含了该板子的基础参数——DDR 时序、时钟频率、外设地址。你从头配一遍,很容易漏掉某个关键选项。
小技巧: 配置完成后,用 make savedefconfig 生成精简的 defconfig 文件。这样下次移植到类似板子时,直接拿这个文件改,比重新配一遍快得多。
3.2 交叉编译工具链设置
交叉编译,就是在 PC 上编译出能在 ARM/RISC-V 等目标板上运行的代码。工具链选不对,编译出来的 U-Boot 根本跑不起来。
设置方法有两种:
| 方式 | 命令/变量 | 适用场景 |
|---|---|---|
| 环境变量 | export CROSS_COMPILE=arm-linux-gnueabihf- |
临时使用,适合测试 |
| Make 参数 | make CROSS_COMPILE=arm-linux-gnueabihf- |
每次编译都要带,适合脚本 |
| 写入 Makefile | 在 config.mk 中固定设置 |
长期项目,团队统一 |
我曾经踩过的坑: 用 arm-none-eabi- 工具链编译 U-Boot,结果链接阶段报错。后来才发现,U-Boot 需要 arm-linux-gnueabihf- 这种带 Linux 支持的工具链,因为 U-Boot 的某些库依赖 Linux 的 ABI。嗯,这个细节很容易被忽略。
验证工具链是否可用:
arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-objdump --version
如果提示「command not found」,说明工具链没装或者没加到 PATH 里。我建议把工具链路径写到 ~/.bashrc 里,省得每次开终端都要 export 一遍。
3.3 编译生成 u-boot.bin 与 u-boot.srec
配置好之后,直接 make 就行。但编译产物有好几种,你得知道它们分别干什么用。
| 产物 | 格式 | 用途 |
|---|---|---|
u-boot |
ELF | 调试用,包含符号表 |
u-boot.bin |
纯二进制 | 烧录到 SPI Flash / SD 卡 |
u-boot.srec |
S-Record (文本) | 通过串口下载到 RAM 运行 |
u-boot.img |
U-Boot 镜像 + 头部 | 包含校验信息,用于启动链 |
你想想看,u-boot.bin 是裸的二进制,烧到 Flash 里直接从 0x0 地址执行。u-boot.srec 是文本格式,每行都带地址信息,适合用 Xmodem 协议通过串口下载到任意地址。我在调试早期启动阶段时,经常用 u-boot.srec 直接下载到 DDR 里跑,省去反复擦写 Flash 的时间。
编译命令速查:
# 完整编译
make CROSS_COMPILE=arm-linux-gnueabihf- -j4
# 只生成 u-boot.bin
make CROSS_COMPILE=arm-linux-gnueabihf- u-boot.bin
# 只生成 u-boot.srec
make CROSS_COMPILE=arm-linux-gnueabihf- u-boot.srec
3.4 知识体系:配置与编译流程
下面这张图,是我画给团队新人的。它把整个配置编译流程串起来了,你一看就明白。
3.5 常见问题与避坑
- 编译报错:undefined reference to `xxx' —— 大概率是工具链版本太老,或者
CROSS_COMPILE前缀写错了。检查一下arm-linux-gnueabihf-gcc -v的输出。 - menuconfig 界面空白 —— 缺少
ncurses库。Ubuntu 下sudo apt install libncurses5-dev即可解决。 - u-boot.bin 烧进去跑不起来 —— 检查一下
CONFIG_SYS_TEXT_BASE是否和板子的启动地址匹配。我遇到过一块板子,DDR 起始地址是 0x80000000,但默认配置写的是 0x40000000,结果当然跑不起来。
我的调试习惯: 编译完成后,先用 arm-linux-gnueabihf-objdump -d u-boot 反汇编,看看入口地址对不对。如果 _start 的地址和 CONFIG_SYS_TEXT_BASE 不一致,那肯定是链接脚本出了问题。
嗯,配置和编译这部分,说白了就是三板斧:选配置、设工具链、编译。但每一板斧都有细节,细节决定成败。你按这个流程走一遍,应该不会出大问题。