UBOOT编译--- UBOOT全部目标的编译过程详解(九)
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中$$的使用
本文来自博客园,作者:BSP-路人甲,转载请注明原文链接:https://www.cnblogs.com/jianhua1992/p/16852767.html,并保留此段声明,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具