Uboot编译过程分析
来源:互联网 发布:淘宝运费险多少钱 编辑:程序博客网 时间:2024/05/29 09:46
Uboot编译过程分析
本文基于firefly-rk3399开发板的uboot,对其编译过程进行分析。
uboot的编译分为两个步骤:
make ARCHV=aarch64 rk3399_box_defconfigmake
因此,接下来主要分为两部分,对uboot的编译过程进行剖析。
一、配置
1. make ARCHV=aarch64 rk3399_box_defconfig
有两个信息:
1-1 ARCHV=aarch64
定义了一个变量ARCHV,其值为aarch64。Makefile根据这个变量,决定使用哪种交叉编译工具:
ifeq ($(ARCHV),aarch64)ifneq ($(wildcard ../toolchain/aarch64-linux-android-4.9),)CROSS_COMPILE ?= $(shell pwd)/../toolchain/aarch64-linux-android-4.9/bin/aarch64-linux-android-endif...else...endif
1-2 构建的终极目标为rk3399_box_defconfig
rk3399_box_defconfig匹配如下规则
%config: scripts_basic outputmakefile FORCE +$(Q)$(CONFIG_SHELL) $(srctree)/scripts/multiconfig.sh $@ #前面“+”的意思是,这条命令始终会执行,#即使使用的make -n。(-n的含义是只打印命令,不实际执行)
rk3399_box_defconfig依赖scripts_basic,因此先构建scripts_basic
scripts_basic: $(Q)$(MAKE) $(build)=scripts/basic $(Q)rm -f .tmp_quiet_recordmcount
build在scripts/Kbuild.include中定义:
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
因此执行scripts/basic/Makefile.build,指定obj变量为scripts/basic,没有明确指定目标,默认构建Makefile.build中的第一个目标__build。
2. scripts/Makefile.build
在该文件中:
prefix := tplsrc := $(patsubst $(prefix)/%,%,$(obj)) #此时src=obj=scripts/basic...__build:...kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)include $(kbuild-file)...
意思就是在scripts/basic下,如果有Kbuild文件,就include该文件,否则include目录下的Makefile文件。
scripts/basic没有Kbuild文件,因此include scripts/basic/Makefile
scripts/basic/Makefile内容如下:
hostprogs-y := fixdepalways := $(hostprogs-y)
接着往下看Makefile.build:
ifneq ($(hostprogs-y)$(hostprogs-m),)include scripts/Makefile.host # hostprogs-y不为空,所以include进该文件endif...__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \ $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \ $(subdir-ym) $(always) @: #@取消命令回显,:是空命令,什么也不做。
由于在scripts/basic/Makefile中定义了always:=fixdep,__build依赖fixdep,所以先构建fixdep,构建的规则在scripts/Makefile.host中定义。
3. scripts/Makefile.host
quiet_cmd_host-csingle = HOSTCC $@ cmd_host-csingle = $(HOSTCC) $(hostc_flags) -o $@ $< \ $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))$(host-csingle): $(obj)/%: $(src)/%.c FORCE #host-csingle=hostprogs-y=scripts/basic/fixdep $(call if_changed_dep,host-csingle)
这是一条静态模式规则,意思就是从 $(host-csingle)中找出匹配$(obj)/%的字符串作为目标,匹配$(src)/%.c的文件作为依赖,生成一条规则
例如:
files = foo.elc bar.o lose.o$(filter %.o,$(files)) : %.o:%.c$(CC) -c $(CFLAGS) $< -o $@$(filter %.elc,$(files)) : %.elc : %.elemacs -f batch-byte-compile $<
等效于
bar.o : bar.c$(CC) -c $(CFLAGS) $< -o $@lose.o : lose.c$(CC) -c $(CFLAGS) $< -o $@foo.elc : foo.elemacs -f batch-byte-compile $<
因此
$(host-csingle): $(obj)/%: $(src)/%.c FORCE $(call if_changed_dep,host-csingle)
展开后就是
scripts/basic/fixdep : scripts/basic/fixdep.c FORCE$(call if_changed_dep,host-csingle)
if_changed_dep在scripts/Kbuild.include中定义,大致意思就是如果目标的依赖有发生变化,就会执行host-csingle定义的命令。
最后,在scripts/basic下生成了fixdep程序文件。
4.构建rk3399_box_defconfig
回过头来,scripts_basic已经构建,接着构建rk3399_box_defconfig:
%config: scripts_basic outputmakefile FORCE +$(Q)$(CONFIG_SHELL) $(srctree)/scripts/multiconfig.sh $@
这条命令意思就是执行scripts/multiconfig.sh rk3399_box_defconfig。执行流程如下:
progname=$(basename $0)target=$1case $target in #target=rk3399_box_defconfig*_defconfig) do_board_defconfig $target;;...esacdo_board_defconfig () {... run_make_config .tmp_defconfig || { cleanup_after_defconfig exit 1 }...}run_make_config () { target=$1 objdir=$2 build scripts/kconfig $options $target}build () { debug $progname: $MAKE -f $srctree/scripts/Makefile.build obj="$@" $MAKE -f $srctree/scripts/Makefile.build obj="$@"}
最终执行
make -f scripts/basic/Makefile.build obj=scripts/kconfig SRCARCH=.. KCONFIG_OBJDIR= .tmp_defconfig
即执行scripts/basic/Makefile.build,构建 .tmp_defconfig
5. 构建 .tmp_defconfig
scripts/basic/Makefile.build引入scripts/kconfig/Makefile,在scripts/kconfig/Makefile中定义了构建.tmp_defconfig的规则:
%_defconfig: $(obj)/conf $(Q)$< --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
.tmp_defconfig依赖scripts/kconfig/conf,先构建scripts/kconfig/conf。
在该Makefile中:
conf-objs := conf.o zconf.tab.ohostprogs-y := conf$(obj)/zconf.tab.o: $(obj)/zconf.lex.c $(obj)/zconf.hash.c
与构建fixdep类似,使用scripts/Makefile.host中定义的规则构建。conf是由conf.o和zconf.tab.o链接而成
$(host-cobjs): $(obj)/%.o: $(src)/%.c FORCE $(call if_changed_dep,host-cobjs)
上面的规则构建conf.o和zconf.tab.o,最后由下面的规则链接成conf:
$(host-cmulti): $(obj)/%: $(host-cobjs) $(host-cshlib) FORCE $(call if_changed,host-cmulti)
需要注意,scripts/kconfig下没有zconf.tab.c文件,只有一个zconf.tab.c_shipped文件,会使用scripts/Makefile.lib中定义的规则
$(obj)/%: $(src)/%_shipped $(call cmd,shipped)
先构建zconf.tab.c,$(obj)/zconf.lex.c $(obj)/zconf.hash.c也一样。
生成conf后,就最终执行构建.tmp_defconfig的规则了:
%_defconfig: $(obj)/conf $(Q)$< --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
展开命令就是
scripts/kconfig/conf --defconfig=arch/../configs/.tmp_defconfig Kconfig
该命令引入了所有Kconfig和.tmp_defconfig(内容和rk3399_box_defconfig一样)定义的CONFIG_XXX变量,最后输出到.config中。
至此,make rk3399_box_defconfig执行完成。
二、make 编译
1、Makefile
先看Makefile的这几行:
-include include/config/auto.conf-include include/config/auto.conf.cmd$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
在执行make后(见下图),没有开始执行构建终极目标的流程,而是最先执行了构建include/config/%.conf的操作,这是为什么呢?
原因在于这一行
-include include/config/auto.conf
make在读入所有的makefile文件之后,首先将所读取的每个makefile作为一个目标,如果存在更新makefile的规则,则更新makefile文件。更新之后make清除本次执行的状态,重新读取一遍所有的makefile文件。
make执行前没有include/config/auto.conf文件,因此在读入所有的Makefile文件之后,会寻找构建include/config/auto.conf的规则,即
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
生成include/config/auto.conf后,make重新读取一次所有的Makefile,这时再-include include/config/auto.conf就会引入auto.conf中的内容了。
2、构建include/config/auto.conf
在Makefile中:
$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
展开后
.config include/config/auto.conf.cmd: ;include/config/auto.conf : .config include/config/auto.conf.cmdmake -f Makefile silentoldconfig
也就是执行make silentoldconfig。接下来的流程与make rk3399_box_defconfig类似
%config: scripts_basic outputmakefile FORCE +$(Q)$(CONFIG_SHELL) $(srctree)/scripts/multiconfig.sh $@
执行multiconfig.sh silentoldconfig,调用到multiconfig.sh中的do_silentoldconfig ()函数
do_silentoldconfig () { ... run_make_config silentoldconfig ... autoconf include include/autoconf.mk include/autoconf.mk.dep || { rm -f include/config/auto.conf exit 1 } touch include/config/auto.conf}
主要做了两个工作:
2-1、 run_make_config silentoldconfig
run_make_config silentoldconfig
最终执行scripts/kconfig/Makefile中定义的规则
silentoldconfig: $(obj)/conf $(Q)mkdir -p include/config include/generated $< --$@ $(Kconfig)
也就是执行了之前说的那条命令:scripts/kconfig/conf --silendoldconfig Kconfig
2-2、autoconf include include/autoconf.mk include/autoconf.mk.dep
autoconf () { debug $progname: $MAKE -f $srctree/scripts/Makefile.autoconf obj="$@" $MAKE -f $srctree/scripts/Makefile.autoconf obj="$@"}
命令展开后:
make -f scripts/Makefile.autoconf obj=include include/autoconf.mk include/autoconf.mk.dep
根据在Makefile.autoconf中定义的规则生成include/autoconf.mk和include/autoconf.mk.dep。
比较autoconf和.config文件内容,发现两者基本上是一样的;make rk3399_box_defconfig汇集了CONFIG_XXX变量到.config文件,在make时由-incldue include/config/autoconf引入到make,这样make就可以使用这些变量的定义了。
然而CONFIG_XXX变量不止在makefile中定义,更多情况实在.h头文件中定义为宏,例如CONFIG_RAM_PHY_START在include/configs/rk30plat.h中定义:
#define CONFIG_RAM_PHY_START 0x60000000
那么,make如何获取这些在头文件中定义的宏呢?就是这个autoconf.mk文件,这个文件将所有相关头文件中的CONFIG_XXX宏,转化为makefile变量,再被-include include/autoconf.mk这句话包含进Makefile,供make使用。
接下来在Makefile中:
ifneq ($(autoconf_is_current),)include $(srctree)/config.mkendif
包含进了源码顶级目录的config.mk,这个config.mk定义了一些变量,并且引入了cpu和board目录下的config.mk。
至此,所有的CONFIG_XXX变量(makefile中定义的和.h中的宏定义)就被make包含进了。
3、 u-boot-dirs
接下来,Makefile定义了一些变量
head-y := $(CPUDIR)/start.olibs-$(CONFIG_OF_EMBED) += dts/libs-y += arch/$(ARCH)/lib/libs-y += fs/libs-y += net/libs-y += disk/...u-boot-dirs := $(patsubst %/,%,$(filter %/, $(libs-y))) tools exampleslibs-y := $(patsubst %/, %/built-in.o, $(libs-y))u-boot-init := $(head-y)u-boot-main := $(libs-y)
有些变量根据CONFIG_XXX的值来决定是否追加定义,例如如果CONFIG_OF_EMBED=y,则libs-$(CONFIG_OF_EMBED) += dts/就会展开成libs-y += dts/。lib-y指定了本次编译需要编译的工程目录,u-boot-main就是各工程目录下的built-in.o文件,例如fs/build-in.o、net/build-in.o,这些build-in.o最后会被链接成uboot。
4、编译终极目标
make没有指定目标,默认构建终极目标_all。_all依赖all,al又依赖$(ALL-y),$(ALL-y)展开后为checkarmreloc RKLoader-uboot.bin u-boot.srec u-boot.bin System.map binary_size_check。依赖树如下图所示:
4-1、include/config/uboot.release
define filechk_uboot.release echo "$(UBOOTVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))"endefinclude/config/uboot.release: include/config/auto.conf FORCE $(call filechk,uboot.release)
调用filechk_uboot.release生成include/config/uboot.release,该文件包含了uboot的版本信息。
4-2、调用filechk_version.h生成$(verison_h)
4-3、调用filechk_timestamp.h生成$(timestamp_h)
4-4、prepare0的构建
prepare0: archprepare FORCE $(Q)$(MAKE) $(build)=.
即
make -f scripts/basic/Makefile.build obj=.
没有指定目标,默认构建__build.
● 在Makefile.build中,引入$(srctree)/Kbuild ● Kbuild定义了$(always) ● 构建__build ● 构建__build的依赖$(always),即generic-asm-offsets.h和asm-offsets.h
4-5、 构建$(u-boot-dirs)
这一步完成了uboot编译的主要工作:
$(u-boot-dirs): prepare scripts $(Q)$(MAKE) $(build)=$@tools: prepare$(filter-out tools, $(u-boot-dirs)): tools
$(Q)$(MAKE) $(build)=$@
这个命令,引入各个目录下的Kbuild或Makefile,依据Makefile.build中定义的规则构建目标
● 如果定义了hostprogs-y,则引入Makefile.host,构建目标 ● 如果定义了obj-y,依据Makefile.build中的规则构建各个.o文件,并最终链接成build-in.o文件
下面以构建arch/arm/cpu/armv8
为例,简单说明:
arch/arm/cpu/armv8 : prepare scripts $(Q)$(MAKE) $(build)=$@
执行make -f scripts/basic/Makefile.build obj=arch/arm/cpu/armv8
在Makefile.build中,include arch/arm/cpu/armv8/Makefile
:
extra-y := start.oobj-y += cpu.oifndef CONFIG_ROCKCHIPobj-y += generic_timer.oendifobj-y += cache_v8.oobj-y += exceptions.oobj-y += cache.oobj-y += tlb.oobj-y += transition.o
接着看Makefile.build:
ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(subdir-m) $(lib-target)),)builtin-target := $(obj)/built-in.oendif
在arch/arm/cpu/armv8/Makefile中定义了obj-y,所以builtin-target := arch/arm/cpu/armv8/built-in.o
所以,__build的构建规则
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \ $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \ $(subdir-ym) $(always) @:
展开后
__build: arch/arm/cpu/armv8/built-in.o arch/arm/cpu/armv8/start.o @:
build-in.o依赖$(obj-y),也就是在arch/arm/cpu/armv8/Makefile中定义的那些.o文件:
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)
.o文件使用如下规则构建:
cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE $(call cmd,force_checksrc) $(call if_changed_rule,cc_o_c)cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $<$(obj)/%.o: $(src)/%.S FORCE $(call if_changed_dep,as_o_S)
执行cmd_cc_o_c定义的命令将.c编译成.o,执行cmd_as_o_S定义的命令将.S编译成.o。
最后,将编译出来的.o文件使用cmd_link_o_target链接成arch/arm/cpu/armv8/build-in.o。
其他目录的的执行过程也是类似的,就不做进一步分析了。
4-6、构建u-boot
执行完构建$(u-boot-dirs)的规则之后,将源文件编译成了.o目标文件,下一步就是链接了。先构建链接脚本u-boot.lds:
quiet_cmd_cpp_lds = LDS $@cmd_cpp_lds = $(CPP) -Wp,-MD,$(depfile) $(cpp_flags) $(LDPPFLAGS) -ansi \ -D__ASSEMBLY__ -x assembler-with-cpp -P -o $@ $<u-boot.lds: $(LDSCRIPT) prepare FORCE $(call if_changed_dep,cpp_lds)
接着链接生成u-boot:
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.mapu-boot: $(u-boot-init) $(u-boot-main) u-boot.lds $(call if_changed,u-boot__)ifeq ($(CONFIG_KALLSYMS),y) $(call cmd,smap) $(call cmd,u-boot__) common/system_map.oendif
4-7、RKLoader_uboot.bin
RKLoader_uboot.bin: u-boot.binifdef CONFIG_SECOND_LEVEL_BOOTLOADER $(if $(CONFIG_MERGER_MINILOADER), ./tools/boot_merger ./tools/rk_tools/RKBOOT/$(RKCHIP)MINIALL.ini &&) \ $(if $(CONFIG_MERGER_TRUSTIMAGE), ./tools/trust_merger $(if $(CONFIG_RK_TRUSTOS), --subfix) \ ./tools/rk_tools/RKTRUST/$(RKCHIP)TRUST.ini &&) \ $(if $(CONFIG_MERGER_TRUSTOS), ./tools/loaderimage --pack --trustos $(RK_TOS_BIN) trust.img &&) \ ./tools/loaderimage --pack --uboot u-boot.bin uboot.imgelse ./tools/boot_merger --subfix "$(RK_SUBFIX)" ./tools/rk_tools/RKBOOT/$(RKCHIP).iniendif # CONFIG_SECOND_LEVEL_BOOTLOADER
u-boot.bin: u-boot FORCE $(call if_changed,objcopy) $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE)) $(BOARD_SIZE_CHECK)
使用objcopy从u-boot生成u-boot.bin二进制文件,根据CONFIG_SECOND_LEVEL_BOOTLOADER是否配置成二级Loader,生成最终的Uboot镜像文件。
三、总结
make rk3399_box_defconfig导入默认的配置,生成.config文件;在make时,根据.config、Makefile和各个.h头文件中定义的CONFIG_XXX变量,决定要编译哪些模块;编译时进入各个工程目录,根据工程目录下的Makefile内容,执行编译过程。如果Makefile中定义了hostprogs-y,则使用scripts/Makefile.host中定义的规则构建目标;如果定义了obj-y,则根据scripts/basic/Makefile.build中的规则构建目标,最终生成uboot镜像。
- Uboot编译过程分析
- uboot编译过程完全分析
- uboot编译过程完全分析
- uboot编译配置过程分析
- uboot的编译过程分析---Makefile分析
- uboot 分析之 配置+编译过程
- Uboot 编译过程
- uboot编译过程
- Uboot 编译过程
- uboot编译过程
- uboot 编译过程
- uboot 编译过程
- uboot 编译过程
- uboot编译链接过程
- uboot编译过程
- uboot的编译过程
- ok6410-uboot的配置和编译过程分析
- uboot的编译和链接过程分析(一)
- Java的Executor框架和线程池实现原理
- TokenProccessor 工具类的生成
- 我的javascript权威指南-7
- SpringBoot静态资源处理 火推05
- ScrollView、SwipeRefreshLayout、ListView、RecyclerView等控件解决滑动冲突
- Uboot编译过程分析
- 圆角按钮
- VMware虚拟机在NAT模式下静态IP地址及Xshell远程控制配置
- elasticsearch系列-elasticsearch学习心得
- 我的javascript权威指南-8
- hibernate4整合spring3出现java.lang.NoClassDefFoundError: [Lorg/hibernate/engine/FilterDefinition;
- 7. java.io.InputStream
- 如何搭建一个网页版的BLAST服务器
- java创建线程的三种方式及其对比