uboot Makefile analysis

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.

d465474f518995533031a54e358de4dc.png

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.

4fa5ff762c8769c0d348866e90dc8d55.png

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.

fab58fc4816df88cd2deafc8523345a9.jpeg