1. Compilation of uboot
First, let’s review how uboot is compiled.
(1) Set temporary environment variables
export ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
(2) Specify board-level configuration and generate .config file
make xxx_defconfig
(3) Compile
make -j8
(4) Clear build
make distclean
Next, we analyze the top-level Makefile file in the uboot root directory to explore what happens behind these three compilation commands.
2. The first part of the Makefile – make related settings
1. uboot version information
VERSION = 2016 PATCHLEVEL = 03 SUBLEVEL = EXTRAVERSION = NAME = # VERSION: major version number # PATCHLEVEL: patch version number #SUBLEVEL: minor version number # EXTRAVERSION: Additional version information # NAME: name
2. MAKEFLAGS
# o Do not use make's built-in rules and variables # (this increases performance and avoids hard-to-debug behavior); # o Look for make include files relative to root of kernel src MAKEFLAGS + = -rR --include-dir=$(CURDIR) # + =: append value to MAKEFLAGS variable # -rR: Disable the use of make's built-in implicit rules and variable definitions # --include-dir: Specify the search path # $(CURDIR): indicates the current directory
3. Recursive build settings
# Avoid funny character set dependencies # Avoid interesting character set dependencies unexport LC_ALL LC_COLLATE=C LC_NUMERIC=C export LC_COLLATE LC_NUMERIC # Avoid interference with shell env settings # Avoid interfering with shell environment settings unexport GREP_OPTIONS
make supports recursive building. Call the Makefile in a subdirectory to complete the compilation of the subdirectory. Use the -C parameter to specify the subdirectory. The syntax is as follows:
$(MAKE) -C subdir
When calling a sub-Makefile, you can use export to specify which variables are passed, and unexport to specify which variables are not passed. However, the SHELL variable and MAKEFLAGS will be passed to the sub-Makefile by default, unless unexport is used to explicitly specify not to pass.
4. Beautify output
# Use 'make V=1' to see the full commands # Use 'make V=1' to see the complete command executed by uboot ifeq ("$(origin V)", "command line") KBUILD_VERBOSE = $(V) endif ifndef KBUILD_VERBOSE KBUILD_VERBOSE = 0 endif ifeq ($(KBUILD_VERBOSE),1) quiet = Q= else quiet=quiet_ Q=@ endif # If the user is running make -s (silent mode), suppress echoing of #commands # If the user runs make -s (silent mode), disable command echo ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4 ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),) quiet=silent_ endif else #make-3.8x ifneq ($(filter s% -s%,$(MAKEFLAGS)),) quiet=silent_ endif endif export quiet Q KBUILD_VERBOSE
(1) Determine whether the user specifies the value of variable V
The function origin in the Makefile is used to get where the variable comes from. If the variable V is defined on the command line, then its source is “command line”, and then assign the value of variable V to KBUILD_VERBOSE, and set whether the log output detailed.
If the user does not define variable V, the default value of KBUILD_VERBOSE is equal to 0.
(2) Control make log output
A rather cool operation is used in the Makefile to use the variables quier and Q to control whether to output the complete command in the terminal, for example:
$(Q)$(MAKE) $(build)=scripts/basic
If Q is empty when V=1, this command will be completely output on the terminal when executed; when V=0, Q=@, the command becomes:
@make $(build) = scripts/basic
At this time, the command execution will not be output to the terminal.
(3) Silent output
In the case of V=0, short commands are displayed in the uboot terminal, but logs are still output. Use the make -s parameter to set silent output, and there will be no logs.
(4) Export variables quiet, Q, and KBUILD_VERBOSE to sub-Makefiles so that they maintain the same log settings.
5. Set the compilation output directory
# KBUILD_SRC is set on invocation of make in OBJ directory # KBUILD_SRC is not intended to be used by the regular user (for now) # Set KBUILD SRC when calling make in the OBJ directory # KBUILD SRC is not intended to be used by regular users (yet) ifeq ($(KBUILD_SRC),) # OK, Make called in directory where kernel src resides # Do we want to locate output files in a separate directory? # Set KBUILD_OUTPUT environment variable ifeq ("$(origin O)", "command line") KBUILD_OUTPUT := $(O) endif # That's our default target when none is given on the command line # This is our default target when no parameters are passed on the command line PHONY := _all _all: # Cancel implicit rules on top Makefile # Cancel the implicit rules of the top Makefile $(CURDIR)/Makefile Makefile: ; ifneq ($(KBUILD_OUTPUT),) # Invoke a second make in the output directory, passing relevant variables # check that the output directory actually exists # Call the second make in the output directory, passing relevant variables to check whether the output directory actually exists saved-output := $(KBUILD_OUTPUT) KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) & amp; & amp; cd $(KBUILD_OUTPUT) \ & amp; & amp; /bin/pwd) $(if $(KBUILD_OUTPUT),, \ $(error failed to create output directory "$(saved-output)")) PHONY + = $(MAKECMDGOALS) sub-make $(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make @: sub-make: FORCE $(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \ -f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS)) # Leave processing to above invocation of make skip-makefile := 1 endif # ifneq ($(KBUILD_OUTPUT),) endif # ifeq ($(KBUILD_SRC),)
kbuild supports saving compiled output files to a separate directory. In order to locate output files in a separate directory, two syntaxes are supported:
(1)O=
make O=dir/to/store/output/files/
(2) Set the KBUILD_OUTPUT environment variable
export KBUILD_OUTPUT=dir/to/store/output/files/ make
Use O= to specify a higher priority than the KBUILD_OUTPUT environment variable.
6. Code inspection
# Call a source code checker (by default, "sparse") as part of the # C compilation. # Invoke the source code inspector ("sparse" by default) as part of the C compilation. # # Use 'make C=1' to enable checking of only re-compiled files. # Use 'make C=2' to enable checking of *all* source files, regardless # of whether they are re-compiled or not. # # See the file "Documentation/sparse.txt" for more details, including # where to get the "sparse" utility. ifeq ("$(origin C)", "command line") KBUILD_CHECKSRC = $(C) endif ifndef KBUILD_CHECKSRC KBUILD_CHECKSRC = 0 endif
Use parameter C= to enable code inspection:
1: Check the files that need to be recompiled
2: Check all source code files
Similarly, if the parameter C comes from the command line, assign C to the environment variable KBUILD_CHECKSRC. If not, the variable KBUILD_CHECKSRC is 0.
7. Module compilation
# If building an external module we do not care about the all: rule # but instead _all depend on modules # If module compilation is specified, compile the module first and then compile all PHONY + = all ifeq ($(KBUILD_EXTMOD),) _all: all else _all: modules endif ifeq ($(KBUILD_SRC),) # building in the source tree srctree := . else ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR))) # building in a subdirectory of the source tree srctree := .. else srctree := $(KBUILD_SRC) endif endif objtree := . src := $(srctree) obj := $(objtree) VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD)) export srctree objtree VPATH
Use the parameter M=dir to specify the external module compilation output directory. If the command line has the M parameter, assign the value of M to KBUILD_EXTMOD. If not, KBUILD_EXTMOD is empty and compile all directly.
3. Makefile Part 2 – Preparation before compilation
1. Obtain the CPU architecture and operating system of the host
# Get the host architecture HOSTARCH := $(shell uname -m | \ sed -e s/i.86/x86/ \ -e s/sun4u/sparc64/ \ -e s/arm.*/arm/ \ -e s/sa110/arm/ \ -e s/ppc64/powerpc/ \ -e s/ppc/powerpc/ \ -e s/macppc/powerpc/\ -e s/sh.*/sh/) # Get the host operating system HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \ sed -e 's/\(cygwin\).*/cygwin/') export HOSTARCH HOSTOS
2. Set up the compiler and configuration files
do you remember? uboot needs to specify the values of ARCH and CROSS_COMPILE before compilation, which will come in handy here!
Compare the HOSTARCH obtained in the previous step with the user-specified ARCH:
(1) If equal, it means compiling on the local machine and using the local compiler directly. The cross-compiler is not required and is set to empty; KCONFIG_CONFIG means using the .config configuration file.
# set default to nothing for native builds ifeq ($(HOSTARCH),$(ARCH)) CROSS_COMPILE ?= endif KCONFIG_CONFIG ?= .config export KCONFIG_CONFIG # SHELL used by kbuild CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ else echo sh; fi ; fi) HOSTCC=cc HOSTCXX=c++ HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer HOSTCXXFLAGS = -O2 ifeq ($(HOSTOS),cygwin) HOSTCFLAGS + = -ansi endif
(2) If the host operation is darwin (the kernel of MAC OS), make relevant settings.
# Mac OS X / Darwin's C preprocessor is Apple specific. It # generates numerous errors and warnings. We want to bypass it # and use GNU C's cpp. To do this we pass the -traditional-cpp # option to the compiler. Note that the -traditional-cpp flag # DOES NOT have the same semantics as GNU C's flag, all it does # is invoke the GNU preprocessor in stock ANSI/ISO C fashion. # # Apple's linker is similar, thanks to the new 2 stage linking # multiple symbol definitions are treated as errors, hence the # -multiply_defined suppress option to turn off this error. # ifeq ($(HOSTOS),darwin) # get major and minor product version (e.g. '10' and '6' for Snow Leopard) DARWIN_MAJOR_VERSION = $(shell sw_vers -productVersion | cut -f 1 -d '.') DARWIN_MINOR_VERSION = $(shell sw_vers -productVersion | cut -f 2 -d '.') os_x_before = $(shell if [ $(DARWIN_MAJOR_VERSION) -le $(1) -a \ $(DARWIN_MINOR_VERSION) -le $(2) ] ; then echo "$(3)"; else echo "$(4)"; fi ;) # Snow Leopards build environment has no longer restrictions as described above HOSTCC = $(call os_x_before, 10, 5, "cc", "gcc") HOSTCFLAGS + = $(call os_x_before, 10, 4, "-traditional-cpp") HOSTLDFLAGS + = $(call os_x_before, 10, 5, "-multiply_defined suppress") # since Lion (10.7) ASLR is on by default, but we use linker generated lists # in some host tools which is a problem then ... so disable ASLR for these #tools HOSTLDFLAGS + = $(call os_x_before, 10, 7, "", "-Xlinker -no_pie") endif
3. Introduce some common definitions
# We need some generic definitions (do not try to remake the file). # We need some common definitions scripts/Kbuild.include: ; include scripts/Kbuild.include
The Kbuild.include file contains some definitions that will be used in the subsequent compilation process.
4. Making variables – full name of the compiler
# Make variables (CC, etc...) # Make variables AS = $(CROSS_COMPILE)as # Always use GNU ld ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),) LD = $(CROSS_COMPILE)ld.bfd else LD = $(CROSS_COMPILE)ld endif CC = $(CROSS_COMPILE)gcc CPP = $(CC)-E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm LDR = $(CROSS_COMPILE)ldr STRIP = $(CROSS_COMPILE)strip OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump AWK = awk PERL = perl PYTHON = python DTC = dtc CHECK = sparse
If it is cross-compilation, this is very important. The value of the environment variable CROSS_COMPILE is arm-linux-gnueabi-hf-, then the produced compiler is:
CC = arm-linux-gnueabi-hf-gcc
The names of the other tools are the same.
5. Export variables required for compilation to the sub-Makefile
export VERSION PATCHLEVEL SUBLEVEL UBOOTRELEASE UBOOTVERSION export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR export CONFIG_SHELL HOSTCC HOSTCFLAGS HOSTLDFLAGS CROSS_COMPILE AS LD CC export CPP AR NM LDR STRIP OBJCOPY OBJDUMP export MAKE AWK PERL PYTHON export HOSTCXX HOSTCXXFLAGS DTC CHECK CHECKFLAGS export KBUILD_CPPFLAGS NOSTDINC_FLAGS UBOOTINCLUDE OBJCOPYFLAGS LDFLAGS export KBUILD_CFLAGS KBUILD_AFLAGS
Some of the variables have been defined, and some have never appeared, such as the variables in the second line, and these variables come from config.mk in the root directory:
ARCH := $(CONFIG_SYS_ARCH:"%"=%) CPU := $(CONFIG_SYS_CPU:"%"=%) ifdef CONFIG_SPL_BUILD ifdef CONFIG_TEGRA CPU := arm720t endif endif BOARD := $(CONFIG_SYS_BOARD:"%"=%) ifneq ($(CONFIG_SYS_VENDOR),) VENDOR := $(CONFIG_SYS_VENDOR:"%"=%) endif ifneq ($(CONFIG_SYS_SOC),) SOC := $(CONFIG_SYS_SOC:"%"=%) endif
The CONFIG_SYS_xxx variables here come from the configuration file .config, as follows:
CONFIG_SYS_ARCH="arm" CONFIG_SYS_CPU="armv7" CONFIG_SYS_SOC="mx6" CONFIG_SYS_VENDOR="freescale" CONFIG_SYS_BOARD="mx6ullatk" CONFIG_SYS_CONFIG_NAME="mx6ullatk"
After printing during actual compilation, the actual values of these variables are as follows:
At this point, you should have understood why you need to set the two environment variables ARCH and CROSS_COMPILE before compiling uboot.
export ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
4. The process behind make xxx_defconfig
1. %config target dependencies
First, match. If the parameter matching the make command is config or xxx_config, set the flag config-targets to 1:
ifeq ($(KBUILD_EXTMOD),) ifneq ($(filter config %config,$(MAKECMDGOALS)),) config-targets := 1 ifneq ($(words $(MAKECMDGOALS)),1) mixed-targets := 1 endif endif endif
If the flag config-targets is 1, enter the corresponding target:
ifeq ($(config-targets),1) # ================================================= ========================== # *config targets only - make sure prerequisites are updated, and descend # in scripts/kconfig to make the *config target KBUILD_DEFCONFIG := sandbox_defconfig export KBUILD_DEFCONFIG KBUILD_KCONFIG config: scripts_basic outputmakefile FORCE $(Q)$(MAKE) $(build)=scripts/kconfig $@ %config: scripts_basic outputmakefile FORCE $(Q)$(MAKE) $(build)=scripts/kconfig $@ else
As you can see, the target of xxx_config is %config, the dependencies are scripts_basic outputmakefile FORCE, and the build command is:
$(Q)$(MAKE) $(build)=scripts/kconfig $@
(1) scripts_basic dependency, the definition of this dependency can be found in the Makefile:
# Rules shared between *config targets and build targets # *Sharing rules between config target and compile target # Basic helpers built in scripts/ PHONY + = scripts_basic scripts_basic: $(Q)$(MAKE) $(build)=scripts/basic $(Q)rm -f .tmp_quiet_recordmcount
Among them, Q is used for log printing, MAKE is make, and build is defined in the scripts/Kbuild file:
### # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj= #Usage: # $(Q)$(MAKE) $(build)=dir build := -f $(srctree)/scripts/Makefile.build obj
The srctree variable is passed from the top-level Makefile and is defined as follows:
ifeq ($(KBUILD_SRC),) # building in the source tree srctree := . else ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR))) # building in a subdirectory of the source tree srctree := .. else srctree := $(KBUILD_SRC) endif endif
Because the KBUILD_SRC variable is empty, the value of the srctree variable is.
In summary, the construction rules of scripts_basic can be expanded as follows:
make -f ./scripts/Makefile.build obj=scripts/basic
This line of command will then call the Makefile.build script, which will be analyzed later.
(2) Outputmakefile dependency, the definition of this dependency can also be found in the Makefile:
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
In the construction rules of this dependency, first determine whether KBUILD_SRC is empty. After the previous analysis, the variable is empty, so the dependency construction is useless.
(3) FORCE dependency, the definition of this dependency can also be found in the Makefile:
PHONY + = FORCE FORCE:
FORCE has no rules and dependencies, so FORCE will be regenerated every time. When FORCE is used as a dependency on other targets, since FORCE is always updated, the rules where the dependency is located will always be executed.
Okay, after analyzing the three dependencies, let’s expand the construction rules of %config as follows, where #@ is the shell script syntax, which represents the list of all parameters passed in.
make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
As you can see, this command also calls the ./scripts/Makefile.build script to build, and will be analyzed later.
As you can see, the 1st and 2nd lines of commands are building the scripts_basic dependency, and the 3rd line of commands is building the %config target.
2.Makefile.build script
(1) scripts_basic target
make -f ./scripts/Makefile.build obj=scripts/basic
Function: Compile script/basic/fixdep software.
(2)%config target
make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
Function: Use the xxx_defconfig file to output the contents of the xxx_defconfig file to the configuration file .config and generate the .config.
At this point, you should have understood how make xxx_defconfig generates the .config file before uboot is compiled.
5. uboot compilation process
In the previous four chapters, the top-level Makefile of uboot has set up various variables required for compilation and generated the .config file. Everything is ready. Next, start compiling uboot and generate an executable file.
1. Dependencies
(1) all goals
all: $(ALL-y) ifneq ($(CONFIG_SYS_GENERIC_BOARD),y) @echo "====================== WARNING ======================" @echo "Please convert this board to generic board." @echo "Otherwise it will be removed by the end of 2014." @echo "See doc/README.generic-board for further information" @echo "============================================== ======" endif ifeq ($(CONFIG_DM_I2C_COMPAT),y) @echo "====================== WARNING ======================" @echo "This board uses CONFIG_DM_I2C_COMPAT. Please remove" @echo "(possibly in a subsequent patch in your series)" @echo "before sending patches to the mailing list." @echo "============================================== ======" endif
(2) ALL-y that all targets depend on
# Always append ALL so that arch config.mk's can add custom ones ALL-y + = u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check ALL-$(CONFIG_ONENAND_U_BOOT) + = u-boot-onenand.bin ifeq ($(CONFIG_SPL_FSL_PBL),y) ALL-$(CONFIG_RAMBOOT_PBL) + = u-boot-with-spl-pbl.bin else ifneq ($(CONFIG_SECURE_BOOT), y) # For Secure Boot The Image needs to be signed and Header must also # be included. So The image has to be built explicitly ALL-$(CONFIG_RAMBOOT_PBL) + = u-boot.pbl endif endif ALL-$(CONFIG_SPL) + = spl/u-boot-spl.bin ALL-$(CONFIG_SPL_FRAMEWORK) + = u-boot.img ALL-$(CONFIG_TPL) + = tpl/u-boot-tpl.bin ALL-$(CONFIG_OF_SEPARATE) + = u-boot.dtb ifeq ($(CONFIG_SPL_FRAMEWORK),y) ALL-$(CONFIG_OF_SEPARATE) + = u-boot-dtb.img endif ALL-$(CONFIG_OF_HOSTFILE) + = u-boot.dtb ifneq ($(CONFIG_SPL_TARGET),) ALL-$(CONFIG_SPL) + = $(CONFIG_SPL_TARGET:"%"=%) endif ALL-$(CONFIG_REMAKE_ELF) + = u-boot.elf ALL-$(CONFIG_EFI_APP) + = u-boot-app.efi ALL-$(CONFIG_EFI_STUB) + = u-boot-payload.efi ifneq ($(BUILD_ROM),) ALL-$(CONFIG_X86_RESET_VECTOR) + = u-boot.rom endif
(3)u-boot.bin target
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
This distinguishes the user’s device tree. In this article, imx6ull is not used, so it depends on the u-boot-nodtb.bin file.
(4)u-boot-nodtb.bin target
u-boot-nodtb.bin: u-boot FORCE $(call if_changed,objcopy) $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE)) $(BOARD_SIZE_CHECK)
(5)u-boot target
u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE $(call if_changed,u-boot__) ifeq ($(CONFIG_KALLSYMS),y) $(call cmd,smap) $(call cmd,u-boot__) common/system_map.o endif
The target u-boot depends on u-boot-init, u-boot-main and u-boot.lfs.
u-boot-init and u-boot-main are two variables in the top-level Makefile:
u-boot-init := $(head-y) u-boot-main := $(libs-y)
head-y is not defined. This variable is related to the CPU architecture and is defined in the sub-Makefile under the relevant architecture. For example, the arch/arm/Makefile is defined as follows:
head-y := arch/arm/cpu/$(CPU)/start.o
libs-y is in the top-level Makefile:
libs-y + = lib/ libs-y + = lib/ libs-$(HAVE_VENDOR_COMMON_LIB) + = board/$(VENDOR)/common/ libs-$(CONFIG_OF_EMBED) + = dts/ libs-y + = fs/ libs-y + = net/ libs-y + = disk/ libs-y + = drivers/ libs-y + = drivers/dma/ libs-y + = drivers/spi/ #... libs-y + = cmd/ libs-y + = common/ libs-$(CONFIG_API) + = api/ libs-$(CONFIG_HAS_POST) + = post/ libs-y + = test/ libs-y + = test/dm/ libs-$(CONFIG_UT_ENV) + = test/env/ libs-y + = $(if $(BOARDDIR),board/$(BOARDDIR)/) libs-y := $(sort $(libs-y)) u-boot-dirs := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples u-boot-alldirs := $(sort $(u-boot-dirs) $(patsubst %/,%,$(filter %/, $(libs-)))) libs-y := $(patsubst %/, %/built-in.o, $(libs-y))
As can be seen from the code snippet, libs-y is a collection of built-in.o in each subdirectory of uboot.
u-boot.lds is in each architecture directory, such as arch/arm/cpu/u-boot.lds.
In summary, the u-boot goal is to use u-boot.lds as the link script to link arch/arm/cpu/armv7/start.o and built-in.o in each subdirectory to generate u-boot.
2. How to generate built-in.o file
Taking driver/gpio/built-in.o as an example, there is a file named .built-in.o.cmd in the drivers/gpio/ directory with the following content:
cmd_drivers/gpio/built-in.o := arm-linux-gnueabihf-ld.bfd -r -o drivers/gpio/built-in.o drivers/gpio/mxc_gpio.o
It can be seen that built-in.o is further linked from the drivers/gpio/built-in.o drivers/gpio/mxc_gpio.o file. mxc_gpio.c is the GPIO driver file of the NXP i.MX series.
The -r parameter is a linker parameter that generates redirectable output for linking a small number of files.
Finally, link all the built-in.o files in each subdirectory together, and then link them with arch/arm/cpu/armv7/start.o to form the u-boot target.