u-boot-2016.09 make编译过程分析(二)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/guyongqiangx/article/details/52761678
u-boot-2016.09 make编译过程分析(二)
综述
u-boot自v2014.10版本开始引入KBuild系统,同更改前的编译系统相比,由于Kbuild系统的原因,其Makefile变得更加复杂。
u-boot的编译跟kernel编译一样,分两步执行:
第一步:配置,执行make xxx_defconfig进行各项配置,生成.config文件
第二部:编译,执行make进行编译,生成可执行的二进制文件u-boot.bin或u-boot.elf
Makefile的核心是依赖和命令。对于每个目标,首先会检查依赖,如果依赖存在,则执行命令更新目标;如果依赖不存在,则会以依赖为目标,先生成依赖,待依赖生成后,再执行命令生成目标。
博客《u-boot-2016.09 make配置过程分析》详尽解释了第一步的操作,在这一步中,u-boot执行配置命令make xxx_defconfig时先搜集所有默认的Kconfig配置,然后再用命令行指定的xxx_defconfig配置进行更新并输出到根目录的.config文件中。
配置完成后执行make命令生成二进制文件的过程,由于涉及的依赖和命令很多,也将make编译过程分析分为两部分,目标依赖和命令执行。
博客《u-boot-2016.09 make编译过程分析(一)》中描述了make过程中的依赖关系
本篇主要分析make过程中的通过命令生成各个目标的依赖,从而一步一步更新目标,直至更新并生成顶层目标u-boot.bin。
第二部分、执行命令更新目标
将上面的依赖关系并到一起,就得到了一个完整的u-boot目标依赖图:
完整的目标依赖关系
(完整的关系图较大,可以将图片拖到浏览器的其他窗口看大图)
这些依赖有两类:
依赖本身通过执行命令生成,但不存在进一步的依赖;
依赖自身还有进一步的依赖,在生成了进一步依赖的基础上,执行命令生成依赖;
完成目标依赖分析后,剩下的就是基于完整的目标依赖关系图,从最底层的依赖开始,逐层运行命令生成目标,直到生成顶层目标。
《u-boot-2016.09 make编译过程分析(一)》分析依赖关系时采用自顶向下的方法,从顶层目标开始到最原始的依赖结束。
此处采用自下而上的方式,先从最原始的依赖开始,一步一步,执行命令生成目标。
1. prepare系列目标依赖
完整的prepare系列的目标依赖:
完整的<code>prepare</code>系列的目标依赖
依次从最右边的依赖说起:
1.1 scripts/kconfig/conf生成的文件
.config
.config在执行make rpi_3_32b_defconfig配置时生成,scripts/kconfig/Makefile中有规则:
%_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
这里展开后为:
rpi_3_32b_defconfig: scripts/kconfig/conf
scripts/kconfig/conf --defconfig=arch/../configs/rpi_3_32b_defconfig Kconfig
scripts/kconfig/conf会从根目录开始读取Kconfig,输出到根目录下的.config中。
include/generated/autoconf.h
include/config/auto.conf.cmd
include/config/tristate.conf
include/config/auto.conf
以上4个文件在执行make编译命令的开始会检查%.conf的依赖规则:
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
{ rm -f include/config/auto.conf; false; }
$(Q)touch include/config/auto.conf
调用make -f ./Makefile silentoldconfig的最终结果是执行scripts/kconfig/Makefile中的规则:
silentoldconfig: $(obj)/conf
$(Q)mkdir -p include/config include/generated
$< $(silent) --$@ $(Kconfig)
这个规则展开为:
silentoldconfig: scripts/kconfig/conf
mkdir -p include/config include/generated
scripts/kconfig/conf --silentoldconfig Kconfig
scripts/kconfig/conf会从根目录开始读取Kconfig,同时检查并更新配置阶段生成的.config文件,再把最终结果输出到以上的4个文件中。
所生成的4个文件中,include/config/auto.conf依赖于include/config/auto.conf.cmd,但是这里的依赖文件include/config/auto.conf.cmd文件并非由fixdep生成,而是直接由conf工具生成,算是*.cmd文件生成的特例。
scripts/kconfig/conf生成了图中右侧的依赖:include/config/auto.conf,$(KCONIFG_CONFIG)/.config和include/config/auto.conf.cmd
1.2 目标include/config/auto.conf的规则
在生成include/config/auto.conf的规则中:
# If .config is newer than include/config/auto.conf, someone tinkered
# with it and forgot to run make oldconfig.
# if auto.conf.cmd is missing then we are probably in a cleaned tree so
# we execute the config step to be sure to catch updated Kconfig files
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
@# If the following part fails, include/config/auto.conf should be
@# deleted so "make silentoldconfig" will be re-run on the next build.
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
{ rm -f include/config/auto.conf; false; }
@# include/config.h has been updated after "make silentoldconfig".
@# We need to touch include/config/auto.conf so it gets newer
@# than include/config.h.
@# Otherwise, 'make silentoldconfig' would be invoked twice.
$(Q)touch include/config/auto.conf
除了执行$(MAKE) -f $(srctree)/Makefile silentoldconfig外,还执行$(MAKE) -f $(srctree)/scripts/Makefile.autoconf
在scripts/Makefile.autoconf的头部是这样的:
# This helper makefile is used for creating
# - symbolic links (arch/$ARCH/include/asm/arch
# - include/autoconf.mk, {spl,tpl}/include/autoconf.mk
# - include/config.h
#
# When our migration to Kconfig is done
# (= When we move all CONFIGs from header files to Kconfig)
# this makefile can be deleted.
#
# SPDX-License-Identifier: GPL-2.0
#
__all: include/autoconf.mk include/autoconf.mk.dep
ifeq ($(shell grep -q '^CONFIG_SPL=y' include/config/auto.conf 2>/dev/null && echo y),y)
__all: spl/include/autoconf.mk
endif
ifeq ($(shell grep -q '^CONFIG_TPL=y' include/config/auto.conf 2>/dev/null && echo y),y)
__all: tpl/include/autoconf.mk
endif
此处没有设置CONFIG_SPL=y或CONFIG_TPL=y,所以整个makefile的__all的依赖有:
include/autoconf.mk
include/autoconf.mk.dep
然而include/autoconf.mk还要进一步依赖于config.h
1.2.1 include/config.h的规则
所有的autoconf.mk都依赖于include/config.h(rpi_3_32b_defconfig配置只需要include/autoconf.mk):
include/autoconf.mk include/autoconf.mk.dep \
spl/include/autoconf.mk tpl/include/autoconf.mk: include/config.h
实际上include/config.h由宏filechk_config_h生成:
# include/config.h
# Prior to Kconfig, it was generated by mkconfig. Now it is created here.
define filechk_config_h
(echo "/* Automatically generated - do not edit */"; \
for i in $$(echo $(CONFIG_SYS_EXTRA_OPTIONS) | sed 's/,/ /g'); do \
echo \#define CONFIG_$$i \
| sed '/=/ {s/=/ /;q; } ; { s/$$/ 1/; }'; \
done; \
echo \#define CONFIG_BOARDDIR board/$(if $(VENDOR),$(VENDOR)/)$(BOARD);\
echo \#include \<config_defaults.h\>; \
echo \#include \<config_uncmd_spl.h\>; \
echo \#include \<configs/$(CONFIG_SYS_CONFIG_NAME).h\>; \
echo \#include \<asm/config.h\>; \
echo \#include \<config_fallbacks.h\>;)
endef
include/config.h: scripts/Makefile.autoconf create_symlink FORCE
$(call filechk,config_h)
最终生成的include/config.h也比较简单,不妨看看:
/* Automatically generated - do not edit */
#define CONFIG_BOARDDIR board/raspberrypi/rpi
#include <config_defaults.h>
#include <config_uncmd_spl.h>
#include <configs/rpi.h>
#include <asm/config.h>
#include <config_fallbacks.h>
生成config.h之前,还要应用create_symlink生成相应的符号链接。
1.2.2 create_symlink的规则
# symbolic links
# If arch/$(ARCH)/mach-$(SOC)/include/mach exists,
# make a symbolic link to that directory.
# Otherwise, create a symbolic link to arch/$(ARCH)/include/asm/arch-$(SOC).
PHONY += create_symlink
create_symlink:
ifdef CONFIG_CREATE_ARCH_SYMLINK
ifneq ($(KBUILD_SRC),)
$(Q)mkdir -p include/asm
$(Q)if [ -d $(KBUILD_SRC)/arch/$(ARCH)/mach-$(SOC)/include/mach ]; then \
dest=arch/$(ARCH)/mach-$(SOC)/include/mach; \
else \
dest=arch/$(ARCH)/include/asm/arch-$(if $(SOC),$(SOC),$(CPU)); \
fi; \
ln -fsn $(KBUILD_SRC)/$$dest include/asm/arch
else
$(Q)if [ -d arch/$(ARCH)/mach-$(SOC)/include/mach ]; then \
dest=../../mach-$(SOC)/include/mach; \
else \
dest=arch-$(if $(SOC),$(SOC),$(CPU)); \
fi; \
ln -fsn $$dest arch/$(ARCH)/include/asm/arch
endif
endif
注释已经很好解释了create_symlink的行为:
如果arch/$(ARCH)/math-$(SOC)/include/mach存在,则生成符号链接:arch/$(ARCH)/include/asm/arch --> arch/$(ARCH)/math-$(SOC)
否则生成符号链接arch/$(ARCH)/include/asm/arch --> arch/$(ARCH)
对基于arm v7架构的bcm2837芯片,arch/arm/math-bcm283x文件夹存在,所以生成链接:
arch/arm/include/asm --> arch/arm/mach-bcm283x/include/mach
简单说来,create_symlink就是将芯片指定的arch/$(ARCH)math-$(SOC)连接到跟芯片名字无关的arch/$(ARCH)/include/asm下。
1.2.3 include/autoconf.mk的规则
# We are migrating from board headers to Kconfig little by little.
# In the interim, we use both of
# - include/config/auto.conf (generated by Kconfig)
# - include/autoconf.mk (used in the U-Boot conventional configuration)
# The following rule creates autoconf.mk
# include/config/auto.conf is grepped in order to avoid duplication of the
# same CONFIG macros
quiet_cmd_autoconf = GEN $@
cmd_autoconf = \
$(CPP) $(c_flags) $2 -DDO_DEPS_ONLY -dM $(srctree)/include/common.h > $@.tmp && { \
sed -n -f $(srctree)/tools/scripts/define2mk.sed $@.tmp | \
while read line; do \
if [ -n "${KCONFIG_IGNORE_DUPLICATES}" ] || \
! grep -q "$${line%=*}=" include/config/auto.conf; then \
echo "$$line"; \
fi \
done > $@; \
rm $@.tmp; \
} || { \
rm $@.tmp; false; \
}
include/autoconf.mk: FORCE
$(call cmd,autoconf)
从cmd_autoconf来看,这里会根据include/common.h的依赖,然后调用tools/scripts/define2mk.sed,并合并之前生成的include/config/auto.conf生成最终的autoconf.mk
1.2.4 include/autoconf.mk.dep的规则
quiet_cmd_autoconf_dep = GEN $@
cmd_autoconf_dep = $(CC) -x c -DDO_DEPS_ONLY -M -MP $(c_flags) \
-MQ include/config/auto.conf $(srctree)/include/common.h > $@ || { \
rm $@; false; \
}
include/autoconf.mk.dep: FORCE
$(call cmd,autoconf_dep)
这个规则比较简单,由于autoconf.mk由common.h和auto.conf而来,因此直接处理这两个文件的依赖并合并到autoconf.mk.dep中。
1.3 include/config/uboot.release
define filechk_uboot.release
echo "$(UBOOTVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))"
endef
# Store (new) UBOOTRELEASE string in include/config/uboot.release
include/config/uboot.release: include/config/auto.conf FORCE
$(call filechk,uboot.release)
命令$(call filechk,uboot.release)展开后就是调用宏filechk_uboot.release,最终将字符串2016.09写入include/config/uboot.release中。
1.4 timestamp.h和version.h的规则
version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h
...
# Generate some files
# ---------------------------------------------------------------------------
define filechk_version.h
(echo \#define PLAIN_VERSION \"$(UBOOTRELEASE)\"; \
echo \#define U_BOOT_VERSION \"U-Boot \" PLAIN_VERSION; \
echo \#define CC_VERSION_STRING \"$$(LC_ALL=C $(CC) --version | head -n 1)\"; \
echo \#define LD_VERSION_STRING \"$$(LC_ALL=C $(LD) --version | head -n 1)\"; )
endef
# The SOURCE_DATE_EPOCH mechanism requires a date that behaves like GNU date.
# The BSD date on the other hand behaves different and would produce errors
# with the misused '-d' switch. Respect that and search a working date with
# well known pre- and suffixes for the GNU variant of date.
define filechk_timestamp.h
(if test -n "$${SOURCE_DATE_EPOCH}"; then \
SOURCE_DATE="@$${SOURCE_DATE_EPOCH}"; \
DATE=""; \
for date in gdate date.gnu date; do \
$${date} -u -d "$${SOURCE_DATE}" >/dev/null 2>&1 && DATE="$${date}"; \
done; \
if test -n "$${DATE}"; then \
LC_ALL=C $${DATE} -u -d "$${SOURCE_DATE}" +'#define U_BOOT_DATE "%b %d %C%y"'; \
LC_ALL=C $${DATE} -u -d "$${SOURCE_DATE}" +'#define U_BOOT_TIME "%T"'; \
LC_ALL=C $${DATE} -u -d "$${SOURCE_DATE}" +'#define U_BOOT_TZ "%z"'; \
LC_ALL=C $${DATE} -u -d "$${SOURCE_DATE}" +'#define U_BOOT_DMI_DATE "%m/%d/%Y"'; \
else \
return 42; \
fi; \
else \
LC_ALL=C date +'#define U_BOOT_DATE "%b %d %C%y"'; \
LC_ALL=C date +'#define U_BOOT_TIME "%T"'; \
LC_ALL=C date +'#define U_BOOT_TZ "%z"'; \
LC_ALL=C date +'#define U_BOOT_DMI_DATE "%m/%d/%Y"'; \
fi)
endef
$(version_h): include/config/uboot.release FORCE
$(call filechk,version.h)
$(timestamp_h): $(srctree)/Makefile FORCE
$(call filechk,timestamp.h)
include/generated/version_autogenerated.h
根据include/config/uboot.release文件,规则调用filechk_version.h宏生成版本相关字符串文件include/generated/version_autogenerated.h,如下:
#define PLAIN_VERSION "2016.09"
#define U_BOOT_VERSION "U-Boot " PLAIN_VERSION
#define CC_VERSION_STRING "arm-linux-gnueabi-gcc (Ubuntu/Linaro 4.7.3-12ubuntu1) 4.7.3"
#define LD_VERSION_STRING "GNU ld (GNU Binutils for Ubuntu) 2.24"
include/generated/timestamp_autogenerated.h
调用宏filechk_timestamp.h生成编译的时间戳文件,如下:
#define U_BOOT_DATE "Oct 02 2016"
#define U_BOOT_TIME "21:54:42"
#define U_BOOT_TZ "+0800"
#define U_BOOT_DMI_DATE "10/02/2016"
1.5 outputmakefile的规则
PHONY += outputmakefile
# outputmakefile generates a Makefile in the output directory, if using a
# separate output directory. This allows convenient use of make in the
# output directory.
outputmakefile:
ifneq ($(KBUILD_SRC),)
$(Q)ln -fsn $(srctree) source
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif
如果编译没有设置O,即输出和代码都在同一个目录下,则outputmakefile的规则什么都不做;
如果编译指定了输出目录O,则调用scripts/mkmakefile在O选项指定的目录下生成一个简单的makefile
1.6 scripts_basic的规则
# Basic helpers built in scripts/
PHONY += scripts_basic
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
scripts_basic的执行结果就是编译生成scripts/basic/fixdep工具,该工具是u-boot编译系统中最常用的工具,用于在编译过程中修正每一个生成文件的依赖关系。
1.7 parepare0的规则
prepare0: archprepare FORCE
$(Q)$(MAKE) $(build)=.
展开后为:
prepare0: archprepare FORCE
make -f ./scripts/Makefile.build obj=.
编译时,命令make -f ./scripts/Makefile.build obj=.不会生成任何目标。
1.8 prepare系列目标总结
prepare阶段主要做了以下工作:
scripts_basic规则生成fixdep工具,用于对整个系统生成目标文件相应依赖文件的更新;
配置阶段,scripts/kconfig/conf根据传入的指定配置文件在根目录下生成.config文件
编译阶段,scripts/kconfig/conf读取配置阶段生成的.config,并检查最新配置生成以下文件:
include/generated/autoconf.h
include/config/auto.conf.cmd
include/config/tristate.conf
include/config/auto.conf
调用宏filechk_config_h生成include/config.h文件
调用命令cmd_autoconf_dep生成autoconf.mk和autoconf.mk.cmd文件
调用宏filechk_uboot.release生成include/config/uboot.release文件
调用宏filechk_version.h生成include/generated/version_autogenerated.h文件
调用宏filechk_timestamp.h生成include/generated/timestamp_autogenerated.h文件
调用宏create_symlink将芯片指定的arch/(ARCH)math−
(SOC)连接到跟芯片名字无关的arch/$(ARCH)/include/asm下
2. u-boot文件系列目标依赖
<code>u-boot</code>文件系列目标依赖关系
从图上可见,除了prepare依赖外,u-boot还依赖于文件$(head-y),$(libs-y)和$(LDSCRIPT),即依赖于:
启动文件arch/arm/cpu/$(CPU)/start.o
各个目录下的build-in.o
链接脚本文件arch/arm/cpu/u-boot.lds
2.1 启动文件start.o
$(head-y)在arch/arm/Makefile中被直接指定:
head-y := arch/arm/cpu/$(CPU)/start.o
在顶层makefile中被指定给变量u-boot-init:
u-boot-init := $(head-y)
2.2 各目录下的build-in.o
$(libs-y)在顶层的makefile中被指定为各个子目录下的build-in.o的集合:
libs-y += lib/
...
libs-y += fs/
libs-y += net/
libs-y += disk/
libs-y += drivers/
...
libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/)
libs-y := $(sort $(libs-y))
...
libs-y := $(patsubst %/, %/built-in.o, $(libs-y))
...
u-boot-main := $(libs-y)
以上脚本中,先将$(libs-y)设置为各子目录的集合,最后调用patsubst函数将$(libs-y)设置为这些目录下的built-in.o文件的集合,最后赋值给变量u-boot-main作为链接的主体文件。
各目录下的built-in.o是如何生成的呢?
以drivers/mmc/built-in.o为例,先查看生成的依赖文件drivers/mmc/.built-in.o.cmd:
cmd_drivers/mmc/built-in.o := arm-linux-gnueabi-ld.bfd -r -o drivers/mmc/built-in.o drivers/mmc/mmc_legacy.o drivers/mmc/bcm2835_sdhci.o drivers/mmc/mmc.o drivers/mmc/sdhci.o drivers/mmc/mmc_write.o
从生成命令cmd_drivers/mmc/built-in.o可以看到,built-in.o是由目录下各个编译生成的*.o文件通过链接操作ld -r而来。
ld的-r选项是什么作用呢?
在ld的手册中是这样介绍-r选项的:
-r
--relocatable
Generate relocatable output—i.e., generate an output file that can in turn serve as input to ld. This is often called partial linking. As a side effect, in environments that support standard Unix magic numbers, this option also sets the output file's magic number to OMAGIC. If this option is not specified, an absolute file is produced. When linking C++ programs, this option will not resolve references to constructors; to do that, use `-Ur'.
When an input file does not have the same format as the output file, partial linking is only supported if that input file does not contain any relocations. Different output formats can have further restrictions; for example some a.out-based formats do not support partial linking with input files in other formats at all.
This option does the same thing as `-i'.
简单说来,ld通过-r选项来产生可重定位的输出,相当于部分链接。
在这里就是通过ld -r选项将目录drivers/mmc/下的*.o文件先链接为单一文件build-in.o,但其并不是最终的生成文件,而是一个可进行重定位的文件.在下一阶段的链接中,ld会将各个目录下的built-in.o链接生成最终的u-boot。
built-in.o的规则
生成built-in.o的规则在scripts/Makefile.build中定义:
#
# Rule to compile a set of .o files into one .o file
#
ifdef builtin-target
quiet_cmd_link_o_target = LD $@
# If the list of objects to link is empty, just create an empty built-in.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)
targets += $(builtin-target)
endif # builtin-target
其中
ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif
objtree := .
src := $(srctree)
obj := $(objtree)
所以builtin-target=./built-in.o
2.3 链接脚本u-boot.lds
链接脚本的规则如下:
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)
2.4 生成u-boot规则
顶层Makefile中定义了生成u-boot文件的规则:
# 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
...
u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
$(call if_changed,u-boot__)
...
u-boot文件的生成很简单,调用ld命令,将$(u-boot-init)和$(u-boot-main)指定的一系列文件通过脚本u-boot.lds连接起来。
u-boot针对raspberry pi 3生成的命令是这样的(由于原命令太长,这里用\分割为多行):
arm-linux-gnueabi-ld.bfd -pie --gc-sections -Bstatic \
-Ttext 0x00008000 \
-o u-boot \
-T u-boot.lds \
arch/arm/cpu/armv7/start.o \
--start-group \
arch/arm/cpu/built-in.o \
arch/arm/cpu/armv7/built-in.o \
arch/arm/lib/built-in.o \
arch/arm/mach-bcm283x/built-in.o \
board/raspberrypi/rpi/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 \
...
lib/built-in.o \
net/built-in.o \
test/built-in.o \
test/dm/built-in.o \
--end-group \
arch/arm/lib/eabi_compat.o \
arch/arm/lib/lib.a \
-Map u-boot.map
生成了u-boot文件后,后续就是针对u-boot文件的各种处理了。
3. 顶层目标依赖
顶层目标依赖
显然,在生成了u-boot的基础上,进一步生成所需要的各种目标文件:
u-boot.srec
# Normally we fill empty space with 0xff
quiet_cmd_objcopy = OBJCOPY $@
cmd_objcopy = $(OBJCOPY) --gap-fill=0xff $(OBJCOPYFLAGS) \
$(OBJCOPYFLAGS_$(@F)) $< $@
...
OBJCOPYFLAGS_u-boot.hex := -O ihex
OBJCOPYFLAGS_u-boot.srec := -O srec
u-boot.hex u-boot.srec: u-boot FORCE
$(call if_changed,objcopy)
调用objcopy命令,通过-O ihex或-O srec指定生成u-boot.hex或u-boot.srec格式文件。
u-boot.sym
quiet_cmd_sym ?= SYM $@
cmd_sym ?= $(OBJDUMP) -t $< > $@
u-boot.sym: u-boot FORCE
$(call if_changed,sym)
调用$(OBJDUMP)命令生成符号表文件u-boot.sym。
System.map
SYSTEM_MAP = \
$(NM) $1 | \
grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
LC_ALL=C sort
System.map: u-boot
@$(call SYSTEM_MAP,$<) > $@
调用$(NM)命令打印u-boot文件的符号表,并用grep -v处理后得到System.map文件,里面包含了最终使用到的各个符号的位置信息。
u-boot.bin和u-boot-nodtb.bin
PHONY += dtbs
dtbs: dts/dt.dtb
@:
dts/dt.dtb: checkdtc u-boot
$(Q)$(MAKE) $(build)=dts dtbs
quiet_cmd_copy = COPY $@
cmd_copy = cp $< $@
ifeq ($(CONFIG_OF_SEPARATE),y)
u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
$(call if_changed,cat)
u-boot.bin: u-boot-dtb.bin FORCE
$(call if_changed,copy)
else
u-boot.bin: u-boot-nodtb.bin FORCE
$(call if_changed,copy)
endif
由于这里没有使用device tree设置,即编译没有定义CONFIG_OF_SEPARATE,因此u-boot.bin和u-boot-nodtb.bin是一样的。
至于生成u-boot-nodtb.bin的规则:
u-boot-nodtb.bin: u-boot FORCE
$(call if_changed,objcopy)
$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
$(BOARD_SIZE_CHECK)
显然,u-boot-nodtb.bin是u-boot文件通过objcopy得到。
u-boot.cfg
u-boot.cfg中包含了所有用到的宏定义,其生成规则如下:
# Create a file containing the configuration options the image was built with
quiet_cmd_cpp_cfg = CFG $@
cmd_cpp_cfg = $(CPP) -Wp,-MD,$(depfile) $(cpp_flags) $(LDPPFLAGS) -ansi \
-DDO_DEPS_ONLY -D__ASSEMBLY__ -x assembler-with-cpp -P -dM -E -o $@ $<
...
u-boot.cfg: include/config.h FORCE
$(call if_changed,cpp_cfg)
因此,阅读源码时如果不确定某个宏的值,可以检查u-boot.cfg文件。
自此,生成了所有的目标文件,完成了整个编译过程的分析。
————————————————
版权声明:本文为CSDN博主「guyongqiangx」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/guyongqiangx/article/details/52761678
u-boot-2016.09 make编译过程分析(二)的更多相关文章
- u-boot-2016.09 make编译过程分析(一)
https://blog.csdn.net/guyongqiangx/article/details/52565493 综述 u-boot自v2014.10版本开始引入KBuild系统,Makefil ...
- 2016.09.14,英语,《Using English at Work》全书笔记
半个月时间,听完了ESLPod出品的<Using English at Work>,笔记和自己听的时候的备注列在下面.准备把每个语音里的快速阅读部分截取出来,放在手机里反复听. 下一阶段把 ...
- [转]QGis2.9在windows下的编译以及二次开发包下载
今天心血来潮,将QGis在github上的代码更新后,又编译了一下.留意到源代码包里面的INSTALL文件有更新,于是本次编译完全基于官方的编译说明.编译过程非常顺利,除了在CMake的第一次conf ...
- Spring Boot 入门之 Web 篇(二)
原文地址:Spring Boot 入门之 Web 篇(二) 博客地址:http://www.extlight.com 一.前言 上一篇<Spring Boot 入门之基础篇(一)>介绍了 ...
- Java代码加密与反编译(二):用加密算法DES修改classLoader实现对.class文件加密
Java代码加密与反编译(二):用加密算法DES修改classLoader实现对.class文件加密 二.利用加密算法DES实现java代码加密 传统的C/C++自动带有保护机制,但java不同,只要 ...
- Spring Boot干货系列:(十二)Spring Boot使用单元测试(转)
前言这次来介绍下Spring Boot中对单元测试的整合使用,本篇会通过以下4点来介绍,基本满足日常需求 Service层单元测试 Controller层单元测试 新断言assertThat使用 单元 ...
- windows下使用VS2015编译V8 JavaScript引擎(v5.5 - 2016/09)
今天心血来潮, 下载了 v8,,然后就想着用vs编译 但是大家都苦恼的是 v8并不直接提供 vs用的项目文件和解决方案(.sln) 于是,在网上搜来搜去, 折腾来折腾去的; 终于一点一点的尝试, 可以 ...
- Linux移植之make uImage编译过程分析
编译出uboot可以运行的linux内核代码的命令是make uImage,下面详细介绍下生成linux-2.6.22.6/arch/arm/boot/uImage的过程: 1.vmlinux.Ima ...
- 跟vczh看实例学编译原理——二:实现Tinymoe的词法分析
文章中引用的代码均来自https://github.com/vczh/tinymoe. 实现Tinymoe的第一步自然是一个词法分析器.词法分析其所作的事情很简单,就是把一份代码分割成若干个tok ...
随机推荐
- flask环境布署--废弃不用,只留作备份
[前置条件] 创建1个flask-demo,生成requirement.txt文件(下载好gunicorn),上传至git.创建demo参照:创建一个flask api-demo(响应体显示中文) g ...
- Delphi驱动方式WINIO模拟按键 可用
http://www.delphitop.com/html/yingjian/152.html Delphi驱动方式WINIO模拟按键 作者:admin 来源:未知 日期:2010/2/1 1:14: ...
- laravel 配置双模板引擎
有时候我们可能有这种需求,pc 和 mobile 端显示的页面不一样,这个时候,我们就需要判断设备类型: ****我们用 composer require whichbrowser/parser ...
- unlink- ctf-stkof
stkof 程序下载:https://pan.baidu.com/s/1_dcm8OFjhKbKYWa3WBtAiQ 提取码:pkyb unlink 基础操作 # define unlink #def ...
- 一、Zabbix-学习列表
近期本人在求职,面试了几家,觉得监控是一个很重要的事情,所以决定深入学习一下监控.目前的监控系统有很多,Zabbix是目前应用最广泛的开源监控之一,功能比较完善,所以决定学习一下. 目前将学习zabb ...
- Cassandra视图
一.简介 Cassandra作为一个P2P结构的NOSQL数据库,使用与HBase不同的去中心化架构,在国外使用非常广泛,受欢迎程度甚至在Hbase之上.今天这篇文章介绍Cassandra在视图方面设 ...
- [转帖]Windows下cwRsyncServer双机连续同步部署
Windows下cwRsyncServer双机连续同步部署 https://www.cnblogs.com/nulige/p/7607503.html 找时间做一下测试 应该能更好的实现 自动部署的功 ...
- Python模块logging
基本用法: import logging import sys # 获取logger实例,如果参数为空则返回root logger logger = logging.getLogger("A ...
- Mybatis-学习笔记(7)缓存机制
1.一级缓存 SqlSession级别的缓存,使用HashMap存储缓存数据,不同的SqlSession之间的缓存数据区域(HashMap)互不影响. 一级缓存的作用域是SqlSession范围(强调 ...
- Android的Monkey和MonkeyRunner
本文部分解释性语段摘自网络百科或其它BLOG,语句内容网络随处可见,也不知道谁是初始原创,便不再署名出处,如有雷同,还请见谅. Monkey 什么是Monkey Monkey是Android中的一个命 ...