如何裁剪u-boot,保留其必要功能,使体积尽可能小
裁剪 U-Boot 的核心目标是在保证其能正常引导内核的前提下,通过关闭冗余功能、精简驱动和优化编译选项,减小二进制体积(尤其针对嵌入式设备有限的存储空间)。以下结合 U-Boot 的模块化设计、配置机制及 Buildroot 环境,从原理、具体方法、验证流程等方面讲解一下。
先通过流程图理清从 “需求分析” 到 “最终验证” 的全流程,避免遗漏关键环节。
U-Boot裁剪流程
├─ 明确核心需求
│ ├─ 必保留功能:CPU/内存/时钟初始化、存储驱动、bootz命令、串口
│ └─ 可裁剪功能:网络、USB、冗余命令、调试功能
├─ 配置准备(Buildroot环境)
│ ├─ 选择基础配置:官方defconfig/自定义defconfig
│ └─ 进入配置界面:make uboot-menuconfig
├─ 分层裁剪功能
│ ├─ 裁剪冗余命令:关闭CONFIG_CMD_XXX(如ping/tftp)
│ ├─ 精简驱动:保留存储/串口,关闭USB/PCIe
│ ├─ 关闭网络/文件系统:仅留启动用文件系统
│ └─ 删除调试功能:关闭CONFIG_DEBUG/CONFIG_LOG
├─ 编译优化
│ ├─ 配置编译参数:-Os(体积优化)-fomit-frame-pointer
│ └─ 可选:启用U-Boot压缩(CONFIG_SYS_BOOT_COMPRESSED)
└─ 验证测试├─ 体积检查:ls -lh对比u-boot.bin大小├─ 功能测试:烧录后验证“启动+内核加载”└─ 迭代调整:若功能缺失,恢复对应配置
再通过树状图了解 U-Boot 的功能层级,标注 “必保留” 和 “可裁剪”,直观区分功能优先级。
U-Boot功能模块
├─ 核心必需功能(不可裁)
│ ├─ 硬件初始化:CPU/内存/时钟
│ ├─ 存储驱动:SPI Flash/NAND(启动介质)
│ ├─ 引导命令:bootm/bootz(加载内核)、setenv/saveenv(环境变量)
│ └─ 串口驱动:调试输出+交互(必留,否则无法排查问题)
│
└─ 可裁剪功能(按需删)├─ 命令模块│ ├─ 网络命令:ping/tftp/dhcp(无网络启动时删)│ ├─ 文件系统命令:ext4ls/fatls(非启动文件系统删)│ ├─ 调试命令:memtest/bdi(量产时删)│ └─ 外设命令:usb/pci(无对应外设删)│├─ 驱动模块│ ├─ USB驱动:USB_HOST/USB_DEVICE(无USB接口删)│ ├─ 网络驱动:以太网/Wi-Fi(无网络功能删)│ ├─ 显示驱动:LCD/HDMI(无屏幕删)│ └─ 传感器驱动:I2C/SPI传感器(不使用删)│├─ 协议/文件系统│ ├─ 网络协议:DHCP/TFTP/NFS(无网络删)│ └─ 文件系统:ext4/FAT/JFFS2(非启动用删)│└─ 冗余特性├─ 调试功能:CONFIG_DEBUG/CONFIG_LOG(量产删)├─ 脚本支持:CONFIG_SCRIPT(无需boot.scr删)└─ 密码保护:CONFIG_PASSWORD(无需验证删)
一、裁剪原理与核心思路
U-Boot 采用模块化设计,所有功能(命令、驱动、协议等)通过CONFIG_XXX配置项控制编译开关,且通过 Kconfig 管理依赖关系。其体积主要由两部分决定:
- 功能模块:如网络协议(TFTP/NFS)、文件系统支持(ext4/FAT)、调试命令等;
- 驱动:针对未使用硬件(如 USB、PCIe、非目标 Flash)的驱动。
裁剪的核心思路是:仅保留当前硬件必需的引导功能(如读取内核镜像、启动内核、基本串口交互),删除所有无关功能和驱动,同时通过编译优化进一步缩减体积。
二、准备工作:基于 Buildroot 的配置环境搭建
在 Buildroot 中裁剪 U-Boot 需先明确配置来源和操作方式,确保裁剪后的配置可持久化管理。
1. 确定 U-Boot 配置文件
Buildroot 中 U-Boot 的配置由BR2_TARGET_UBOOT_CONFIG指定(通过make menuconfig配置),有两种方式:
- 官方 defconfig:选择芯片 / 板子对应的官方默认配置(如
stm32mp157_defconfig),作为基础配置; - 自定义 defconfig:基于官方 defconfig 修改后,保存为
board/<厂商>/<板子名>/u-boot.defconfig,便于后续升级和复用,推荐优先使用。
2. 进入 U-Boot 配置界面
在 Buildroot 中可直接通过图形化工具裁剪功能,操作如下:
# 在Buildroot根目录执行,进入U-Boot配置界面(类似内核menuconfig)
make uboot-menuconfig
界面中,[*]表示启用功能,[ ]表示禁用,按?可查看每个配置项的功能说明及依赖关系,这是裁剪的核心操作入口。
三、分析:确定可裁剪的模块
在裁剪前需明确目标硬件的核心需求(如启动介质、调试接口、是否需要网络等),再通过以下方式定位可裁剪部分:
1. 基于配置文件和 Kconfig 分析
- 目标板配置头文件(
include/configs/xxx.h)和.config文件记录当前启用的功能(如CONFIG_CMD_PING对应ping命令); - 通过
make menuconfig的帮助信息(按?)了解功能用途(如CONFIG_ETH对应以太网驱动),判断是否为必需。
2. 基于编译产物分析体积占比
编译后生成的u-boot.map文件记录各模块的大小,可定位 “体积大户”:
cmd/目录下的命令模块(如cmd/network.c对应网络命令);drivers/下的驱动(如drivers/usb/占比通常较大);fs/下的文件系统支持(如fs/ext4/)。
3. 基于启动流程确定必需功能
U-Boot 核心启动流程为:硬件初始化(CPU / 内存 / 时钟)→ 驱动加载(存储 / 串口)→ 执行启动命令(bootz)。必须保留的功能包括:
- CPU、内存(SDRAM/DDR)、时钟初始化(芯片级核心代码,不可裁剪);
- 目标存储介质驱动(如 SPI Flash、NAND,用于读取内核);
- 串口驱动(用于调试输出和交互);
- 内核加载命令(
bootm/bootz)和环境变量支持(saveenv/setenv)。
四、具体裁剪方法(按功能模块分类)
1. 裁剪冗余命令(核心!命令模块占比大)
U-Boot 的命令由CONFIG_CMD_XXX控制,仅保留引导必需的命令,其余可关闭。
| 分类 | 必保留命令(配置项) | 功能描述 | 可关闭的冗余命令(配置项) | 禁用场景 |
|---|---|---|---|---|
| 核心引导 | CONFIG_CMD_BOOTM/CONFIG_CMD_BOOTZ | 启动内核(bootm/bootz) | - | - |
| 镜像加载 | CONFIG_CMD_LOADIMG | 加载镜像到内存 | - | - |
| 环境变量 | CONFIG_CMD_ENV | 环境变量操作(saveenv/setenv) | - | - |
| 网络相关 | - | - | CONFIG_CMD_TFTP(tftp)、CONFIG_CMD_PING(ping) | 无需网络下载内核时 |
| 文件系统 | - | - | CONFIG_CMD_EXT4(ext4ls)、CONFIG_CMD_FAT(fatls) | 内核不存于对应文件系统时 |
| 外设操作 | - | - | CONFIG_CMD_USB(usb)、CONFIG_CMD_PCI(lspci) | 无对应外设或无需 U-Boot 阶段操作时 |
| 调试相关 | - | - | CONFIG_CMD_MEMTEST(memtest)、CONFIG_CMD_BDI | 量产阶段(调试阶段可保留) |
2. 关闭不必要的网络功能
若设备仅从本地存储(如 Flash)启动,可完全禁用网络模块:
# 关闭以太网驱动(无网卡时)
# CONFIG_DRIVER_NET=y
# 关闭Wi-Fi驱动(无Wi-Fi时)
# CONFIG_DRIVER_WLAN=y
# 关闭网络协议栈及工具
# CONFIG_NET=y
# CONFIG_DHCP=y
# CONFIG_TFTP=y
3. 裁剪文件系统支持
仅保留存放内核的文件系统格式,其余关闭。例如:若内核存于 SPI Flash 的squashfs分区:
# 保留必需的文件系统
CONFIG_SQUASHFS=y
# 关闭其他文件系统
# CONFIG_EXT4_FS=y
# CONFIG_FAT_WRITE=y
# CONFIG_JFFS2=y
# CONFIG_UBIFS=y
4. 精简驱动
仅保留目标硬件必需的驱动,删除无关外设驱动:
-
必保留驱动:
- 板载存储驱动(如 SPI Flash:
CONFIG_SPI_FLASH=y;NAND:CONFIG_NAND=y); - 串口驱动(如
CONFIG_SERIAL_STM32=y,用于调试); - CPU / 内存驱动(芯片级默认配置,不可删)。
- 板载存储驱动(如 SPI Flash:
-
可关闭的驱动:
# 关闭USB驱动(无USB接口时)
# CONFIG_USB=y
# CONFIG_USB_EHCI_HCD=y
# 关闭PCIe驱动(无PCIe设备时)
# CONFIG_PCI=y
# 关闭显示驱动(无屏幕时)
# CONFIG_VIDEO=y
# CONFIG_LCD=y
# 关闭传感器驱动(不使用I2C传感器时)
# CONFIG_I2C=y
5. 关闭调试和冗余特性
调试功能会显著增加体积,量产固件可完全禁用:
# 关闭调试输出(减少大量打印代码)
# CONFIG_DEBUG=y
# 关闭详细日志
# CONFIG_LOG=y
# 关闭调试符号(编译时不生成)
# CONFIG_DEBUG_SYMBOLS=y
# 关闭脚本支持(无需执行boot.scr时)
# CONFIG_SCRIPT=y
# 关闭密码保护(无需验证时)
# CONFIG_PASSWORD=y
五、编译优化:进一步减小体积
除配置裁剪外,通过编译选项和 U-Boot 自身特性进一步缩减体积:
1. 优化编译参数(Buildroot 中配置)
在 Buildroot 中,通过BR2_TARGET_UBOOT_CFLAGS传递体积优化参数(路径:Target packages → Bootloaders → U-Boot → Extra U-Boot compiler flags):
- 添加
-Os(优化体积,替代默认-O2); - 去除
-g(不生成调试符号); - 可选
-fomit-frame-pointer(减少栈帧信息)。
配置示例:
BR2_TARGET_UBOOT_CFLAGS="-Os -fomit-frame-pointer"
2. 启用 U-Boot 自身压缩(可选)
部分 U-Boot 支持压缩自身镜像(如 gzip),启动时先解压再执行,可减小存储体积(代价是增加启动时间):
# 启用压缩(需U-Boot版本支持)
CONFIG_SYS_BOOT_COMPRESSED=y
# 选择压缩算法
CONFIG_COMPRESS_GZIP=y
六、验证裁剪效果
裁剪后需通过以下步骤确认功能正常且体积减小:
1. 查看体积变化
编译完成后,U-Boot 镜像(u-boot.bin或u-boot.img)位于output/images/目录,通过ls -lh对比裁剪前后大小:
ls -lh output/images/u-boot.bin
2. 实际烧录测试
将裁剪后的 U-Boot 烧录到设备,验证核心功能:
- 能否正常启动(串口输出启动日志);
- 能否识别目标存储介质(如
sf probe检测 SPI Flash); - 能否加载并启动内核(执行
bootz命令)。
七、注意事项
- 逐步裁剪,避免过度删除:每次仅禁用少量功能,编译测试后再继续,避免一次性裁剪过多导致难以定位问题;
- 关注依赖关系:部分功能存在隐性依赖(如
CONFIG_CMD_BOOTZ依赖CONFIG_ZLIB),可通过make menuconfig的帮助信息(?)确认; - 保留硬件核心功能:确保板载存储、串口、内核启动命令依赖的功能不被误删;
- 基于官方配置修改:以芯片厂商提供的 defconfig 为基础裁剪,避免删除芯片必需的底层配置;
- 记录裁剪项:将关闭的配置项记录到文档,便于后续升级 U-Boot 或排查问题时复用。
通过以上步骤,U-Boot 体积可从默认的数百 KB(甚至数 MB)缩减至几十 KB(极简场景),核心是围绕 “仅保留引导内核必需的功能” 这一原则,结合配置裁剪和编译优化,实现体积与功能的平衡。
