第十一章 U-boot 顶层 Makefile 详解 (make 过程)

本文详细解读了如何使用make编译U-Boot,介绍了配置过程中的变量、目标文件生成以及编译流程,重点讨论了ALL-y目标和不同配置项对最终二进制文件的影响。
摘要由CSDN通过智能技术生成

11.2.15 make 过程

        配置好 uboot 以后就可以直接 make 编译了,因为没有指明目标,所以会使用默认目标,主Makefile 中的默认目标如下:

        目标_all 又依赖于 all

        如 果 KBUILD_EXTMOD 为空的话_all依赖于all。这里不编译模块 , 所以KBUILD_EXTMOD 肯定为空,_all 的依赖就是 all。在主 Makefile 中 all 目标规则如下:

         从 960 行可以看出,all 目标依赖$(ALL-y),而在顶层 Makefile 中,ALL-y 如下:

# Always append ALL so that arch config.mk's can add custom ones
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check

ALL-$(CONFIG_ONENAND_U_BOOT) += u-boot-onenand.bin
ifeq ($(CONFIG_SPL_FSL_PBL),y)
ALL-$(CONFIG_RAMBOOT_PBL) += u-boot-with-spl-pbl.bin
else
ifneq ($(CONFIG_SECURE_BOOT), y)
# For Secure Boot The Image needs to be signed and Header must also
# be included. So The image has to be built explicitly
ALL-$(CONFIG_RAMBOOT_PBL) += u-boot.pbl
endif
endif
ALL-$(CONFIG_SPL) += spl/u-boot-spl.bin
ifeq ($(CONFIG_MX6)$(CONFIG_IMX_HAB), yy)
ALL-$(CONFIG_SPL_FRAMEWORK) += u-boot-ivt.img
else
ifeq ($(CONFIG_MX7)$(CONFIG_IMX_HAB), yy)
ALL-$(CONFIG_SPL_FRAMEWORK) += u-boot-ivt.img
else
ALL-$(CONFIG_SPL_FRAMEWORK) += u-boot.img
endif
endif
ALL-$(CONFIG_TPL) += tpl/u-boot-tpl.bin
ALL-$(CONFIG_OF_SEPARATE) += u-boot.dtb
ifeq ($(CONFIG_SPL_FRAMEWORK),y)
ALL-$(CONFIG_OF_SEPARATE) += u-boot-dtb.img
endif
ALL-$(CONFIG_OF_HOSTFILE) += u-boot.dtb
ifneq ($(CONFIG_SPL_TARGET),)
ALL-$(CONFIG_SPL) += $(CONFIG_SPL_TARGET:"%"=%)
endif
ALL-$(CONFIG_REMAKE_ELF) += u-boot.elf
ALL-$(CONFIG_EFI_APP) += u-boot-app.efi
ALL-$(CONFIG_EFI_STUB) += u-boot-payload.efi

ifneq ($(BUILD_ROM)$(CONFIG_BUILD_ROM),)
ALL-$(CONFIG_X86_RESET_VECTOR) += u-boot.rom
endif

# Build a combined spl + u-boot image for sunxi
ifeq ($(CONFIG_ARCH_SUNXI)$(CONFIG_SPL),yy)
ALL-y += u-boot-sunxi-with-spl.bin
endif

        从上述代码可以看出,ALL-y 包含 u-boot.srec、u-boot.bin、u-boot.sym、System.map、u-boot.cfg 和 binary_size_check 这几个文件。根据 uboot 的配置情况也可能包含其他的文件,比如说:
                 ALL-$(CONFIG_ONENAND_U_BOOT) += u-boot-onenand.bin

        CONFIG_ONENAND_U_BOOT 就是 uboot 中跟 ONENAND 配置有关的,如果我们使能ONENAND,那么在.config 配置文件中就会有“CONFIG_ONENAND_U_BOOT=y”这一句。相
当于 CONFIG_ONENAND_U_BOOT 是个变量,这个变量的值为“y”,所以展开以后就是:
                                                        ALL-y += u-boot-onenand.bin
        这个就是.config 里面的配置参数的含义,这些参数其实都是变量,后面跟着变量值,会在顶层 Makefile 或者其他 Makefile 中调用这些变量。
        ALL-y 里面有个 u-boot.bin,这个就是我们最终需要的 uboot 二进制可执行文件,所作的所
有工作就是为了它。在顶层 Makefile 中找到 u-boot.bin 目标对应的规则,如下所示:

1092 ifeq ($(CONFIG_MULTI_DTB_FIT),y)
......
1133 else ifeq ($(CONFIG_OF_SEPARATE),y)
1134 u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
1135     $(call if_changed,cat)
1136
1137 u-boot.bin: u-boot-dtb.bin FORCE
1138     $(call if_changed,copy)
1139 else
1140 u-boot.bin: u-boot-nodtb.bin FORCE
1141     $(call if_changed,copy)
1142 endif

        第 1133 行判断 CONFIG_OF_SEPARATE 是否等于 y,如果相等,那条件就成。,在include/config/auto.conf 文件中 CONFIG_OF_SEPARAT=y,所以条件成立。
        第 1134 行就是目标 u-boot-dtb.bin 的规则,目标 u-boot-dtb.bin 依赖于 u-boot-nodtb.bin,命令为$(call if_changed,copy),
        第 1137 行就是目标 u-boot.bin 的规则,目标 u-boot.bin 依赖于 u-boot-dtb.bin , 他们都是用了if_changed , if_changed是一个函数,这个函数在scripts/Kbuild.include 中有定义,而顶层 Makefile 中会包含 scripts/Kbuild.include 文件,这个前面已经说过了。
        if_changed 在 Kbuild.include 中的定义如下:

227 ###
228 # if_changed - execute command if any prerequisite is newer
229 # than target, or command line has changed
230 # if_changed_dep
231 #
- as if_changed, but uses fixdep to reveal
dependencies including used config symbols
232 # if_changed_rule - as if_changed but execute rule instead
233 # See Documentation/kbuild/makefiles.txt for more info
234 ......
256 # Execute command if command has changed or prerequisite(s) are
257 # updated.
258 if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
259 @set -e;                                \
260 $(echo-cmd) $(cmd_$(1));                    \
261 printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd) 

        第 227 行为 if_changed 的描述,根据描述,在一些先决条件比目标新的时候,或者命令行有改变的时候,if_changed 就会执行一些命令。
        第 257 行就是函数 if_changed,if_changed 函数引用的变量比较多,也比较绕,我们只需要
知道它可以从 u-boot-nodtb.bin 生成 u-boot.bin 就行了。
        既然 u-boot.bin 依赖于 u-boot-dtb.bin,u-boot-dtb.bin 又依赖于 u-boot-nodtb.bin,那么肯定要先生成 u-boot-nodtb.bin 文件,顶层 Makefile 中相关代码如下

1235 u-boot-nodtb.bin: u-boot FORCE
1236 $(call if_changed,objcopy)
1237 $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
1238 $(BOARD_SIZE_CHECK)

        目标 u-boot-nodtb.bin 又依赖于 u-boot,顶层 Makefile 中 u-boot 相关规则如下:

1670 u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
1671     +$(call if_changed,u-boot__)
1672 ifeq ($(CONFIG_KALLSYMS),y)
1673     $(call cmd,smap)
1674     $(call cmd,u-boot__) common/system_map.o
1675 endif

         目标 u-boot 依赖于 u-boot_init、u-boot-main 和 u-boot.lds,u-boot_init 和 u-boot-main 是两个变量,在顶层 Makefile 中有定义,值如下:

781 u-boot-init := $(head-y)
782 u-boot-main := $(libs-y)

        $(head-y)跟 CPU 架构有关,我们使用的是 ARM 芯片,所以 head-y 在 arch/arm/Makefile 中
被指定为:
                        head-y := arch/arm/cpu/$(CPU)/start.o
        根据 11.2.12 小节的分析,我们知道 CPU=armv7,因此 head-y 展开以后就是:
                        head-y := arch/arm/cpu/armv7/start.o
        因此:
                        u-boot-init= arch/arm/cpu/armv7/start.o
        $(libs-y)在顶层 Makefile 中被定义为 uboot 所有子目录下 build-in.o 的集合,代码如下:

724 libs-y += lib/
725 libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/
726 libs-$(CONFIG_OF_EMBED) += dts/
727 libs-y += fs/
728 libs-y += net/
729 libs-y += disk/
730 libs-y += drivers/
731 libs-y += drivers/dma/
732 libs-y += drivers/gpio/
733 libs-y += drivers/i2c/
734 libs-y += drivers/net/
735 libs-y += drivers/net/phy/
736 libs-y += drivers/power/ \
......
761 libs-y += cmd/
762 libs-y += common/
763 libs-y += env/
764 libs-$(CONFIG_API) += api/
765 libs-$(CONFIG_HAS_POST) += post/
766 libs-$(CONFIG_UNIT_TEST) += test/ test/dm/
767 libs-$(CONFIG_UT_ENV) += test/env/
768 libs-$(CONFIG_UT_OPTEE) += test/optee/
769 libs-$(CONFIG_UT_OVERLAY) += test/overlay/
770
771 libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/)
772
773 libs-y := $(sort $(libs-y))
774
775 u-boot-dirs := $(patsubst %/,%,$(filter %/, $(libs-y))) tools
                examples
776
777 u-boot-alldirs := $(sort $(u-boot-dirs)
                    $(patsubst %/,%,$(filter %/, $(libs-))))
778
779 libs-y := $(patsubst %/, %/built-in.o, $(libs-y))

        从上面的代码可以看出,libs-y 都是 uboot 各子目录的集合,最后:
                libs-y := $(patsubst %/, %/built-in.o, $(libs-y))
        这里调用了函数 patsubst,将 libs-y 中的“/”替换为”/built-in.o”,比如“drivers/dma/”就变为了“drivers/dma/built-in.o”,相当于将 libs-y 改为所有子目录中 built-in.o 文件的集合。那么 u-boot-main 就等于所有子目录中 built-in.o 的集合。
        这个规则就相当于将以 u-boot.lds 为链接脚本,将 arch/arm/cpu/armv7/start.o 和各个子目录
下的 built-in.o 链接在一起生成 u-boot。
        u-boot.lds 的规则如下:

1821 u-boot.lds: $(LDSCRIPT) prepare FORCE
1822 $(call if_changed_dep,cpp_lds)

        接下来的重点就是各子目录下的 built-in.o 是怎么生成的,以 drivers/gpio/built-in.o 为例,在drivers/gpio/目录下会有个名为.built-in.o.cmd 的文件,此文件内容如下:

cmd_drivers/gpio/built-in.o :=arm-none-linux-gnueabihf-ld.bfd-r
-o drivers/gpio/built-in.o drivers/gpio/gpio-uclass.o
drivers/gpio/stm32_gpio.o

        从命令“cmd_drivers/gpio/built-in.o”可以看出,drivers/gpio/built-in.o 这个文件是使用 ld 命令由文件 drivers/gpio/gpio-uclass.o 和 drivers/gpio/stm32_gpio.o 生成而来的。
        stm32_gpio.o 是stm32_gpio.c 编译生成的.o 文件,这个是 ST 的 STM32 系列的 GPIO 驱动文件。这里用到了 ld的“-r”参数,参数含义如下:
        -r –relocateable: 产生可重定向的输出,比如,产生一个输出文件它可再次作为‘ld’的输
入,这经常被叫做“部分链接”,当我们需要将几个小的.o 文件链接成为一个.o 文件的时候,需
要使用此选项。

        最终将各个子目录中的 built-in.o 文件链接在一起就形成了 u-boot,使用如下命令编译 uboot
就可以看到链接的过程。

make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- stm32mp157d_atk_defconfig V=1
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- DEVICE_TREE=stm32mp157d-atk all V=1

编译的时候会有如图所示内容输出 :      

将其整理一下,内容如下:

arm-none-linux-gnueabihf-ld.bfd -pie --gc-sections -Bstatic
0xC0100000 -o u-boot -T u-boot.lds
arch/arm/cpu/armv7/start.o --start-group
arch/arm/cpu/built-in.o
arch/arm/cpu/armv7/built-in.o
arch/arm/lib/built-in.o
arch/arm/mach-stm32mp/built-in.o
board/st/common/built-in.o
board/st/stm32mp1/built-in.o
cmd/built-in.o
common/built-in.o
disk/built-in.o
drivers/built-in.o
drivers/dma/built-in.o
drivers/gpio/built-in.o
drivers/i2c/built-in.o
drivers/net/built-in.o
drivers/net/phy/built-in.o
drivers/power/built-in.o
drivers/power/battery/built-in.o
drivers/power/domain/built-in.o
drivers/power/fuel_gauge/built-in.o
drivers/power/mfd/built-in.o
drivers/power/pmic/built-in.o
drivers/power/regulator/built-in.o
drivers/serial/built-in.o
drivers/spi/built-in.o
drivers/usb/cdns3/built-in.o
drivers/usb/common/built-in.o
drivers/usb/dwc3/built-in.o
drivers/usb/emul/built-in.o
drivers/usb/eth/built-in.o
drivers/usb/gadget/built-in.o
drivers/usb/gadget/udc/built-in.o
drivers/usb/host/built-in.o
drivers/usb/musb-new/built-in.o
drivers/usb/musb/built-in.o
drivers/usb/phy/built-in.o
drivers/usb/ulpi/built-in.o
env/built-in.o
fs/built-in.o
lib/built-in.o
net/built-in.o --end-group
arch/arm/lib/eabi_compat.o
arch/arm/lib/lib.a

        可以看出最终是用 arm-none-linux-gnueabihf-ld.bfd 命令将 arch/arm/cpu/armv7/start.o 和其他众多的 built_in.o 链接在一起,形成 u-boot。
        目标all除了u-boot.bin 外还有其他的依赖,比如 u-boot.srec、u-boot.sym 、System.map、
u-boot.cfg 和 binary_size_check 等等,这些依赖的生成方法和 u-boot.bin 很类似,大家自行查看
一下顶层 Makefile,我们就不详细的讲解了。

        总结一下“make”命令的流程,如图 11.2.15.2 所示:

      上图就是“make”命令的执行流程,关于 uboot 的顶层 Makefile 就分析到这里,重点是“make xxx_defconfig”和“make”这两个命令的执行流程:
        make xxx_defconfig:用于配置 uboot,这个命令最主要的目的就是生成.config 文件。
        make:用于编译 uboot,这个命令的主要工作就是生成二进制的 u-boot.bin 文件和其他的一些与 uboot 有关的文件,比如 u-boot.stm32 等等。
        关于 uboot 的顶层 Makefile 就分析到这里,有些内容我们没有详细、深入的去研究,因为我们的重点是使用 uboot,而不是 uboot 的研究者,我们要做的是缕清 uboot 的流程。至于更具体的实现,有兴趣的可以参考一下其他资料。

  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值