当前位置:首页 > 技术 > 正文内容

U-Boot 构建系统深度剖析

访客 技术 2026年6月5日 1

一、构建系统基础认知

1.1 前置知识储备

深入理解 U-Boot 构建体系需具备:Linux 常用操作、Shell 脚本编写能力以及 Makefile 语法基础。

1.2 Makefile 核心范式

任何复杂的构建系统都源于基础规则。Makefile 的本质结构为:

output: prerequisite1 prerequisite2 ...
    command

以下是一个嵌入式固件编译的极简示例:

firmware.bin: startup.o flash.o core.o
    arm-none-eabi-ld -T flash.ld -o firmware.elf startup.o core.o
    arm-none-eabi-objcopy -O binary -S firmware.elf firmware.bin

startup.o: startup.S
    arm-none-eabi-gcc -Wall -c -o startup.o startup.S

flash.o: flash.c hw_register.h
    arm-none-eabi-gcc -Wall -c -o flash.o flash.c

core.o: core.c hw_register.h
    arm-none-eabi-gcc -Wall -c -o core.o core.c

clean:
    rm -f firmware.bin firmware.elf *.o

其核心理念可概括为:产出(目标)由原材料(依赖)经加工流程(命令)转化而来。

基础 Makefile 完成后,为达成模块化裁剪与自动化构建,需引入高级机制:

  • 动态目标路径指定
  • 自动化依赖生成与条件源码筛选
  • 隐式规则、模式匹配及工具链参数化

1.3 U-Boot 构建体系组成

U-Boot 作为跨平台引导程序,需从海量源文件中筛选合适内容,采用匹配策略完成编译链接。这依赖多层级 Makefile 协同运作:

组件职能说明
根目录 Makefile全局调控编译链接流程,定义最终产物 u-boot.bin
根目录 config.mk统一定义编译规则,被各级 Makefile 引用
根目录 rules.mk负责依赖关系生成,供子目录 Makefile 调用
各级子目录 Makefile管控当前目录的编译与归档行为
根目录 mkconfig编译前执行的环境准备脚本

编译过程中生成的关键辅助文件:

文件说明
include/config.mk由 mkconfig 生成,定义 ARCH、CPU 等全局变量,被根目录 Makefile 包含
各级 .depend子目录 Makefile 生成的依赖描述文件,供同级 Makefile 引用

二、目标体系解析

2.1 顶层目标

根目录 Makefile 的 all 伪目标由多个子目标构成:

ALL_TARGETS = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)

用户可扩展其他格式目标,如 u-boot.disu-boot.imgu-boot.hex 等。执行 make all 将无条件重建所有分目标。

2.2 子目录目标

子目录 Makefile 中典型定义:

all:    $(obj).depend $(INIT) $(ARCHIVE)

$(ARCHIVE): $(obj).depend $(COBJS) $(SOBJS)

其中 INIT 为启动相关目标文件,ARCHIVE 为待生成的静态库,COBJSSOBJS 分别对应 C 文件与汇编文件编译产生的 .o 文件。

三、依赖管理机制

3.1 最终产物的依赖构成

$(obj)u-boot:    depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)

核心依赖项为 $(OBJS)$(LIBS)。编译流程的正向过程为:源文件(*.c *.S)→ 中间目标(*.o *.a)→ 最终固件(*.bin);而 Make 的解析方向恰好相反。

3.2 平台适配与源码裁剪

U-Boot 支持 ARM、PowerPC 等多种架构及 VxWorks、Linux 等操作系统,源码包含 lib_ppclib_arm 等平台相关目录。实际编译时需针对特定硬件进行裁剪。

执行 make smdk2410_config 等配置命令后,mkconfig 脚本生成 include/config.mk,其中定义:

ARCH   = arm
CPU    = arm920t
BOARD  = smdk2410
SOC    = s3c24x0

根目录 Makefile 包含此文件后,依据变量值选择参与编译的目录,如 lib_arm/board/smdk2410cpu/arm920t 等。

外设驱动的裁剪则通过 C 预处理实现,配置文件位于 include/configs/<board_name>.h。例如启用 CS8900 网卡:

#define CONFIG_DRIVER_CS8900 1

3.3 依赖变量的动态组装

OBJS  = cpu/$(CPU)/start.o
ifeq ($(CPU),i386)
OBJS += cpu/$(CPU)/start16.o
OBJS += cpu/$(CPU)/reset.o
endif
ifeq ($(CPU),ppc4xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc83xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc85xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc86xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),bf533)
OBJS += cpu/$(CPU)/start1.o      cpu/$(CPU)/interrupt.o  cpu/$(CPU)/cache.o
OBJS += cpu/$(CPU)/cplbhdlr.o   cpu/$(CPU)/cplbmgr.o   cpu/$(CPU)/flush.o
endif

LIBS  = lib_generic/libgeneric.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).a
ifdef SOC
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
       fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += rtc/librtc.a
LIBS += dtt/libdtt.a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a
LIBS += drivers/nand_legacy/libnand_legacy.a
LIBS += drivers/sk98lin/libsk98lin.a
LIBS += post/libpost.a post/cpu/libcpu.a
LIBS += common/libcommon.a
LIBS += $(BOARDLIBS)

3.4 子目录依赖追踪

源文件与头文件的变更需触发对应目标重建。GCC 提供 -M 系列选项自动生成依赖描述。U-Boot 通过 rules.mk 在每个子目录构建 .depend 文件:

#########################################################################
_depend: $(obj).depend

$(obj).depend:    $(src)Makefile $(TOPDIR)/config.mk $(SRCS)
    @rm -f $@
    @for f in $(SRCS); do \
        g=`basename $$f | sed -e 's/\(.*\)\.\w/\1.o/'`; \
        $(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; \
    done
#########################################################################

四、编译规则详解

4.1 顶层链接规则

all:          $(ALL_TARGETS)

$(obj)u-boot.hex:   $(obj)u-boot
    $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@

$(obj)u-boot.srec:   $(obj)u-boot
    $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@

$(obj)u-boot.bin:    $(obj)u-boot
    $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

$(obj)u-boot.img:   $(obj)u-boot.bin
    ./tools/mkimage -A $(ARCH) -T firmware -C none \
    -a $(TEXT_BASE) -e 0 \
    -n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
        sed -e 's/"[      ]*$$/ for $(BOARD) board"/') \
    -d $< $@

$(obj)u-boot.dis:    $(obj)u-boot
    $(OBJDUMP) -d $< > $@

$(obj)u-boot:         depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
    UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
    cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
        --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
        -Map u-boot.map -o u-boot

$(OBJS):
    $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

$(LIBS):
    $(MAKE) -C $(dir $(subst $(obj),,$@))

$(SUBDIRS):
    $(MAKE) -C $@ all

$(NAND_SPL):     version
    $(MAKE) -C nand_spl/board/$(BOARDDIR) all

$(U_BOOT_NAND):     $(NAND_SPL) $(obj)u-boot.bin
    cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin

version:
    @echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \
    echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \
    echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \
        $(TOPDIR)) >> $(VERSION_FILE); \
    echo "\"" >> $(VERSION_FILE)

depend dep:
    for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done

$(obj)System.map: $(obj)u-boot
    @$(NM) $< | \
    grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.ng$$\)\|\(LASH[RL]DI\)' | \
    sort > $(obj)System.map

4.2 子目录编译规则

模式规则定义于根目录 config.mk

$(obj)%.s:      %.S
    $(CPP) $(AFLAGS) -o $@ $<

$(obj)%.o:      %.S
    $(CC) $(AFLAGS) -c -o $@ $<

$(obj)%.o:      %.c
    $(CC) $(CFLAGS) -c -o $@ $<

静态库归档规则在子目录 Makefile 中定义,以 board/smdk2410/libsmdk2410.a 为例:

$(ARCHIVE):   $(obj).depend $(COBJS) $(SOBJS)
    $(AR) $(ARFLAGS) $@ $(COBJS) $(SOBJS)

关键提示: config.mk 是理解编译选项、链接参数及生成规则的核心文件,分析时需重点关注。

五、完整构建流程

5.1 解析逻辑

生成 u-boot.bin 需先构建其依赖的各库文件与目标文件,这要求递归进入各子目录执行本地 Makefile,由此确定全局编译顺序。

5.2 执行顺序

首先依据依赖声明的先后次序,逐层进入子目录完成编译:

$(OBJS):  
    $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

$(LIBS):  
    $(MAKE) -C $(dir $(subst $(obj),,$@))

随后返回根目录,执行顶层链接规则,最终产出 u-boot.bin

六、构建特性总结

6.1 递归编译机制

通过 $(MAKE) -C 进入子目录触发独立 Makefile 执行,实现模块化并行构建。

6.2 子目录 Makefile 的复用设计

子目录 Makefile 通过 include 引入根目录 config.mkrules.mk,保持规则统一的同时降低维护成本。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。