UBOOT编译--- UBOOT全部目标的编译过程详解(九)

2022-12-19,,,,

1. 前言

 UBOOT版本:uboot2018.03,开发板myimx8mmek240。

2. 概述

本文接续上篇文章,采用自下而上的方法,先从最原始的依赖开始,一步一步,执行命令生成目标。这里先把上节所有依赖关系再次列在这里:

                                                                                              --------------------------------------------|
| arch/arm/cpu \ $(u-boot-dirs)|
arch/arm/cpu/built-in.o \ | arch/arm/cpu/armv8 \ 的值 |
arch/arm/cpu/armv8/built-in.o \ | arch/arm/lib \ |
arch/arm/lib/built-in.o \ | arch/arm/mach-imx \ |
arch/arm/mach-imx/built-in.o \ | board/myzr/common \ |
board/myzr/common/built-in.o \ | board/myzr/myimx8mm \ |
board/myzr/myimx8mm/built-in.o \ | cmd \ |
cmd/built-in.o \ | common \ |
common/built-in.o \ | disk \ |
disk/built-in.o \ | drivers \ |
drivers/built-in.o \ | drivers/dma \ |
drivers/dma/built-in.o \ | drivers/gpio \ |
drivers/gpio/built-in.o \ | drivers/i2c \ |
include/config/auto.conf scripts_basic drivers/i2c/built-in.o \ | drivers/mtd \ |
\ / drivers/mtd/built-in.o \ | drivers/mtd/onenand \ |
\ / drivers/mtd/onenand/built-in.o \ | drivers/mtd/spi \ |
\ / drivers/mtd/spi/built-in.o \ | drivers/net \ |
scripts prepare drivers/net/built-in.o \ | drivers/net/phy \ |
----- ----- drivers/net/phy/built-in.o \ | . |
\ / . | . |
\ / . | . |
\ / . | env \ |
\ / env/built-in.o \ | fs \ |
$(u-boot-dirs) fs/built-in.o \ | lib \ |
------------------------- lib/built-in.o \ | net \ |
| \ net/built-in.o \ | test \ |
| \ test/built-in.o \ | test/dm |
| 依赖 \ test/dm/built-in.o ---------------------------------------------
| \ || include/config/uboot.release
| \ || arch/arm/cpu/armv8/u-boot.lds |
| \ || || outputmakefile prepare3
$(u-boot-init)==$(head-y) $(u-boot-main)== u-boot.lds FORCE \ /
arch/arm/cpu/armv8/start.o $(libs-y) / / \ /
\ | / / \ /
\ | / / \ /
\ | / / \ / include/generated/version_autogenerated.h include/generated/timestamp_autogenerated.h
\ | / / \ / | /
\ | / / \ / | /
---------------------------------------------- prepare2 $(version_h) $(timestamp_h) include/config/auto.conf(auto.conf里去掉了.config中的注释项目以及空格行,其它的都一样)
| \ \ / /
| \ \ / /
u-boot \ \ / /
--------------------------------------------------------------------------------------- \ \ / /
/ / \ \ \ -------------------------------------------
/ / \ \ \ prepare1 scripts_basic
/ | \ \ \ ------- -------------
| | | | | \ /
| dts/dt.dtb | | | \ /
| -------------------------- | | | -----------------------------------------
| / / | | | | archprepare
u-boot-nodtb.bin / / | | | | ----------
--------------- / / | | | | |
| \ \ / / | | | | |
| \ \/ / | | | | prepare0
| \ /\ / | | | | --------
| \ / \ / | | | | |
| \/ \-------\ / | | | | |
| /\ \ / | | | | tools prepare
| / \ \ / | | | | ----- -----
| / \ \/ | | | | \ /
u-boot-dtb.bin \ /\ | | | | \ /
-------------- \ / \-----------\ | | | | \ /
| \ / \ | | | | \ /
| \ / \| | | | spl/u-boot-spl
| \ / \ | | | --------------
| \ / |\--------------| | | | |
| \/ | | | | | |
u-boot.bin u-boot.img/u-boot-dtb.img u-boot.dtb binary_size_check u-boot.srec u-boot.sym System.map spl/u-boot-spl.bin
---------- -------------------------- --------- ----------------- ----------- --------- -------- -----------------
/ | | | | | | | |
/ | | | | | | | |
/ | | | | | | | |
u-boot.elf | | | | | | | |
--------- | | | | | | | |
\ | | | | | | | |
\ | | | | | | | |
\ \ \ | / / / / / include/config.h
\ \ \ | / / / / / |
\ \ \ | / / / / / |
\ \ \ | / / / / / |
\ \ \ | / / / / / |
\ \ \ | / / / / / u-boot.cfg
\ \ \ | / / / / / |
\ \ \ | / / / / / |
---------------------------------------------------------------------------------------------------------- |
$(ALL-y) cfg
\ /
\ /
\ /
-----------------------------------------------------------------------------------------------------
all
|
_all

3. 构建include/config/auto.conf

参见:UBOOT编译--- include/config/auto.conf、 include/config/auto.conf.cmd、 include/generated/autoconf.h (二)。auto.conf 和 .config 的差别是:auto.conf 里去掉了 .config 中的注释项目以及空格行,其它的都一样。

4. 构建scripts_basic 、scripts

4.1 构建scripts_basic

参见:UBOOT编译--- make xxx_deconfig过程详解(一) - 4.1 依赖 scripts_basic。

4.2 构建scripts

<<<<<<<<<顶层Makefile>>>>>>>>>
# ===========================================================================
# Build targets only - this includes vmlinux, arch specific targets, clean
# targets and others. In general all targets except *config targets. # Additional helpers built in scripts/
# Carefully list dependencies so we do not try to build scripts twice
# in parallel
PHONY += scripts
scripts: scripts_basic include/config/auto.conf
$(Q)$(MAKE) $(build)=$(@) //规则

规则展开后为:make -f $(srctree)/scripts/Makefile.build obj=scripts。未指定目标,采用Makefile.build中的默认目标__build。且在Makefile.build中会引用scripts目录下的Makefile

4.2.1 make -f $(srctree)/scripts/Makefile.build obj=scripts分析

# <<<<<<<<<scripts/Makefile.build>>>>>>>>>
PHONY := __build
__build: # The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) // scripts/
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) // scripts/Makefile
include $(kbuild-file) (1)//include scripts/Makefile ...... include scripts/Makefile.lib //要关注这个引用位置 在include $(kbuild-file)之后.下面第6项会用来解析subdir-ym ...... ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a (2)lib-target :=
endif ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)//去空格函数—strip
builtin-target := $(obj)/built-in.o (3) builtin-target :=
endif modorder-target := $(obj)/modules.order # We keep a list of all modules in $(MODVERDIR) __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@: # Descending
# ---------------------------------------------------------------------------
PHONY += $(subdir-ym)
$(subdir-ym):
$(Q)$(MAKE) $(build)=$@

1. 引用scripts/Makefile

# <<<<<<<<<scripts/Makefile>>>>>>>>>
hostprogs-$(CONFIG_BUILD_BIN2C) += bin2c //CONFIG_BUILD_BIN2C未定义 always := $(hostprogs-y) //特别注意always定义的这个位置
//由于 Makefile中
//(1)使用“=”进行赋值,变量的值是整个makefile中最后被指定的值;
//(2)使用":="进行赋值,即根据当前位置进行赋值
// 因此这里的always只与hostprogs-$(CONFIG_BUILD_BIN2C)的定义有关,与后面的hostprogs-y无关
# The following hostprogs-y programs are only build on demand
hostprogs-y += docproc # These targets are used internally to avoid "is up to date" messages
PHONY += build_docproc
build_docproc: $(obj)/docproc
@: # Let clean descend into subdirs
subdir- += basic kconfig
subdir-$(CONFIG_DTC) += dtc //有定义

特别注意always定义的这个位置,由于CONFIG_BUILD_BIN2C未定义,always为空(注意always后面的hostprogs-y不生效)。此外CONFIG_DTC定义为y,因此subdir-y+= dtc。

2. lib-target的定义

ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a (2)lib-target :=
endif

$(lib-y) $(lib-m) $(lib-)在scripts/Makefile中均未定义,因此lib-target := 。

3. builtin-target的定义

ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)//去空格函数—strip
builtin-target := $(obj)/built-in.o (3) builtin-target :=
endif

$(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)在scripts/Makefile中均未定义,因此builtin-target:= 。

4. extra-y的定义

extra-y 在scripts/Makefile中未定义,因此extra-y:= 。

5. always的定义

在scripts/Makefile中always为空,因此always:= 。

6. subdir-ym的定义

# <<<<<<<<<scripts/Makefile.lib >>>>>>>>>
# Subdirectories we need to descend into subdir-ym := $(sort $(subdir-y) $(subdir-m)) // dtc ...... subdir-ym := $(addprefix $(obj)/,$(subdir-ym)) //scripts/dtc

subdir-ym定义在在scripts/Makefile.lib中,展开为scripts/dtc 。要想构建默认目标__build,要先构建依赖$ (subdir-ym)(即scripts/dtc)。$(subdir-ym)的规则也定义在Makefile.build中(递归调用),就是执行make -f ./scripts/Makefile.build obj=scripts/dtc

4.2.1.1 make -f ./scripts/Makefile.build obj=scripts/dtc分析

make -f ./scripts/Makefile.build obj=scripts/dtc由于未指定目标,采用Makefile.build中的默认目标__build

# <<<<<<<<<scripts/Makefile.build>>>>>>>>>
PHONY := __build
__build: # The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file) //include scripts/dtc/Makefile (1) ...... include scripts/Makefile.lib //要关注这个引用位置 在include $(kbuild-file)之后 (2) ...... # Do not include host rules unless needed
ifneq ($(hostprogs-y)$(hostprogs-m),)
include scripts/Makefile.host //要关注这个引用 (3)
endif ...... ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a (4)lib-target :=
endif ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)//去空格函数—strip
builtin-target := $(obj)/built-in.o (5) builtin-target :=
endif modorder-target := $(obj)/modules.order
...... __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:

1. 引用scripts/dtc目录下的Makefile

# <<<<<<<<<scripts/dtc/Makefile>>>>>>>>>

hostprogs-y	:= dtc
always := $(hostprogs-y) //always := dtc dtc-objs := dtc.o flattree.o fstree.o data.o livetree.o treesource.o \
srcpos.o checks.o util.o
dtc-objs += dtc-lexer.lex.o dtc-parser.tab.o # Source files need to get at the userspace version of libfdt_env.h to compile HOSTCFLAGS_DTC := -I$(src) -I$(src)/libfdt HOSTCFLAGS_checks.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_data.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_dtc.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_flattree.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_fstree.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_livetree.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_srcpos.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_treesource.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_util.o := $(HOSTCFLAGS_DTC) HOSTCFLAGS_dtc-lexer.lex.o := $(HOSTCFLAGS_DTC)
HOSTCFLAGS_dtc-parser.tab.o := $(HOSTCFLAGS_DTC) # dependencies on generated files need to be listed explicitly
$(obj)/dtc-lexer.lex.o: $(obj)/dtc-parser.tab.h # generated files need to be cleaned explicitly
clean-files := dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h # Added for U-Boot
subdir-$(CONFIG_PYLIBFDT) += pylibfdt //未定义CONFIG_PYLIBFDT

2. 引用scripts/Makefile.lib

# <<<<<<<<<scripts/Makefile.lib>>>>>>>>>
always := $(addprefix $(obj)/,$(always)) //always := scripts/dtc/dtc

3. 引用scripts/Makefile.host

# <<<<<<<<<scripts/Makefile.host>>>>>>>>>
__hostprogs := $(sort $(hostprogs-y) $(hostprogs-m)) //(1) = dtc # C executables linked based on several .o files
host-cmulti := $(foreach m,$(__hostprogs),\
$(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m)))) //(2) dtc # Object (.o) files compiled from .c files
host-cobjs := $(sort $(foreach m,$(__hostprogs),$($(m)-objs)))//(3) dtc-objs ...... __hostprogs := $(addprefix $(obj)/,$(__hostprogs)) //(4) scripts/dtc/dtc-objs
host-cmulti := $(addprefix $(obj)/,$(host-cmulti)) //(5) scripts/dtc/dtc
host-cobjs := $(addprefix $(obj)/,$(host-cobjs)) //(6) cripts/dtc/**.o ...... # Link an executable based on list of .o files, all plain c
# host-cmulti -> executable
quiet_cmd_host-cmulti = HOSTLD $@
cmd_host-cmulti = $(HOSTCC) $(HOSTLDFLAGS) -o $@ \
$(addprefix $(obj)/,$($(@F)-objs)) \
$(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) //(7) scripts/dtc/dtc的编译 == $(always)
$(host-cmulti): FORCE
$(call if_changed,host-cmulti)
$(call multi_depend, $(host-cmulti), , -objs) # Create .o file from a single .c file
# host-cobjs -> .o
quiet_cmd_host-cobjs = HOSTCC $@
cmd_host-cobjs = $(HOSTCC) $(hostc_flags) -c -o $@ $< //(6) 所有.o的编译
$(host-cobjs): $(obj)/%.o: $(src)/%.c FORCE
$(call if_changed_dep,host-cobjs)

4. lib-target的定义

ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a (2)lib-target :=
endif

$(lib-y) $(lib-m) $(lib-)在scripts/Makefile中均未定义,因此lib-target := 。

5. builtin-target的定义

ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)//去空格函数—strip
builtin-target := $(obj)/built-in.o (3) builtin-target :=
endif

$(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)在scripts/Makefile中均未定义,因此builtin-target:= 。

6. extra-y的定义

extra-y 在scripts/Makefile中未定义,因此extra-y:= 。

7. always的定义

在scripts/dtc/Makefil中定义为dtc,在scripts/Makefile.lib中添加目录前缀,最终为scripts/dtc/dtc。

8. subdir-ym的定义

subdir-ym为空。

综上,对于make -f ./scripts/Makefile.build obj=scripts/dtc:

KBUILD_BUILTIN为y,$(builtin-target) $(lib-target) $(extra-y)为空;
$(subdir-ym)为空;
$(always)为scripts/dtc/dtc;
展开为:
__build: $(always)
    @:

要想构建默认目标__build,要先构建依赖$ (always)(即scripts/dtc/dtc)。构建$ (always)等价于构建$(host-cmulti)

编译打印命令如下:

make -f ./scripts/Makefile.build obj=scripts
HOSTCC scripts/dtc/dtc.o
HOSTCC scripts/dtc/flattree.o
HOSTCC scripts/dtc/fstree.o
HOSTCC scripts/dtc/data.o
HOSTCC scripts/dtc/livetree.o
HOSTCC scripts/dtc/treesource.o
HOSTCC scripts/dtc/srcpos.o
HOSTCC scripts/dtc/checks.o
HOSTCC scripts/dtc/util.o
SHIPPED scripts/dtc/dtc-lexer.lex.c
SHIPPED scripts/dtc/dtc-parser.tab.h
HOSTCC scripts/dtc/dtc-lexer.lex.o
SHIPPED scripts/dtc/dtc-parser.tab.c
HOSTCC scripts/dtc/dtc-parser.tab.o
HOSTLD scripts/dtc/dtc

真实执行的完整命令如下:

make -f ./scripts/Makefile.build obj=scripts
make -f ./scripts/Makefile.build obj=scripts/dtc
cc -Wp,-MD,scripts/dtc/.dtc.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/dtc.o scripts/dtc/dtc.c
cc -Wp,-MD,scripts/dtc/.flattree.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/flattree.o scripts/dtc/flattree.c
cc -Wp,-MD,scripts/dtc/.fstree.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/fstree.o scripts/dtc/fstree.c
cc -Wp,-MD,scripts/dtc/.data.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/data.o scripts/dtc/data.c
cc -Wp,-MD,scripts/dtc/.livetree.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/livetree.o scripts/dtc/livetree.c
cc -Wp,-MD,scripts/dtc/.treesource.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/treesource.o scripts/dtc/treesource.c
cc -Wp,-MD,scripts/dtc/.srcpos.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/srcpos.o scripts/dtc/srcpos.c
cc -Wp,-MD,scripts/dtc/.checks.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/checks.o scripts/dtc/checks.c
cc -Wp,-MD,scripts/dtc/.util.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/util.o scripts/dtc/util.c
cat scripts/dtc/dtc-lexer.lex.c_shipped > scripts/dtc/dtc-lexer.lex.c
cat scripts/dtc/dtc-parser.tab.h_shipped > scripts/dtc/dtc-parser.tab.h
cc -Wp,-MD,scripts/dtc/.dtc-lexer.lex.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/dtc-lexer.lex.o scripts/dtc/dtc-lexer.lex.c
cat scripts/dtc/dtc-parser.tab.c_shipped > scripts/dtc/dtc-parser.tab.c
cc -Wp,-MD,scripts/dtc/.dtc-parser.tab.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/dtc-parser.tab.o scripts/dtc/dtc-parser.tab.c
cc -o scripts/dtc/dtc scripts/dtc/dtc.o scripts/dtc/flattree.o scripts/dtc/fstree.o scripts/dtc/data.o scripts/dtc/livetree.o scripts/dtc/treesource.o scripts/dtc/srcpos.o scripts/dtc/checks.o scripts/dtc/util.o scripts/dtc/dtc-lexer.lex.o scripts/dtc/dtc-parser.tab.o

综上,对于make -f $(srctree)/scripts/Makefile.build obj=scripts,默认目标__build:

KBUILD_BUILTIN为y,$(builtin-target) $(lib-target) $(extra-y)为空 ;
$(subdir-ym)为scripts/dtc;
$(always)为空;
展开为:
__build: scripts/dtc
    @:

5. 构建prepare、prepare0、prepare1、prepare2、prepare3、archprepare

# Things we need to do before we recursively start building the kernel
# or the modules are listed in "prepare".
# A multi level approach is used. prepareN is processed before prepareN-1.
# archprepare is used in arch Makefiles and when processed asm symlink,
# version.h and scripts_basic is processed / created. # Listed in dependency order
PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3 # prepare3 is used to check if we are building in a separate output directory,
# and if so do:
# 1) Check that make has not been executed in the kernel src $(srctree)
prepare3: include/config/uboot.release
ifneq ($(KBUILD_SRC),)
@$(kecho) ' Using $(srctree) as source for U-Boot'
$(Q)if [ -f $(srctree)/.config -o -d $(srctree)/include/config ]; then \
echo >&2 " $(srctree) is not clean, please run 'make mrproper'"; \
echo >&2 " in the '$(srctree)' directory.";\
/bin/false; \
fi;
endif # prepare2 creates a makefile if using a separate output directory
prepare2: prepare3 outputmakefile prepare1: prepare2 $(version_h) $(timestamp_h) \
include/config/auto.conf
ifeq ($(wildcard $(LDSCRIPT)),)
@echo >&2 " Could not find linker script."
@/bin/false
endif archprepare: prepare1 scripts_basic prepare0: archprepare FORCE
$(Q)$(MAKE) $(build)=. //规则 # All the preparing..
prepare: prepare0

伪目标prepare依赖prepare0;prepare0又依赖 archprepare、FORCE;archprepare又依赖prepare1和scripts_basic;prepare1又依赖于

repare2、$ (version_h) 、$ (timestamp_h) 、 include/config/auto.conf;prepare2又依赖prepare3和outputmakefile;prepare3又依赖include/config/uboot.release,如果KBUILD_SRC为空,没有规则。涉及的具体目标链接:

UBOOT编译--- UBOOT的$(version_h) $(timestamp_h)(七) - 3.1.1 依赖include/config/uboot.release;
UBOOT编译--- UBOOT的$(version_h) $(timestamp_h)(七)
UBOOT编译--- include/config/auto.conf、 include/config/auto.conf.cmd、 include/generated/autoconf.h (二);

除了prepare0有具体规则外,其它都是伪目标。

5.1 prepare0

规则为:

make -f ./scripts/Makefile.build obj=.

在Makefile.build会引用当前目录下的Kbuild(注意这个引用的不是Makefile):

# <<<<<<<<<scripts/Makefile.build>>>>>>>>>
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) // ./.
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)// ././Kbuild
include $(kbuild-file) (1)//include ././Kbuild
...... ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a (2)lib-target :=
endif ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)//去空格函数—strip
builtin-target := $(obj)/built-in.o (3) builtin-target :=
endif modorder-target := $(obj)/modules.order # We keep a list of all modules in $(MODVERDIR) __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:

1. 引用顶层目录Kbuild

# <<<<<<<<<Kbuild>>>>>>>>>
# 1) Generate generic-asm-offsets.h generic-offsets-file := include/generated/generic-asm-offsets.h always := $(generic-offsets-file) //include/generated/generic-asm-offsets.h
targets := lib/asm-offsets.s # We use internal kbuild rules to avoid the "is up to date" message from make
lib/asm-offsets.s: lib/asm-offsets.c FORCE
$(Q)mkdir -p $(dir $@)
$(call if_changed_dep,cc_s_c) $(obj)/$(generic-offsets-file): lib/asm-offsets.s FORCE
$(call filechk,offsets,__GENERIC_ASM_OFFSETS_H__) #####
# 2) Generate asm-offsets.h
# ifneq ($(wildcard $(srctree)/arch/$(ARCH)/lib/asm-offsets.c),)//成立
offsets-file := include/generated/asm-offsets.h
endif always += $(offsets-file) //include/generated/asm-offsets.h
targets += arch/$(ARCH)/lib/asm-offsets.s CFLAGS_asm-offsets.o := -DDO_DEPS_ONLY # We use internal kbuild rules to avoid the "is up to date" message from make
arch/$(ARCH)/lib/asm-offsets.s: arch/$(ARCH)/lib/asm-offsets.c FORCE
$(Q)mkdir -p $(dir $@)
$(call if_changed_dep,cc_s_c) $(obj)/$(offsets-file): arch/$(ARCH)/lib/asm-offsets.s FORCE
$(call filechk,offsets,__ASM_OFFSETS_H__)

2. lib-target的定义

ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a (2)lib-target :=
endif

$(lib-y) $(lib-m) $(lib-)在顶层目录Kbuild中均未定义,因此lib-target := 。

3. builtin-target的定义

ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)//去空格函数—strip
builtin-target := $(obj)/built-in.o (3) builtin-target :=
endif

$(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)在顶层目录Kbuild中均未定义,因此builtin-target:= 。

4. always的定义

在kbuild中定义为include/generated/asm-offsets.h和include/generated/asm-offsets.h 。

综上,对于默认目标__build:

KBUILD_BUILTIN为y,$(builtin-target) $(lib-target) $(extra-y)为空 ;
$(subdir-ym)为空(注意并不是所有的编译目标都是空);
$(always)为nclude/generated/asm-offsets.h、include/generated/asm-offsets.h;
展开为:
__build: include/generated/asm-offsets.h include/generated/asm-offsets.h
    @:

include/generated/asm-offsets.h 、include/generated/asm-offsets.h 的规则定义在顶层目录Kbuild中,如本小节(1)。

5.1.1 include/generated/generic-asm-offsets.h的编译(通用)

# <<<<<<<<<scripts/Kbuild.include:>>>>>>>>>
###
# filechk is used to check if the content of a generated file is updated.
# Sample usage:
# define filechk_sample
# echo $KERNELRELEASE
# endef
# version.h : Makefile
# $(call filechk,sample)
# The rule defined shall write to stdout the content of the new file.
# The existing file will be compared with the new one.
# - If no file exist it is created
# - If the content differ the new file is used
# - If they are equal no change, and no timestamp update
# - stdin is piped in from the first prerequisite ($<) so one has
# to specify a valid file as first prerequisite (often the kbuild file)
define filechk
$(Q)set -e; \
$(kecho) ' CHK $@'; \ //打印' CHK include/generated/generic-asm-offsets.h'
mkdir -p $(dir $@); \ //创建 include/generated/目录
$(filechk_$(1)) < $< > $@.tmp; \ //调用filechk_offsets < lib/asm-offsets.s > include/generated/generic-asm-offsets.h.tmp
if [ -r $@ ] && cmp -s $@ $@.tmp; then \ //如果存在目标,则比较更新,否则把$@.tmp更名为$@
rm -f $@.tmp; \
else \
$(kecho) ' UPD $@'; \
mv -f $@.tmp $@; \
fi
endef # <<<<<<<<<Kbuild>>>>>>>>>
#
# Kbuild for top-level directory of U-Boot
# This file takes care of the following:
# 1) Generate generic-asm-offsets.h
# 2) Generate asm-offsets.h # Default sed regexp - multiline due to syntax constraints
define sed-y
"s:[[:space:]]*\.ascii[[:space:]]*\"\(.*\)\":\1:; \
/^->/{s:->#\(.*\):/* \1 */:; \
s:^->\([^ ]*\) [\$$#]*\([-0-9]*\) \(.*\):#define \1 \2 /* \3 */:; \
s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \
s:->::; p;}"
endef # Use filechk to avoid rebuilds when a header changes, but the resulting file
# does not
define filechk_offsets
(set -e; \
echo "#ifndef $2"; \ //输出 #ifndef __GENERIC_ASM_OFFSETS_H__
echo "#define $2"; \ //输出 #define __GENERIC_ASM_OFFSETS_H__
echo "/*"; \ //输出 /*
echo " * DO NOT MODIFY."; \ //输出 * DO NOT MODIFY.
echo " *"; \ //输出 *
echo " * This file was generated by Kbuild"; \ //输出 * This file was generated by Kbuild
echo " */"; \ //输出 */
echo ""; \ //输出 空行
sed -ne $(sed-y); \ //使用正则表达式解析汇编中符合指定格式的行,并输出
echo ""; \ //输出 空行
echo "#endif" ) //输出 #endif
endef
# 1) Generate generic-asm-offsets.h generic-offsets-file := include/generated/generic-asm-offsets.h always := $(generic-offsets-file) //include/generated/generic-asm-offsets.h
targets := lib/asm-offsets.s # We use internal kbuild rules to avoid the "is up to date" message from make
lib/asm-offsets.s: lib/asm-offsets.c FORCE (1)
$(Q)mkdir -p $(dir $@) //创建lib目录
$(call if_changed_dep,cc_s_c) //把lib/asm-offsets.c编译成lib/asm-offsets.s $(obj)/$(generic-offsets-file): lib/asm-offsets.s FORCE (2)
$(call filechk,offsets,__GENERIC_ASM_OFFSETS_H__) //生成include/generated/generic-asm-offsets.h

1. 创建lib目录,把lib/asm-offsets.c编译成lib/asm-offsets.s;

2. 调用filechk生成include/generated/generic-asm-offsets.h:

(1)打印' CHK include/generated/generic-asm-offsets.h'

(2)创建 include/generated/目录

(3)调用filechk_offsets < lib/asm-offsets.s > include/generated/generic-asm-offsets.h.tmp

    ( 3.1) 输出 "#ifndef GENERIC_ASM_OFFSETS_H"

    ( 3.2) 输出 "#define GENERIC_ASM_OFFSETS_H"

    ( 3.3)输出 "/"

    ( 3.4)输出 " DO NOT MODIFY."

    ( 3.5)输出 ""

    ( 3.6)输出 " This file was generated by Kbuild"

    ( 3.7)输出 "*/"

    ( 3.8)输出 空行

    ( 3.9)使用正则表达式解析汇编lib/asm-offsets.s中符合指定格式的行(如下),并输出

    ( 3.10)输出 空行

    ( 3.12)输出 #endif

(4)如果已经存在目标文件include/generated/generic-asm-offsets.h,且比include/generated/generic-asm-offsets.h.tmp更新,则删除后者,保留前者;否则把include/generated/generic-asm-offsets.h.tmp更名为include/generated/generic-asm-offsets.h。

最终生成的include/generated/generic-asm-offsets.h文件如下:

5.1.2 include/generated/asm-offsets.h的编译(架构有关)

# <<<<<<<<<Kbuild>>>>>>>>>
# 2) Generate asm-offsets.h
# ifneq ($(wildcard $(srctree)/arch/$(ARCH)/lib/asm-offsets.c),)//成立
offsets-file := include/generated/asm-offsets.h
endif always += $(offsets-file) //include/generated/asm-offsets.h
targets += arch/$(ARCH)/lib/asm-offsets.s CFLAGS_asm-offsets.o := -DDO_DEPS_ONLY # We use internal kbuild rules to avoid the "is up to date" message from make
arch/$(ARCH)/lib/asm-offsets.s: arch/$(ARCH)/lib/asm-offsets.c FORCE (1)
$(Q)mkdir -p $(dir $@) //创建lib目录
$(call if_changed_dep,cc_s_c) //把arch/arm/asm-offsets.c编译成arch/arm/asm-offsets.s $(obj)/$(offsets-file): arch/$(ARCH)/lib/asm-offsets.s FORCE (2)
$(call filechk,offsets,__ASM_OFFSETS_H__)//include/generated/asm-offsets.h

1. 创建arch\arm\lib目录,把arch\arm\lib/asm-offsets.c编译成arch\arm\lib/asm-offsets.s;

2. 调用filechk生成include/generated/asm-offsets.h:

编译过程同上。

最终生成的include/generated/asm-offsets.h文件如下:

6. 构建$(u-boot-dirs)

关于$(u-boot-dirs)的定义在上一篇文中已经讲过,所包含全部需要编译的目录,这里以仅以arch/arm/cpu/armv8 为例,其它雷同。

#  <<<<<<<<<顶层Makefile>>>>>>>>>
u-boot-dirs := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples //目标 ...... # Handle descending into subdirectories listed in $(vmlinux-dirs)
# Preset locale variables to speed up the build process. Limit locale
# tweaks to this spot to avoid wrong language settings when running
# make menuconfig etc.
# Error messages still appears in the original language PHONY += $(u-boot-dirs)
$(u-boot-dirs): prepare scripts
$(Q)$(MAKE) $(build)=$@ //规则

$ (Q) $ (MAKE) $ (build)=$@这个命令,引入各个目录下的Kbuild或Makefile,依据Makefile.build中定义的规则构建目标。

● 如果定义了hostprogs-y,则引入Makefile.host,构建目标

● 如果定义了obj-y,依据Makefile.build中的规则构建各个.o文件,并最终链接成build-in.o文件

$(u-boot-dirs)的定义在上文中已经讲过:

arch/arm/cpu  \
arch/arm/cpu/armv8 \
arch/arm/lib \
arch/arm/mach-imx \
board/myzr/common \
board/myzr/myimx8mm \
cmd \
common \
disk \
drivers \
drivers/dma \
drivers/gpio \
drivers/i2c \
drivers/mtd \
drivers/mtd/onenand \
drivers/mtd/spi \
drivers/net \
drivers/net/phy \
.
.
.
env \
fs \
lib \
net \
test \
test/dm

下面以构建arch/arm/cpu/armv8为例:

6.1 构建arch/arm/cpu/armv8(举例)

arch/arm/cpu/armv8 : prepare scripts
$(Q)$(MAKE) $(build)=$@

其中 $ (build)定义在scripts/Kbuild.include 中,如下:

# <<<<<<<<<scripts/Kbuild.include>>>>>>>>>
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj

展开为

make -f scripts/basic/Makefile.build obj=arch/arm/cpu/armv8

在Makefile.build中,会引用要编译目录的Makefile(arch/arm/cpu/armv8/Makefile),这点在前文中多次讲到过:

# <<<<<<<<<scripts/Makefile.build>>>>>>>>>
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) //arch/arm/cpu/armv8/Makefile
include $(kbuild-file) (1)//include arch/arm/cpu/armv8/Makefile ...... ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a (2)lib-target :=
endif ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)//去空格函数—strip
builtin-target := $(obj)/built-in.o (3) builtin-target := arch/arm/cpu/armv8/built-in.o
endif modorder-target := $(obj)/modules.order # We keep a list of all modules in $(MODVERDIR) __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:

1. 引用arch/arm/cpu/armv8/目录下的Makefile

# <<<<<<<<<arch/arm/cpu/armv8/Makefile>>>>>>>>>
extra-y := start.o obj-y += cpu.o
ifndef CONFIG_$(SPL_TPL_)TIMER
obj-y += generic_timer.o
endif
obj-y += cache_v8.o
obj-y += exceptions.o
obj-y += cache.o
obj-y += tlb.o
obj-y += transition.o
obj-y += fwcall.o
obj-y += cpu-dt.o
obj-$(CONFIG_ARM_SMCCC) += smccc-call.o ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_ARMV8_SPIN_TABLE) += spin_table.o spin_table_v8.o
endif
obj-$(CONFIG_$(SPL_)ARMV8_SEC_FIRMWARE_SUPPORT) += sec_firmware.o sec_firmware_asm.o obj-$(CONFIG_FSL_LAYERSCAPE) += fsl-layerscape/
obj-$(CONFIG_S32V234) += s32v234/
obj-$(CONFIG_ARCH_ZYNQMP) += zynqmp/
obj-$(CONFIG_TARGET_HIKEY) += hisilicon/
obj-$(CONFIG_ARMV8_PSCI) += psci.o
obj-$(CONFIG_ARCH_SUNXI) += lowlevel_init.o
obj-$(CONFIG_XEN) += xen/

2. lib-target的定义

ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a (2)lib-target :=
endif

$(lib-y) $(lib-m) $(lib-)在arch/arm/cpu/armv8/Makefile中均未定义,因此lib-target := 。

3. builtin-target的定义

ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)//去空格函数—strip
builtin-target := $(obj)/built-in.o (3) builtin-target := arch/arm/cpu/armv8/built-in.o
endif

综上,对于默认目标__build:

KBUILD_BUILTIN为y,在arch/arm/cpu/armv8/Makefile中定义了obj-y,其它为空(注意,如果subdir-m不为空会继续遍历这些子目录)。所以builtin-target=arch/arm/cpu/armv8/built-in.o ,lib-targe=,extra-y=arch/arm/cpu/armv8/start.o ;
$(subdir-ym)为空(注意并不是所有的编译目标都是空);
$(always)为空;
展开为:
__build: arch/arm/cpu/armv8/built-in.o arch/arm/cpu/armv8/start.o
    @:

$ (builtin-target)(*/build-in.o)依赖$(obj-y),也就是在arch/arm/cpu/armv8/Makefile中定义的那些.o文件。因此要先构建%.o,再构建%/built-in.o:

# <<<<<<<<<scripts/Makefile.build>>>>>>>>>
cmd_link_o_target = $(if $(strip $(obj-y)),\
$(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \
$(cmd_secanalysis),\
rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@) $(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)

6.1.1 %.o文件的编译

.o文件使用如下规则构建(因为-f指定 scripts/Makefile.build,优先在该文件中寻找规则):

# <<<<<<<<<scripts/Makefile.build>>>>>>>>>
# C (.c) files
# The C file is compiled and updated dependency information is generated.
# (See cmd_cc_o_c + relevant part of rule_cc_o_c) quiet_cmd_cc_o_c = CC $(quiet_modtag) $@ ifndef CONFIG_MODVERSIONS //校验符号用,这里我编译时未定义
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< //重点关注 %.c --> %.o的具体编译命令 else
# When module versioning is enabled the following steps are executed:
# o compile a .tmp_<file>.o from <file>.c
# o if .tmp_<file>.o doesn't contain a __ksymtab version, i.e. does
# not export symbols, we just rename .tmp_<file>.o to <file>.o and
# are done.
# o otherwise, we calculate symbol versions using the good old
# genksyms on the preprocessed source and postprocess them in a way
# that they are usable as a linker script
# o generate <file>.o from .tmp_<file>.o using the linker to
# replace the unresolved symbols __crc_exported_symbol with
# the actual value of the checksum generated by genksyms cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<
cmd_modversions = \
if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \
$(call cmd_gensymtypes,$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
> $(@D)/.tmp_$(@F:.o=.ver); \
\
$(LD) $(LDFLAGS) -r -o $@ $(@D)/.tmp_$(@F) \
-T $(@D)/.tmp_$(@F:.o=.ver); \
rm -f $(@D)/.tmp_$(@F) $(@D)/.tmp_$(@F:.o=.ver); \
else \
mv -f $(@D)/.tmp_$(@F) $@; \
fi;
endif ...... define rule_cc_o_c
$(call echo-cmd,checksrc) $(cmd_checksrc) \
$(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \
$(cmd_modversions) \
$(call echo-cmd,record_mcount) \
$(cmd_record_mcount) \
scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > \
$(dot-target).tmp; \
rm -f $(depfile); \
mv -f $(dot-target).tmp $(dot-target).cmd
endef # Built-in and composite module parts
$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
$(call cmd,force_checksrc)
$(call if_changed_rule,cc_o_c) quiet_cmd_as_o_S = AS $(quiet_modtag) $@
cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< //重点关注 %.S --> %.o的具体编译命令 $(obj)/%.o: $(src)/%.S FORCE
$(call if_changed_dep,as_o_S)

其中CONFIG_MODVERSIONS 宏的作用,请参考: Linux内核编译 CONFIG_MODVERSIONS 作用。

执行cmd_cc_o_c定义的命令将.c编译成.o,执行cmd_as_o_S定义的命令将.S编译成.o。

编译打印命令如下:

  CC      arch/arm/cpu/armv8/cpu.o
CC arch/arm/cpu/armv8/generic_timer.o
CC arch/arm/cpu/armv8/cache_v8.o
AS arch/arm/cpu/armv8/exceptions.o
AS arch/arm/cpu/armv8/cache.o
AS arch/arm/cpu/armv8/tlb.o
AS arch/arm/cpu/armv8/transition.o
CC arch/arm/cpu/armv8/fwcall.o
CC arch/arm/cpu/armv8/cpu-dt.o
AS arch/arm/cpu/armv8/start.o

真实执行的完整命令如下:

/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -Wp,-MD,arch/arm/cpu/armv8/.cpu.o.d  -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include -Iinclude   -I./arch/arm/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -fshort-wchar -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -Werror=date-time -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8    -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(cpu)"  -D"KBUILD_MODNAME=KBUILD_STR(cpu)" -c -o arch/arm/cpu/armv8/cpu.o arch/arm/cpu/armv8/cpu.c
/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -Wp,-MD,arch/arm/cpu/armv8/.generic_timer.o.d -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -fshort-wchar -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -Werror=date-time -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8 -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(generic_timer)" -D"KBUILD_MODNAME=KBUILD_STR(generic_timer)" -c -o arch/arm/cpu/armv8/generic_timer.o arch/arm/cpu/armv8/generic_timer.c
/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -Wp,-MD,arch/arm/cpu/armv8/.cache_v8.o.d -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -fshort-wchar -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -Werror=date-time -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8 -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(cache_v8)" -D"KBUILD_MODNAME=KBUILD_STR(cache_v8)" -c -o arch/arm/cpu/armv8/cache_v8.o arch/arm/cpu/armv8/cache_v8.c
/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -Wp,-MD,arch/arm/cpu/armv8/.exceptions.o.d -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -D__ASSEMBLY__ -g -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8 -c -o arch/arm/cpu/armv8/exceptions.o arch/arm/cpu/armv8/exceptions.S
/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -Wp,-MD,arch/arm/cpu/armv8/.cache.o.d -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -D__ASSEMBLY__ -g -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8 -c -o arch/arm/cpu/armv8/cache.o
/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -Wp,-MD,arch/arm/cpu/armv8/.tlb.o.d -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -D__ASSEMBLY__ -g -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8 -c -o arch/arm/cpu/armv8/tlb.o
/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -Wp,-MD,arch/arm/cpu/armv8/.transition.o.d -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -D__ASSEMBLY__ -g -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8 -c -o arch/arm/cpu/armv8/transition.o
/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -Wp,-MD,arch/arm/cpu/armv8/.fwcall.o.d -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -fshort-wchar -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -Werror=date-time -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8 -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(fwcall)" -D"KBUILD_MODNAME=KBUILD_STR(fwcall)" -c -o arch/arm/cpu/armv8/fwcall.o
/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -Wp,-MD,arch/arm/cpu/armv8/.cpu-dt.o.d -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -fshort-wchar -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -Werror=date-time -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8 -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(cpu_dt)" -D"KBUILD_MODNAME=KBUILD_STR(cpu_dt)" -c -o arch/arm/cpu/armv8/cpu-dt.o
/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -Wp,-MD,arch/arm/cpu/armv8/.start.o.d -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -D__ASSEMBLY__ -g -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8 -c -o arch/arm/cpu/armv8/start.o

-wp,-MD 会生成相应的依赖文件。-fno-pic是用来生成位置有关代码。

6.1.2 %/build-in.o文件的编译

最后,将编译出来的.o文件使用cmd_link_o_target链接成arch/arm/cpu/armv8/build-in.o,注意参数-r 表示。

编译打印如下:

  LD      arch/arm/cpu/armv8/built-in.o

真实执行的完整命令如下:

/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-ld.bfd     -r -o arch/arm/cpu/armv8/built-in.o arch/arm/cpu/armv8/cpu.o arch/arm/cpu/armv8/generic_timer.o arch/arm/cpu/armv8/cache_v8.o arch/arm/cpu/armv8/exceptions.o arch/arm/cpu/armv8/cache.o arch/arm/cpu/armv8/tlb.o arch/arm/cpu/armv8/transition.o arch/arm/cpu/armv8/fwcall.o arch/arm/cpu/armv8/cpu-dt.o

注意链接参数-r' --relocateable' 产生可重定位的输出, 比如,产生一个输出文件它可再次作为'ld'的输入。这经常被叫做"部分连接"。

其他目录的的执行过程也是类似的,就不做进一步分析了

7. 构建$ (u-boot-init)

注意在编译arch/arm/cpu/armv8/目录时,同时生成了arch/arm/cpu/armv8/start.o,这是u-boot的依赖 $ (u-boot-init)==$(head-y)

8. 构建$(u-boot-dirs)

注意在编译$ (u-boot-dirs)时,同时生成了 */built-in.o ,这是u-boot的依赖 $ (u-boot-main)==$(libs-y)

9. 构建u-boot.lds

u-boot.lds的定义位于顶层Makefile中(参见UBOOT编译--- UBOOT编译过程目标依赖分析(八) - 5.3 依赖u-boot.lds)。上文已经讲过,如果没有定义LDSCRIPT和CONFIG_SYS_LDSCRIPT,则默认使用u-boot自带的lds文件,包括board/$ (BOARDDIR)和$ (CPUDIR)目录下定制的针对board或cpu的lds文件;如果没有定制的lds文件,则采用arch/$(ARCH)/cpu目录下默认的lds文件。针对我的开发板,u-boot.lds = arch/arm/cpu/armv8/u-boot.lds。

规则如下:

# <<<<<<<<<顶层config.mk>>>>>>>>>
quiet_cmd_cpp_lds = LDS $@
cmd_cpp_lds = $(CPP) -Wp,-MD,$(depfile) $(cpp_flags) $(LDPPFLAGS) \
-D__ASSEMBLY__ -x assembler-with-cpp -P -o $@ $< u-boot.lds: $(LDSCRIPT) prepare FORCE
$(call if_changed_dep,cpp_lds)

编译打印如下:

在这里插入代码片

真实执行的完整命令如下:

/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,./.u-boot.lds.d -D__KERNEL__ -D__UBOOT__   -D__ARM__           -fno-pic  -mstrict-align  -ffunction-sections -fdata-sections -fno-common -ffixed-r9    -fno-common -ffixed-x18 -pipe -march=armv8-a -mgeneral-regs-only -D__LINUX_ARM_ARCH__=8   -Iinclude   -I./arch/arm/include -include ./include/linux/kconfig.h  -nostdinc -isystem /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1/include -ansi -include ./include/u-boot/u-boot.lds.h -DCPUDIR=arch/arm/cpu/armv8  -D__ASSEMBLY__ -x assembler-with-cpp -P -o u-boot.lds arch/arm/cpu/armv8/u-boot.lds

编译选项'-E'、'-x'、-P(具体解释见本文末尾参考章节)。该编译过程主要是为了展开arch/arm/cpu/armv8/u-boot.lds中的头文件中的宏定义重点关注引用的<config.h>,这个头文件是我们在编译的过程中产生的include/config.h,里面又会引用其它头文件,都会递归展开并进行对应的宏替换。这里我截取了一部分,如下:

10. u-boot编译

# <<<<<<<<<顶层顶层Makefile>>>>>>>>>
# Rule to link u-boot
# May be overridden by arch/$(ARCH)/config.mk
quiet_cmd_u-boot__ ?= LD $@
cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
-T u-boot.lds $(u-boot-init) \
--start-group $(u-boot-main) --end-group \
$(PLATFORM_LIBS) -Map u-boot.map; \
$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) quiet_cmd_smap = GEN common/system_map.o
cmd_smap = \
smap=`$(call SYSTEM_MAP,u-boot) | \
awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \
$(CC) $(c_flags) -DSYSTEM_MAP="\"$${smap}\"" \
-c $(srctree)/common/system_map.c -o common/system_map.o u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
+$(call if_changed,u-boot__) //uboot的规则
ifeq ($(CONFIG_KALLSYMS),y) //未定义
$(call cmd,smap)
$(call cmd,u-boot__) common/system_map.o
endif

目标u-boot的依赖:$ (u-boot-init)、$ (u-boot-main)、u-boot.lds、FORCE在前面小节已经分析过。

/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-ld.bfd   -pie  --gc-sections -Bstatic  --no-dynamic-linker -Ttext 0x40200000 -o u-boot -T u-boot.lds arch/arm/cpu/armv8/start.o --start-group  arch/arm/cpu/built-in.o  arch/arm/cpu/armv8/built-in.o  arch/arm/lib/built-in.o  arch/arm/mach-imx/built-in.o  board/myzr/common/built-in.o  board/myzr/myimx8mm/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/mtd/built-in.o  drivers/mtd/onenand/built-in.o  drivers/mtd/spi/built-in.o  drivers/net/built-in.o  drivers/net/phy/built-in.o  drivers/pci/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  test/built-in.o  test/dm/built-in.o --end-group -L /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.3.1 -lgcc -Map u-boot.map;  true

-pie: 生成position-independent executable (ET_EXEC)。
-Bstatic:在-L指定的目录列表中查找 xxx.a
--no-dynamic-linker : Produce an executable with no program interpreter header
-T :指定链接脚本就是u-boot.lds
–start-group archives --end-group :正常情况,链接的时候库文件只会按它们出现在命令行的顺序搜索一遍,如果包里有未定义的引用标号,而且该包还被放在命令行的后面,这样链接器就无法解决该标号的引用问题。通过给包分组,这些包可以被循环搜索直到所有的引用都可以解决为止。使用该选项将降低性能。只有在无法避免多个包之间互相引用的情况下才使用。
-Ttext 0x40200000 ’-T’命令行选项只能用于设置 “text” 、“data” 和 “bss” 段的基址,这里是把text段基址设为0x40200000。
-Map FILE :Write a map file。主要是一些段的信息。

11. 构建include/config/uboot.release

参见:UBOOT编译--- UBOOT的$(version_h) $(timestamp_h)(七) - 3.1.1 依赖include/config/uboot.release。

12. 构建outputmakefile

参见:UBOOT编译--- make xxx_deconfig过程详解(一) - 4.2 依赖 outputmakefile。

13. 构建$(version_h)、 $(timestamp_h)

参见:UBOOT编译--- UBOOT的$(version_h) $(timestamp_h)(七)。

14. 构建tool

没什么好讲的,就是编译一些后面会使用的工具(tools/bmp_logo、 tools/gen_eth_addr、 tools/gen_ethaddr_crc、 tools/img2srec、 tools/mkenvimage 、tools/dumpimage 、tools/mkimage、 tools/proftool 、tools/relocate-rela 、tools/fdtgrep),编译规则参见前面的章节,套路是一模一样的。

15. cfg

# <<<<<<<<<scripts/Makefile.autoconf>>>>>>>>>
u-boot.cfg: include/config.h FORCE
$(call cmd,u_boot_cfg) # <<<<<<<<<顶层顶层Makefile>>>>>>>>>
u-boot.cfg spl/u-boot.cfg tpl/u-boot.cfg: include/config.h FORCE
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf $(@) //指定目标u-boot.cfg,定义在scripts/Makefile.autoconf中 cfg: u-boot.cfg

参见:UBOOT编译--- include/config.h、 include/autoconf.mk、include/autoconf.mk.dep、u-boot.cfg(三)。

16. u-boot-nodtb.bin

# <<<<<<<<<顶层Makefile>>>>>>>>>
u-boot-nodtb.bin: u-boot FORCE
$(call if_changed,objcopy)
$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
$(BOARD_SIZE_CHECK)

16.1 规则$(call if_changed,objcopy)

16.1.1 if_changed定义

# <<<<<<<<<scripts/Kbuild.include>>>>>>>>>
# Execute command if command has changed or prerequisite(s) are updated.
# //如果命令已更改或prerequisites已更新,请执行命令
if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
@set -e; \
$(echo-cmd) $(cmd_$(1)); \
printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)

1. $(strip $(any-prereq) $(arg-check) )

(1) any-prereq定义

# <<<<<<<<<scripts/Kbuild.include>>>>>>>>>
# Find any prerequisites that is newer than target or that does not exist.
# PHONY targets skipped in both cases.
//查找比目标更新或不存在的任何prerequisites。
any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^)//这里为u-boot

$ ? 表示所有比目标还要新的依赖文件;
$ ^ 表示所有的依赖文件;

$(filter-out $ (PHONY), $?)就是过滤掉比目标还要新的依赖文件中的伪目标;

$ (filter-out $ (PHONY) $ (wildcard $ ^ ), $^)表示过滤掉所有的依赖文件中的伪目标与存在的依赖文件。

(2) arg-check定义:

# <<<<<<<<<scripts/Kbuild.include>>>>>>>>>
ifneq ($(KBUILD_NOCMDDEP),1)
# Check if both arguments has same arguments. Result is empty string if equal.
# User may override this check using make KBUILD_NOCMDDEP=1
//检查两个参数是否具有相同的参数。如果相等,则结果为空字符串。用户可以使用make KBUILD\u NOCMDDEP=1覆盖此检查
arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \
$(filter-out $(cmd_$@), $(cmd_$(1))) )
else
arg-check = $(if $(strip $(cmd_$@)),,1)
endif

KBUILD_NOCMDDEP是在make命令行中定义,我们并没有定义,所以:

arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) $(filter-out $(cmd_$@), $(cmd_$(1))) )

$ (filter-out $ (cmd_ $ (1)), $ (cmd_ $@)) 表示过滤掉 $(cmd_ $@)中符合 $(cmd_ $(1))的项。 $(1)表示if_changed函数的第一个参数objcopy, $@表示目标文件u-boot-nodtb.bin。cmd_ $(1)为cmd_objcopy,cmd_ $@为cmd_u-boot-nodtb.bin。

cmd_u-boot-nodtb.bin并没有定义,所以 $(filter-out $(cmd_ $(1)), $(cmd_ $@))为空;
cmd_objcopy 在顶层Makefile中定义:

# <<<<<<<<<顶层Makefile>>>>>>>>>
# Normally we fill empty space with 0xff
quiet_cmd_objcopy = OBJCOPY $@
cmd_objcopy = $(OBJCOPY) --gap-fill=0xff $(OBJCOPYFLAGS) \
$(OBJCOPYFLAGS_$(@F)) $< $@

所以arg-check = $ (filter-out $ (cmd_$ @), $ (cmd_$ (1))) = $(OBJCOPY) --gap-fill=0xff $(OBJCOPYFLAGS)

$(OBJCOPYFLAGS_ $(@F)) $< $@。

因为 $(any-prereq) $(arg-check)都为非空,所以if_changed展开为:

if_changed = @set -e;   \ /如果任何语句的执行结果不是true则应该退出
$(echo-cmd) $(cmd_$(1)); \
printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)

16.1.2 $(echo-cmd) $(cmd_ $(1))等价于 $(echo-cmd) $(cmd_objcopy)

1. $(echo-cmd)定义

# <<<<<<<<<scripts/Kbuild.include>>>>>>>>>
# echo command.
# Short version is used, if $(quiet) equals `quiet_', otherwise full one.
echo-cmd = $(if $($(quiet)cmd_$(1)),\
echo ' $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';)

quiet=quiet_,在顶层Makefile分析过(当然如果你想看到更详细的打印,您可以通过传入V值,来改变), $(cmd_objcopy)上面分析过,存在,所以:

echo-cmd = echo '  $(call escsq,$(cmd_objcopy))$(echo-why)';

在scripts/Kbuild.include中:

# <<<<<<<<<scripts/Kbuild.include>>>>>>>>>
# Escape single quote for use in echo statements
escsq = $(subst $(squote),'\$(squote)',$1)
ifeq ($(KBUILD_VERBOSE),2)
why = \
$(if $(filter $@, $(PHONY)),- due to target is PHONY, \
$(if $(wildcard $@), \
$(if $(strip $(any-prereq)),- due to: $(any-prereq), \
$(if $(arg-check), \
$(if $(cmd_$@),- due to command line change, \
$(if $(filter $@, $(targets)), \
- due to missing .cmd file, \
- due to $(notdir $@) not in $$(targets) \
) \
) \
) \
), \
- due to target missing \
) \
) echo-why = $(call escsq, $(strip $(why)))
endif

KBUILD_VERBOSE一般我们会采用默认值0(需要调试编译除外),所以 echo-why 为空。

2. $ (cmd_$(1))定义

# <<<<<<<<<顶层Makefile>>>>>>>>>
# Normally we fill empty space with 0xff
quiet_cmd_objcopy = OBJCOPY $@
cmd_objcopy = $(OBJCOPY) --gap-fill=0xff $(OBJCOPYFLAGS) \
$(OBJCOPYFLAGS_$(@F)) $< $@ //$(OBJCOPYFLAGS_$(@F)) = OBJCOPYFLAGS_u-boot-nodtb.bin
//$(@F):表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o","$(@F)"相当于函数"$(notdir $@)"

(1) $(OBJCOPYFLAGS)定义在顶层config.mk和arch/ $(ARCH)/config.mk(arch/arm/config.mk)中

# <<<<<<<<<顶层config.mk>>>>>>>>>
OBJCOPYFLAGS := # <<<<<<<<<arch/arm/config.mk>>>>>>>>>
# limit ourselves to the sections we want in the .bin.
ifdef CONFIG_ARM64 //定义
OBJCOPYFLAGS += -j .text -j .secure_text -j .secure_data -j .rodata -j .data \
-j .u_boot_list -j .rela.dyn -j .got -j .got.plt \
-j .binman_sym_table
else
OBJCOPYFLAGS += -j .text -j .secure_text -j .secure_data -j .rodata -j .hash \
-j .data -j .got -j .got.plt -j .u_boot_list -j .rel.dyn \
-j .binman_sym_table
endif # if a dtb section exists we always have to include it
# there are only two cases where it is generated
# 1) OF_EMBEDED is turned on
# 2) unit tests include device tree blobs
OBJCOPYFLAGS += -j .dtb.init.rodata

(2) OBJCOPYFLAGS_u-boot-nodtb.bin定义在顶层Makefile中

# <<<<<<<<<顶层Makefile>>>>>>>>>
OBJCOPYFLAGS_u-boot-nodtb.bin := -O binary \
$(if $(CONFIG_X86_16BIT_INIT),-R .start16 -R .resetvec)

16.1.3 编译时具体打印

/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-objcopy --gap-fill=0xff  -j .text -j .secure_text -j .secure_data -j .rodata -j .data -j .u_boot_list -j .rela.dyn -j .got -j .got.plt -j .binman_sym_table -j .dtb.init.rodata -j .efi_runtime -j .efi_runtime_rel -O binary  u-boot u-boot-nodtb.bin

整个编译过程就是把u-boot中的指定段拷贝u-boot-nodtb.bin中。

objcopy的功能:将目标文件的一部分或者全部内容拷贝到另外一个目标文件中,或者实现目标文件的格式转换。

j sectionname , --only-section=sectionname : 只将由 sectionname 指定的 section 拷贝到输出文件,可以多次指定,并且注意如果使用不当会导致输出文件不可用。

-O bfdname :--output-target= bfdname 使用指定的格式来写输出文件(即目标文件),bfdname是BFD库中描述的标准格式名。

16.2 规则 $(call DO_STATIC_RELA, $ <, $@, $(CONFIG_SYS_TEXT_BASE))

# <<<<<<<<<顶层Makefile>>>>>>>>>
//静态应用RELA-style的重定位(目前仅 arm64)这对于需要在原始二进制文件上执行静态重定位的 arm64 很有用,但某些模拟器只接受 ELF 文件(但不执行重定位)。
# Statically apply RELA-style relocations (currently arm64 only)
# This is useful for arm64 where static relocation needs to be performed on
# the raw binary, but certain simulators only accept an ELF file (but don't
# do the relocation).
ifneq ($(CONFIG_STATIC_RELA),)
# $(1) is u-boot ELF, $(2) is u-boot bin, $(3) is text base
DO_STATIC_RELA = \
start=$$($(NM) $(1) | grep __rel_dyn_start | cut -f 1 -d ' '); \
end=$$($(NM) $(1) | grep __rel_dyn_end | cut -f 1 -d ' '); \
tools/relocate-rela $(2) $(3) $$start $$end
else
DO_STATIC_RELA =
endif

编译时具体打印:

start=$(/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-nm u-boot | grep __rel_dyn_start | cut -f 1 -d ' '); end=$(/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-nm u-boot | grep __rel_dyn_end | cut -f 1 -d ' '); tools/relocate-rela u-boot-nodtb.bin 0x40200000 $start $end

1. aarch64-linux-gnu-nm u-boot | grep __rel_dyn_start | cut -f 1 -d ' '

nm : 列出指定文件的符号表(下图只截取一点);

grep __rel_dyn_start :找到符号grep __rel_dyn_start

cut -f 1 -d ' ' :取出第1列,分隔符为' '

2. aarch64-linux-gnu-nm u-boot | grep __rel_dyn_end | cut -f 1 -d ' '

nm : 列出指定文件的符号表(下图只截取一点);

grep __rel_dyn_end:找到符号grep __rel_dyn_end

cut -f 1 -d ' ' :取出第1列,分隔符为' '

3. tools/relocate-rela u-boot-nodtb.bin 0x40200000 $start $end

重定位。

16.3 规则 $(BOARD_SIZE_CHECK)

# <<<<<<<<<顶层Makefile>>>>>>>>>
ifneq ($(CONFIG_BOARD_SIZE_LIMIT),)
BOARD_SIZE_CHECK = \
@actual=`wc -c $@ | awk '{print $$1}'`; \
limit=`printf "%d" $(CONFIG_BOARD_SIZE_LIMIT)`; \
if test $$actual -gt $$limit; then \
echo "$@ exceeds file size limit:" >&2 ; \
echo " limit: $$limit bytes" >&2 ; \
echo " actual: $$actual bytes" >&2 ; \
echo " excess: $$((actual - limit)) bytes" >&2; \
exit 1; \
fi
else
BOARD_SIZE_CHECK =
endif

如果有定义CONFIG_BOARD_SIZE_LIMIT,则检查编译出的目标文件是否超过最大值;如果没有定义,则此项为空。

17. dts/dt.dtb

# <<<<<<<<<顶层Makefile>>>>>>>>>
dts/dt.dtb: u-boot
$(Q)$(MAKE) $(build)=dts dtbs //规则

规则展开为:make -f $(srctree)/scripts/Makefile.build obj=dts dtbs。指定目标dtbs,在Makefile.build中会引用dts目录下的Makefile

# <<<<<<<<<scripts/Makefile.build>>>>>>>>>
PHONY := __build
__build: # The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) // dts/
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) // dts/Makefile
include $(kbuild-file) (1)//include dts/Makefile ...... include scripts/Makefile.lib // ......

dts目录下的Makefile内容有目标dtbs的定义:

# <<<<<<<<<dts/Makefile >>>>>>>>>
DEVICE_TREE ?= $(CONFIG_DEFAULT_DEVICE_TREE:"%"=%) //.config:193:CONFIG_DEFAULT_DEVICE_TREE="myimx8mmek240-8mm"
ifeq ($(DEVICE_TREE),)
DEVICE_TREE := unset
endif ARCH_PATH := arch/$(ARCH)/dts //arch/arm/dts
dtb_depends := arch-dtbs ifneq ($(EXT_DTB),)
DTB := $(EXT_DTB)
else
DTB := $(ARCH_PATH)/$(DEVICE_TREE).dtb //arch/arm/dts/myimx8mmek240-8mm.dtb
dtb_depends += $(DTB:.dtb=.dts) //dtb_depends += arch/arm/dts/myimx8mmek240-8mm.dts
endif $(obj)/dt-spl.dtb: $(DTB) $(objtree)/tools/fdtgrep FORCE //关注dts/dt-spl.dtb
$(call if_changed,fdtgrep) $(obj)/dt.dtb: $(DTB) FORCE //关注dts/dt.dtb
$(call if_changed,shipped) targets += dt.dtb dt-spl.dtb $(DTB): $(dtb_depends)
ifeq ($(EXT_DTB),)
$(Q)$(MAKE) $(build)=$(ARCH_PATH) $@
endif
$(Q)test -e $@ || ( \
echo >&2; \
echo >&2 "Device Tree Source is not correctly specified."; \
echo >&2 "Please define 'CONFIG_DEFAULT_DEVICE_TREE'"; \
echo >&2 "or build with 'DEVICE_TREE=<device_tree>' argument"; \
echo >&2; \
/bin/false) arch-dtbs:
$(Q)$(MAKE) $(build)=$(ARCH_PATH) dtbs .SECONDARY: $(obj)/dt.dtb.S $(obj)/dt-spl.dtb.S ifeq ($(CONFIG_SPL_BUILD),y)
obj-$(CONFIG_OF_EMBED) := dt-spl.dtb.o
# support "out-of-tree" build for dtb-spl
$(obj)/dt-spl.dtb.o: $(obj)/dt-spl.dtb.S FORCE
$(call if_changed_dep,as_o_S)
else
obj-$(CONFIG_OF_EMBED) := dt.dtb.o
endif dtbs: $(obj)/dt.dtb $(obj)/dt-spl.dtb //关注
@: clean-files := dt.dtb.S dt-spl.dtb.S # Let clean descend into dts directories
subdir- += ../arch/arm/dts ../arch/microblaze/dts ../arch/mips/dts ../arch/sandbox/dts ../arch/x86/dts
......

可以看到目标dtbs依赖于$(obj)/dt.dtb 和 $(obj)/dt-spl.dtb ,且这两个依赖又依赖于 $(DTB),

17.1 $(DTB)

ARCH_PATH := arch/$(ARCH)/dts //arch/arm/dts
dtb_depends := arch-dtbs ifneq ($(EXT_DTB),)
DTB := $(EXT_DTB)
else
DTB := $(ARCH_PATH)/$(DEVICE_TREE).dtb //arch/arm/dts/myimx8mmek240-8mm.dtb
dtb_depends += $(DTB:.dtb=.dts) //dtb_depends += arch/arm/dts/myimx8mmek240-8mm.dts
endif
$(DTB): $(dtb_depends)
ifeq ($(EXT_DTB),)
$(Q)$(MAKE) $(build)=$(ARCH_PATH) $@//make -f $(srctree)/scripts/Makefile.build obj=arch/arm/dts arch/arm/dts/myimx8mmek240-8mm.dtb
endif
$(Q)test -e $@ || ( \
echo >&2; \
echo >&2 "Device Tree Source is not correctly specified."; \
echo >&2 "Please define 'CONFIG_DEFAULT_DEVICE_TREE'"; \
echo >&2 "or build with 'DEVICE_TREE=<device_tree>' argument"; \
echo >&2; \
/bin/false) arch-dtbs:
$(Q)$(MAKE) $(build)=$(ARCH_PATH) dtbs//make -f $(srctree)/scripts/Makefile.build obj=arch/arm/dts dtbs

17.1.1 $(DTB)的依赖arch-dtbs和arch/arm/dts/myimx8mmek240-8mm.dts

1. arch-dtbs

规则就是执行:make -f $(srctree)/scripts/Makefile.build obj=arch/arm/dts dtbs命令。目标dtbs定义在arch/arm/dts/Makefile中

# <<<<<<<<<scripts/Makefile.build>>>>>>>>>
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) // arch/arm/dts
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) // arch/arm/dts /Makefile
include $(kbuild-file) //include arch/arm/dts /Makefile ...... include scripts/Makefile.lib //要关注这个引用。$(obj)/%.dtb定义在这里面
...... # <<<<<<<<<arch/arm/dts/Makefile >>>>>>>>>
dtb-$(CONFIG_AT91FAMILY) += at91sam9260-smartweb.dtb \
at91sam9g20-taurus.dtb \
at91sam9g45-corvus.dtb \
......
// $(dtb-y)为各种*.dtb的集合
...... PHONY += dtbs
dtbs: $(addprefix $(obj)/, $(dtb-y)) //dtbs: $(obj)/*.dtb
@:

这里dtbs又依赖于$(obj)/*.dtb。这个目标不是在arch/arm/dts/Makefile中定义,而是在scripts/Makefile.lib中

# <<<<<<<<<scripts/Makefile.lib >>>>>>>>>
quiet_cmd_dtc = DTC $@
# Modified for U-Boot
# Bring in any U-Boot-specific include at the end of the file
cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
(cat $<; $(if $(u_boot_dtsi),echo '\#include "$(u_boot_dtsi)"')) > $(pre-tmp); \
$(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $(pre-tmp) ; \
$(DTC) -O dtb -o $@ -b 0 \
-i $(dir $<) $(DTC_FLAGS) \
-d $(depfile).dtc.tmp $(dtc-tmp) ; \
cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile) $(obj)/%.dtb: $(src)/%.dts FORCE
$(call if_changed_dep,dtc)

没什么好讲的,就是调用scripts/dtc/dtc 工具生成对应的dtb文件,同时生成*.d文件。

具体编译过程如下:

make -f ./scripts/Makefile.build obj=arch/arm/dts dtbs
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/myimx8mek314-8mq.dts; ) > arch/arm/dts/.myimx8mek314-8mq.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.myimx8mek314-8mq.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.myimx8mek314-8mq.dtb.dts.tmp arch/arm/dts/.myimx8mek314-8mq.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/myimx8mek314-8mq.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.myimx8mek314-8mq.dtb.d.dtc.tmp arch/arm/dts/.myimx8mek314-8mq.dtb.dts.tmp ; cat arch/arm/dts/.myimx8mek314-8mq.dtb.d.pre.tmp arch/arm/dts/.myimx8mek314-8mq.dtb.d.dtc.tmp > arch/arm/dts/.myimx8mek314-8mq.dtb.d
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/myimx8mevk-8mq.dts; ) > arch/arm/dts/.myimx8mevk-8mq.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.myimx8mevk-8mq.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.myimx8mevk-8mq.dtb.dts.tmp arch/arm/dts/.myimx8mevk-8mq.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/myimx8mevk-8mq.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.myimx8mevk-8mq.dtb.d.dtc.tmp arch/arm/dts/.myimx8mevk-8mq.dtb.dts.tmp ; cat arch/arm/dts/.myimx8mevk-8mq.dtb.d.pre.tmp arch/arm/dts/.myimx8mevk-8mq.dtb.d.dtc.tmp > arch/arm/dts/.myimx8mevk-8mq.dtb.d
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/myimx8mek300-8mq.dts; ) > arch/arm/dts/.myimx8mek300-8mq.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.myimx8mek300-8mq.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.myimx8mek300-8mq.dtb.dts.tmp arch/arm/dts/.myimx8mek300-8mq.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/myimx8mek300-8mq.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.myimx8mek300-8mq.dtb.d.dtc.tmp arch/arm/dts/.myimx8mek300-8mq.dtb.dts.tmp ; cat arch/arm/dts/.myimx8mek300-8mq.dtb.d.pre.tmp arch/arm/dts/.myimx8mek300-8mq.dtb.d.dtc.tmp > arch/arm/dts/.myimx8mek300-8mq.dtb.d
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/myimx8mmek240-8mm.dts; ) > arch/arm/dts/.myimx8mmek240-8mm.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.myimx8mmek240-8mm.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.myimx8mmek240-8mm.dtb.dts.tmp arch/arm/dts/.myimx8mmek240-8mm.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/myimx8mmek240-8mm.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.myimx8mmek240-8mm.dtb.d.dtc.tmp arch/arm/dts/.myimx8mmek240-8mm.dtb.dts.tmp ; cat arch/arm/dts/.myimx8mmek240-8mm.dtb.d.pre.tmp arch/arm/dts/.myimx8mmek240-8mm.dtb.d.dtc.tmp > arch/arm/dts/.myimx8mmek240-8mm.dtb.d
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/fsl-imx8mq-evk.dts; echo '#include "fsl-imx8mq-evk-u-boot.dtsi"') > arch/arm/dts/.fsl-imx8mq-evk.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.fsl-imx8mq-evk.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.fsl-imx8mq-evk.dtb.dts.tmp arch/arm/dts/.fsl-imx8mq-evk.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/fsl-imx8mq-evk.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.fsl-imx8mq-evk.dtb.d.dtc.tmp arch/arm/dts/.fsl-imx8mq-evk.dtb.dts.tmp ; cat arch/arm/dts/.fsl-imx8mq-evk.dtb.d.pre.tmp arch/arm/dts/.fsl-imx8mq-evk.dtb.d.dtc.tmp > arch/arm/dts/.fsl-imx8mq-evk.dtb.d
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/fsl-imx8mq-ddr3l-arm2.dts; echo '#include "fsl-imx8mq-ddr3l-arm2-u-boot.dtsi"') > arch/arm/dts/.fsl-imx8mq-ddr3l-arm2.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.fsl-imx8mq-ddr3l-arm2.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.fsl-imx8mq-ddr3l-arm2.dtb.dts.tmp arch/arm/dts/.fsl-imx8mq-ddr3l-arm2.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/fsl-imx8mq-ddr3l-arm2.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.fsl-imx8mq-ddr3l-arm2.dtb.d.dtc.tmp arch/arm/dts/.fsl-imx8mq-ddr3l-arm2.dtb.dts.tmp ; cat arch/arm/dts/.fsl-imx8mq-ddr3l-arm2.dtb.d.pre.tmp arch/arm/dts/.fsl-imx8mq-ddr3l-arm2.dtb.d.dtc.tmp > arch/arm/dts/.fsl-imx8mq-ddr3l-arm2.dtb.d
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/fsl-imx8mq-ddr4-arm2.dts; echo '#include "fsl-imx8mq-ddr4-arm2-u-boot.dtsi"') > arch/arm/dts/.fsl-imx8mq-ddr4-arm2.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.fsl-imx8mq-ddr4-arm2.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.fsl-imx8mq-ddr4-arm2.dtb.dts.tmp arch/arm/dts/.fsl-imx8mq-ddr4-arm2.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/fsl-imx8mq-ddr4-arm2.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.fsl-imx8mq-ddr4-arm2.dtb.d.dtc.tmp arch/arm/dts/.fsl-imx8mq-ddr4-arm2.dtb.dts.tmp ; cat arch/arm/dts/.fsl-imx8mq-ddr4-arm2.dtb.d.pre.tmp arch/arm/dts/.fsl-imx8mq-ddr4-arm2.dtb.d.dtc.tmp > arch/arm/dts/.fsl-imx8mq-ddr4-arm2.dtb.d
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/fsl-imx8mq-phanbell.dts; echo '#include "fsl-imx8mq-phanbell-u-boot.dtsi"') > arch/arm/dts/.fsl-imx8mq-phanbell.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.fsl-imx8mq-phanbell.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.fsl-imx8mq-phanbell.dtb.dts.tmp arch/arm/dts/.fsl-imx8mq-phanbell.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/fsl-imx8mq-phanbell.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.fsl-imx8mq-phanbell.dtb.d.dtc.tmp arch/arm/dts/.fsl-imx8mq-phanbell.dtb.dts.tmp ; cat arch/arm/dts/.fsl-imx8mq-phanbell.dtb.d.pre.tmp arch/arm/dts/.fsl-imx8mq-phanbell.dtb.d.dtc.tmp > arch/arm/dts/.fsl-imx8mq-phanbell.dtb.d
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/fsl-imx8mm-ddr3l-val.dts; ) > arch/arm/dts/.fsl-imx8mm-ddr3l-val.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.fsl-imx8mm-ddr3l-val.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.fsl-imx8mm-ddr3l-val.dtb.dts.tmp arch/arm/dts/.fsl-imx8mm-ddr3l-val.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/fsl-imx8mm-ddr3l-val.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.fsl-imx8mm-ddr3l-val.dtb.d.dtc.tmp arch/arm/dts/.fsl-imx8mm-ddr3l-val.dtb.dts.tmp ; cat arch/arm/dts/.fsl-imx8mm-ddr3l-val.dtb.d.pre.tmp arch/arm/dts/.fsl-imx8mm-ddr3l-val.dtb.d.dtc.tmp > arch/arm/dts/.fsl-imx8mm-ddr3l-val.dtb.d
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/fsl-imx8mm-ddr4-evk.dts; ) > arch/arm/dts/.fsl-imx8mm-ddr4-evk.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.fsl-imx8mm-ddr4-evk.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.fsl-imx8mm-ddr4-evk.dtb.dts.tmp arch/arm/dts/.fsl-imx8mm-ddr4-evk.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/fsl-imx8mm-ddr4-evk.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.fsl-imx8mm-ddr4-evk.dtb.d.dtc.tmp arch/arm/dts/.fsl-imx8mm-ddr4-evk.dtb.dts.tmp ; cat arch/arm/dts/.fsl-imx8mm-ddr4-evk.dtb.d.pre.tmp arch/arm/dts/.fsl-imx8mm-ddr4-evk.dtb.d.dtc.tmp > arch/arm/dts/.fsl-imx8mm-ddr4-evk.dtb.d
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/fsl-imx8mm-ddr4-val.dts; ) > arch/arm/dts/.fsl-imx8mm-ddr4-val.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.fsl-imx8mm-ddr4-val.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.fsl-imx8mm-ddr4-val.dtb.dts.tmp arch/arm/dts/.fsl-imx8mm-ddr4-val.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/fsl-imx8mm-ddr4-val.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.fsl-imx8mm-ddr4-val.dtb.d.dtc.tmp arch/arm/dts/.fsl-imx8mm-ddr4-val.dtb.dts.tmp ; cat arch/arm/dts/.fsl-imx8mm-ddr4-val.dtb.d.pre.tmp arch/arm/dts/.fsl-imx8mm-ddr4-val.dtb.d.dtc.tmp > arch/arm/dts/.fsl-imx8mm-ddr4-val.dtb.d
mkdir -p arch/arm/dts/ ; (cat arch/arm/dts/fsl-imx8mm-evk.dts; ) > arch/arm/dts/.fsl-imx8mm-evk.dtb.pre.tmp; /home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -E -Wp,-MD,arch/arm/dts/.fsl-imx8mm-evk.dtb.d.pre.tmp -nostdinc -I./arch/arm/dts -I./arch/arm/dts/include -Iinclude -I./include -I./arch/arm/include -include ./include/linux/kconfig.h -D__ASSEMBLY__ -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/dts/.fsl-imx8mm-evk.dtb.dts.tmp arch/arm/dts/.fsl-imx8mm-evk.dtb.pre.tmp ; ./scripts/dtc/dtc -O dtb -o arch/arm/dts/fsl-imx8mm-evk.dtb -b 0 -i arch/arm/dts/ -Wno-unit_address_vs_reg -Wno-simple_bus_reg -Wno-unit_address_format -Wno-pci_bridge -Wno-pci_device_bus_num -Wno-pci_device_reg -d arch/arm/dts/.fsl-imx8mm-evk.dtb.d.dtc.tmp arch/arm/dts/.fsl-imx8mm-evk.dtb.dts.tmp ; cat arch/arm/dts/.fsl-imx8mm-evk.dtb.d.pre.tmp arch/arm/dts/.fsl-imx8mm-evk.dtb.d.dtc.tmp > arch/arm/dts/.fsl-imx8mm-evk.dtb.d

2. arch/arm/dts/myimx8mmek240-8mm.dts

文件存在即可。

17.1.2 $(DTB)规则

make -f $(srctree)/scripts/Makefile.build obj=arch/arm/dts arch/arm/dts/myimx8mmek240-8mm.dtb

arch/arm/dts/myimx8mmek240-8mm.dtb的实际编译位置在17.1.1-1中。

17.2 $(obj)/dt.dtb(dts/dt.dtb)

# <<<<<<<<<scripts/Makefile.lib >>>>>>>>>
# Shipped files
# =========================================================================== quiet_cmd_shipped = SHIPPED $@
cmd_shipped = cat $< > $@ //$<:第一个依赖对象arch/arm/dts/myimx8mmek240-8mm.dtb # <<<<<<<<<dts/Makefile >>>>>>>>>
$(obj)/dt.dtb: $(DTB) FORCE //关注dts/dt.dtb
$(call if_changed,shipped)

没什么好讲的,具体编译过程如下:

  cat arch/arm/dts/myimx8mmek240-8mm.dtb > dts/dt.dtb

17.3 $(obj)/dt-spl.dtb(dts/dt-spl.dtb)

# <<<<<<<<<scripts/Makefile.lib >>>>>>>>>
# fdtgrep
# ---------------------------------------------------------------------------
# Pass the original device tree file through fdtgrep twice. The first pass
# removes any unwanted nodes (i.e. those which don't have the
# 'u-boot,dm-pre-reloc' property and thus are not needed by SPL. The second
# pass removes various unused properties from the remaining nodes.
# The output is typically a much smaller device tree file.
ifeq ($(CONFIG_TPL_BUILD),y)
fdtgrep_props := -b u-boot,dm-pre-reloc -b u-boot,dm-tpl
else
fdtgrep_props := -b u-boot,dm-pre-reloc -b u-boot,dm-spl
endif
quiet_cmd_fdtgrep = FDTGREP $@
cmd_fdtgrep = $(objtree)/tools/fdtgrep $(fdtgrep_props) -RT $< \
-n /chosen -n /config -O dtb | \
$(objtree)/tools/fdtgrep -r -O dtb - -o $@ \
$(addprefix -P ,$(subst $\",,$(CONFIG_OF_SPL_REMOVE_PROPS))) $(obj)/dt-spl.dtb: $(DTB) $(objtree)/tools/fdtgrep FORCE //关注
$(call if_changed,fdtgrep)

将原始设备树文件通过fdtgrep两次处理:

第一个过程:删除任何不需要的节点(比如去掉不含“u-boot,dm pre reloc”属性的节点,因为SPL不需要这些节点)。
第二个过程:从其余节点中删除各种未使用的属性。

输出通常是一个小得多的设备树文件。

没什么好讲的,具体编译过程如下:

 ./tools/fdtgrep -b u-boot,dm-pre-reloc -b u-boot,dm-spl -RT arch/arm/dts/myimx8mmek240-8mm.dtb -n /chosen -n /config -O dtb | ./tools/fdtgrep -r -O dtb - -o dts/dt-spl.dtb

参数解释如下

-b:properties in the node;
-R :Include the root node and all properties;
-T :Add aliases node to output
-n :要保留的节点名;
-O :Output formats;
-r :Remove unused strings;
-o:Output filename;

最终生成的dts/dt-spl.dtb对应的dts内容如下:

18. u-boot-dtb.bin

# <<<<<<<<<顶层Makefile>>>>>>>>>
quiet_cmd_cat = CAT $@
cmd_cat = cat $(filter-out $(PHONY), $^) > $@ ...... u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
$(call if_changed,cat)

没什么好讲的,就是把u-boot-nodtb.bin和dts/dt.dtb打包成u-boot-dtb.bin。具体编译过程如下:

cat u-boot-nodtb.bin dts/dt.dtb > u-boot-dtb.bin

19. u-boot.bin

# <<<<<<<<<顶层Makefile>>>>>>>>>
quiet_cmd_copy = COPY $@
cmd_copy = cp $< $@ ...... u-boot.bin: u-boot-dtb.bin FORCE
$(call if_changed,copy)

没什么好讲的,就是把u-boot-dtb.bin 重命名为u-boot.bin。具体编译过程如下:

cp u-boot-dtb.bin u-boot.bin

20. u-boot.img/u-boot-dtb.img

# <<<<<<<<<顶层Makefile>>>>>>>>>
MKIMAGEFLAGS_u-boot.img = -f auto -A $(ARCH) -T firmware -C none -O u-boot \
-a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_UBOOT_START) \
-n "U-Boot $(UBOOTRELEASE) for $(BOARD) board" -E \
$(patsubst %,-b arch/$(ARCH)/dts/%.dtb,$(subst ",,$(CONFIG_OF_LIST))) MKIMAGEFLAGS_u-boot-dtb.img = $(MKIMAGEFLAGS_u-boot.img) quiet_cmd_mkimage = MKIMAGE $@
cmd_mkimage = $(objtree)/tools/mkimage $(MKIMAGEFLAGS_$(@F)) -d $< $@ \ //$(@F)为
$(if $(KBUILD_VERBOSE:1=), >$(MKIMAGEOUTPUT)) u-boot-dtb.img u-boot.img u-boot.kwb u-boot.pbl u-boot-ivt.img: \ //CONFIG_SPL_LOAD_FIT =1
$(if $(CONFIG_SPL_LOAD_FIT),u-boot-nodtb.bin dts/dt.dtb,u-boot.bin) FORCE
$(call if_changed,mkimage)

很简单,就是调用mkimage 工具制作带有mkimage头的U-Boot映像。注意u-boot.img和u-boot-dtb.img内容相同。

用到的mkimage参数解释如下:

-A ==> set architecture to 'arch'

-O ==> set operating system to 'os'

-T ==> set image type to 'type'

-C ==> set compression type 'comp'

-a ==> set load address to 'addr' (hex)"

-e ==> set entry point to 'ep' (hex)

-n ==> set image name to 'name'

-d ==> use image data from 'datafile'

-b ==> dtb

-E => place data outside of the FIT structure

具体编译过程如下:

./tools/mkimage -f auto -A arm -T firmware -C none -O u-boot -a 0x40200000 -e 0 -n "U-Boot 2018.03"" for myimx8mm board" -E -b arch/arm/dts/myimx8mmek240-8mm.dtb -d u-boot-nodtb.bin u-boot.img

./tools/mkimage -f auto -A arm -T firmware -C none -O u-boot -a 0x40200000 -e 0 -n "U-Boot 2018.03"" for myimx8mm board" -E -b arch/arm/dts/myimx8mmek240-8mm.dtb -d u-boot-nodtb.bin u-boot-dtb.img

21. u-boot.dtb

# <<<<<<<<<顶层Makefile>>>>>>>>>
quiet_cmd_copy = COPY $@
cmd_copy = cp $< $@ u-boot.dtb: dts/dt.dtb
$(call cmd,copy)

没什么好讲的,就是把dts/dt.dtb 重命名为u-boot.dtb。具体编译过程如下:

cp dts/dt.dtb u-boot.dtb

22. binary_size_check

# <<<<<<<<<顶层Makefile>>>>>>>>>
binary_size_check: u-boot-nodtb.bin FORCE
@file_size=$(shell wc -c u-boot-nodtb.bin | awk '{print $$1}') ; \ //获取文件大小
map_size=$(shell cat u-boot.map | \
awk '/_image_copy_start/ {start = $$1} /_image_binary_end/ {end = $$1} END {if (start != "" && end != "") print "ibase=16; " toupper(end) " - " toupper(start)}' \
| sed 's/0X//g' \
| bc); \
if [ "" != "$$map_size" ]; then \
if test $$map_size -ne $$file_size; then \
echo "u-boot.map shows a binary size of $$map_size" >&2 ; \
echo " but u-boot-nodtb.bin shows $$file_size" >&2 ; \
exit 1; \
fi \
fi

shell wc -c : wc命令的功能为统计指定文件中的字节数、字数、行数, 并将统计结果显示输出。- c 统计字节数;
$$1 : makefile中展开为$1,shell中$1表示第一个参数;
sed 's/0X//g' :把每一行的'0X'替换为空,即去掉地址的0X前缀;
toupper :转换为大写;
ibase=16 :设置输入为16进制,默认值为10;
sed 's/0X//g':'0X'替换为空;
bc :能够对计算公式的语法进行解释并返回出结果
test -ne :不等于则为真

上述命令:就是

把 u-boot.map中的_image_copy_start去掉0X赋值给start,_image_binary_end地址值去掉0X赋值给end;
如果start 不为空且end 不为空,则计算end-start的差值,并赋值给map_size,这时未指定输出格式,默认为10进制数据。
然后把十六进制的0X去掉;
输出10进制数值;
判断map_size是否为空,如果不为空。则继续判断map_size是否等于file_size(u-boot-nodtb.bin的大小);

23. u-boot.srec

# <<<<<<<<<顶层Makefile>>>>>>>>>
OBJCOPYFLAGS_u-boot.srec := -O srec # Normally we fill empty space with 0xff
quiet_cmd_objcopy = OBJCOPY $@
cmd_objcopy = $(OBJCOPY) --gap-fill=0xff $(OBJCOPYFLAGS) \
$(OBJCOPYFLAGS_$(@F)) $< $@
...... u-boot.hex u-boot.srec: u-boot FORCE
$(call if_changed,objcopy)

objcopy的功能:将目标文件的一部分或者全部内容拷贝到另外一个目标文件中,或者实现目标文件的格式转换。

j sectionname , --only-section=sectionname : 只将由 sectionname 指定的 section 拷贝到输出文件,可以多次指定,并且注意如果使用不当会导致输出文件不可用。

-O bfdname :--output-target= bfdname 使用指定的格式来写输出文件(即目标文件),bfdname是BFD库中描述的标准格式名。

具体编译过程如下:

/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-objcopy --gap-fill=0xff  -j .text -j .secure_text -j .secure_data -j .rodata -j .data -j .u_boot_list -j .rela.dyn -j .got -j .got.plt -j .binman_sym_table -j .dtb.init.rodata -j .efi_runtime -j .efi_runtime_rel -O srec u-boot u-boot.srec

24. u-boot.sym

# <<<<<<<<<顶层Makefile>>>>>>>>>
quiet_cmd_sym ?= SYM $@
cmd_sym ?= $(OBJDUMP) -t $< > $@ ...... u-boot.sym: u-boot FORCE
$(call if_changed,sym)

objdump的功能:是Linux下的反汇编目标文件或者可执行文件的命令:

-t /--syms :显示文件的符号表入口。类似于nm -s提供的信息 。

具体编译过程如下:

/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-objdump -t u-boot > u-boot.sym

25. System.map

# <<<<<<<<<顶层Makefile>>>>>>>>>
SYSTEM_MAP = \
$(NM) $1 | \
grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
LC_ALL=C sort
System.map: u-boot
@$(call SYSTEM_MAP,$<) > $@

具体编译过程如下:

/home/h/my-work/03_toolchain/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-nm u-boot | grep -v '\(compiled\)\|\(\.o$\)\|\( [aUw] \)\|\(\.\.ng$\)\|\(LASH[RL]DI\)' | LC_ALL=C sort > System.map

grep -v : 是反向查找的意思,比如 grep -v grep 就是查找不含有 grep 字段的行 。
' '中的内容 :引号中的 \ 是转义的意思,即不把 / 后面的当作命令信息解释。
LC_ALL=C : 去除所有本地化的设置,让命令能正确执行。"C"是系统默认的locale,"POSIX"是"C"的别名
sort : 排序

也就是将nm命令查看u-boot的输出信息经过过滤排序后输出到System.map。

System.map表示的是地址标号到该标号表示的地址的一个映射关系。System.map每一行的格式都是“addr type name”,addr是标号对应的地址值,name是标号名,type表示标号的类型。

U-Boot的编译和运行并不一定要生成System.map,这个文件主要是提供给用户或外部程序调试时使用的。

.

26. 关于SPL镜像

由于篇幅有限这里先不讲,后面单独讲。

27. 总结

成功编译之后,就会在 U-Boot 源码的根目录下产生多个可执行二进制文件以及编译过程文件,这些文件都是 u-boot.xxx 的命名方式,对应目录下的.xxx.cmd 这些文件都是由编译时的具体指令(if_changed函数中生成$(dot-target).cmd)。

-- u-boot:ELF 格式的 U-Boot 镜像文件,后续的文件都是由它产生的。具体编译过程参见u-boot.cmd ;
-- dts/dt.dtb:设备树 (来自于 arch/arm/dts/myimx8mmek240-8mm.dtb 重命名,具体编译过程参见.myimx8mmek240-8mm.dtb.cmd);
-- u-boot.dtb:dts/dt.dtb的重命名;
-- u-boot-nodtb.bin: 使用编译工具链的 objcopy 工具从 u-boot 这个文件中提取来的,它只包含可执行的二进制代码。就是把 u-boot 这个文件中对于执行不需要的节区删除后剩余的仅执行需要的部分。具体编译过程参见.u-boot-nodtb.bin.cmd;
-- u-boot-dtb.bin:在 u-boot-nodtb.bin 尾部拼接上设备树后形成的文件。具体编译过程参见 .u-boot-dtb.bin.cmd;
-- u-boot.bin:编译出来的二进制格式的uboot可执行镜像文件 。在我使用的单板等价于 u-boot-dtb.bin。具体编译过程参见 .u-boot-dtb.bin.cmd;
-- u-boot.cfg:uboot的另外一种配置文件 .
-- u-boot.img/u-boot-dtb.img :调用mkimage 工具制作带有mkimage头的U-Boot映像 ,输入是u-boot-nodtb.bin。具体编译过程参见 .u-boot.img.cmd 和 .u-boot-dtb.img.cmd;
-- u-boot.lds:链接脚本 。具体编译过程参见 .u-boot.lds.cmd;
-- u-boot.map:uboot映射文件,可查看某个函数被链接到哪个地址上了 。具体编译过程参见u-boot.cmd ;
-- u-boot.srec:S-Record格式的镜像文件 。具体编译过程参见 .u-boot.srec.cmd;
-- u-boot.sym:uboot符号表文件 。具体编译过程参见 .u-boot.sym.cmd

28. 参考

[1] '-E' 只执行到预编译,只涉及到宏替换、头文件展开 、注释去掉(在预处理阶段结束后停止'-E');

[2] '-x' 为输入文件显式指定语言(而不是让编译器根据文件名后缀选择默认语言)(显式指定语言'-x');

[3] '-P' 删除无用的信息(预处理器控制选项 -P);

[4] 利用gcc -E -P 进行宏替换 宏展开 预处理文件;

[5] makefile中$$的使用

UBOOT编译--- UBOOT全部目标的编译过程详解(九)的相关教程结束。

《UBOOT编译--- UBOOT全部目标的编译过程详解(九).doc》

下载本文的Word格式文档,以方便收藏与打印。