4. 编译与配置

好,咱们今天聊聊U-Boot的编译和配置。说实话,很多刚入行的朋友拿到U-Boot源码,第一反应就是直接make,然后发现一堆错误。嗯,这很正常。我当年也这么干过,结果被各种莫名其妙的报错折磨了两天。后来才明白,U-Boot的配置系统远比想象中复杂,但也远比想象中有章可循。

4.1 Kconfig配置系统——U-Boot的“灵魂开关”

Kconfig,说白了就是一套菜单式的配置系统。你想想看,U-Boot要支持几百种开发板、上千种外设,如果全靠手动改头文件,那不得疯掉?Kconfig就是来解决这个问题的。

它的核心逻辑很简单:通过图形化或文本界面,让你选择要开启或关闭哪些功能。每个选项背后都对应一个宏定义,最终生成一个名为.config的文件。

关键文件结构:

  • Kconfig —— 定义菜单选项的“剧本”
  • .config —— 你选完后的“结果清单”
  • include/configs/ —— 板级头文件,老派配置方式,现在逐渐被Kconfig取代

我个人习惯,拿到一个新板子,第一件事就是看它的arch/arm/Kconfig(如果是ARM架构)。这里定义了所有支持的SoC和板型。举个例子:

config TARGET_MYBOARD
    bool "Support myboard"
    select CPU_V7
    select DM
    select DM_SERIAL
    help
      Say Y here to support myboard based on MySoC.

看到没?select关键字会自动依赖其他功能。我在项目中遇到过有人手动改了.config,结果漏掉了某个select依赖,编译出来串口不工作。嗯,这种坑踩过一次就记住了。

4.2 make menuconfig——图形化配置实战

配置U-Boot,最常用的命令就是:

make menuconfig

前提是你得先有一个默认配置。通常做法是:

make <board_name>_defconfig
make menuconfig

比如:

make qemu_arm_defconfig
make menuconfig

这时候你会看到一个蓝底白字的菜单界面。上下键移动,回车进入子菜单,空格切换选项状态。我建议你花10分钟把所有菜单都逛一遍,心里有个数。

我的小技巧:

按 / 键可以搜索配置项。比如你想找“USB”相关的配置,直接搜USB,它会列出所有包含USB的选项和路径。这比手动翻菜单快多了。

配置完成后,保存退出。系统会生成.config文件。你可以用cat .config | grep -i "CONFIG_USB"来验证你的配置是否生效。

注意:

千万不要手动编辑.config文件!虽然语法简单,但Kconfig有依赖检查,手动改很容易导致依赖不一致。我见过有人把CONFIG_CMD_USB改成y,但忘了开USB控制器驱动,结果编译通过但运行时报错。老老实实用menuconfig吧。

4.3 交叉编译工具链设置——让代码在目标板上跑起来

你的电脑是x86架构,但嵌入式板子可能是ARM、RISC-V或者MIPS。所以你需要交叉编译工具链。说白了,就是在你的电脑上编译出能在目标板上运行的二进制文件。

设置方法有两种:

  1. 环境变量法:
export CROSS_COMPILE=arm-linux-gnueabihf-
make qemu_arm_defconfig
make
  1. 命令行参数法:
make CROSS_COMPILE=arm-linux-gnueabihf-

我个人更推荐第一种,因为设置一次,整个终端会话都有效。但要注意,CROSS_COMPILE后面一定要带短横线-,这是前缀,U-Boot会自动补全gcc、ld等命令名。

如何验证工具链是否可用?

arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-ld --version

如果提示“command not found”,说明工具链没装或者没加到PATH里。

我曾经在一个项目里,客户发来的工具链名字叫arm-none-eabi-,我习惯性用了arm-linux-gnueabihf-,结果链接阶段报了一堆莫名其妙的内存对齐错误。后来花了半天才排查出来——工具链的浮点ABI不一样。所以,一定要确认工具链和你的目标板匹配。

4.4 编译输出产物分析——看看U-Boot到底生成了什么

编译完成后,你会看到一堆文件。新手往往一脸懵:这么多文件,哪个才是要烧录的?

咱们来逐个分析:

文件 说明 用途
u-boot ELF格式的可执行文件 调试用,包含符号表
u-boot.bin 纯二进制镜像 直接烧录到Flash
u-boot.img 带U-Boot头部的镜像 用于网络启动或SPL加载
u-boot.srec Motorola S-record格式 串口下载用
u-boot.map 链接映射文件 查看函数/变量地址分布
System.map 符号表 调试时查地址用

最常用的是u-boot.bin。但要注意,有些平台还需要SPL(Secondary Program Loader)。比如TI的AM335x系列,SPL负责初始化DDR,然后加载完整的U-Boot。这时候你会看到SPL/u-boot-spl.bin和u-boot.img两个文件。

怎么看编译出来的U-Boot有多大?

ls -lh u-boot.bin
arm-linux-gnueabihf-objdump -h u-boot

用objdump -h可以查看各个段的尺寸,比如.text(代码段)、.data(数据段)、.bss(未初始化数据段)。如果发现.bss特别大,说明你开了太多全局变量,得优化一下。

我还记得有一次,编译出来的u-boot.bin有800多KB,但板子的Flash只有512KB。我一看.map文件,发现某个驱动把整个LCD帧缓冲都编译进去了。嗯,去掉那个驱动后,镜像缩到了300KB。所以,分析.map文件是优化镜像大小的必修课。

4.5 知识体系总览

下面这张图,是我自己总结的U-Boot配置与编译的完整流程。你看一遍,心里就有谱了:

U-Boot 配置与编译流程 1. 选择板级配置 make xxx_defconfig 2. 自定义配置 make menuconfig 3. 生成 .config 自动保存配置 4. 设置工具链 export CROSS_COMPILE=... 5. 执行编译 make -j4 6. 输出产物 u-boot.bin / .img / .map 常见问题排查 • 工具链不匹配 • 依赖未开启 产物验证 • 检查文件大小 • 查看.map布局 常见错误 • 未定义引用 • Flash空间不足

这张图把整个流程串起来了。从选择板级配置开始,到最终拿到可烧录的镜像,每一步都有对应的产出和检查点。你照着这个流程走,基本不会出大问题。

好了,关于U-Boot的编译与配置,核心就是这些。记住:配置靠Kconfig,编译靠工具链,分析靠.map。这三板斧耍熟了,U-Boot这块你就入门了。