Understanding U-Boot Makefile

1. Compiling U-Boot

First, let’s review how to compile U-Boot.

(1) Set temporary environment variables

export ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

(2) Specify board configuration and generate .config file

make xxx_defconfig

(3) Compile

make -j8

(4) Clean build

make distclean

Next, we will analyze the top-level Makefile in the U-Boot root directory to explore what happens behind these three compilation commands.

2. Makefile Part One – Make Related Settings

1. U-Boot 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 behaviour);
# o Look for make include files relative to root of kernel src
MAKEFLAGS += -rR --include-dir=$(CURDIR)

#  +=: Appending value to MAKEFLAGS variable
#  -rR: Prohibits using make's built-in implicit rules and variable definitions
#  --include-dir: Specifies search path
#  $(CURDIR): Represents the current directory

3. Recursive Build Settings

# Avoid funny character set dependencies
# Avoid character set dependencies
unexport LC_ALL
LC_COLLATE=C
LC_NUMERIC=C
export LC_COLLATE LC_NUMERIC

# Avoid interference with shell env settings
# Avoid interference with shell environment settings
unexport GREP_OPTIONS

Make supports recursive builds, calling Makefiles in subdirectories to complete the compilation of subdirectories. The syntax is as follows:

$(MAKE) -C subdir

When calling a sub-Makefile, you can specify which variables to pass using export and which to not pass using unexport. However, the SHELL variable and MAKEFLAGS are passed to the sub-Makefile by default unless explicitly unexported.

4. Beautifying Output

# Use 'make V=1' to see the full commands
# Use 'make V=1' to see the complete commands executed by U-Boot
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), suppress command echoing
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) Check if the user specified variable V

The origin function in the Makefile is used to check where a variable comes from. If variable V is defined in the command line, its origin is “command line”, and thus its value is assigned to KBUILD_VERBOSE, determining whether the log output is detailed.

If the user does not define variable V, KBUILD_VERBOSE defaults to 0.

(2) Control make log output

The Makefile uses a clever operation to control whether to output the complete command to the terminal using the variables quiet and Q, for example:

$(Q)$(MAKE) $(build)=scripts/basic

If V=1, Q is empty, and this command will be fully output to the terminal when executed; if V=0, Q=@, the command becomes:

@make $(build) = scripts/basic

At this point, the command execution will not be output to the terminal.

(3) Silent output

In the case of V=0, the U-Boot terminal displays a short command, but logs are still output. Using the make -s option sets silent output, with no logs at all.

(4) Export variables quiet, Q, KBUILD_VERBOSE to sub-Makefiles to maintain the same log settings.

5. Set 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)
# KBUILD_SRC is set when make is invoked in the OBJ directory
# KBUILD_SRC is not intended to be used by the regular user (for now)

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
# When no parameters are passed on the command line, this is our default target

PHONY := _all
_all:

# Cancel implicit rules on top Makefile
# Cancel implicit rules in the top-level 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 a second make in the output directory, passing relevant variables to check if the output directory actually exists
saved-output := $(KBUILD_OUTPUT)
KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \
                && /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 the compiled output files to a separate directory. To locate output files in a separate directory, two syntaxes are supported:

(1) O=


make O=dir/to/store/output/files/

(2) Set KBUILD_OUTPUT environment variable


export KBUILD_OUTPUT=dir/to/store/output/files/
make

Using O= takes precedence over setting the KBUILD_OUTPUT environment variable.

6. Code Checking

# Call a source code checker (by default, "sparse") as part of the
# C compilation.
# Call a source code checker (default is "sparse") as part of 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 the C= parameter to enable code checking: 1: Check files that need recompilation; 2: Check all source files.

Similarly, if the C parameter comes from the command line, it assigns the value to the environment variable KBUILD_CHECKSRC; if not, KBUILD_CHECKSRC defaults to 0.

7. Module Compilation

# If building an external module we do not care about the all: rule
# but instead _all depend on modules
# If building an external module, we do not care about the all: rule
# but instead _all depends on modules
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 M=dir parameter to specify the output directory for external module compilation. If the command line has the M parameter, its value is assigned to KBUILD_EXTMOD; if not, KBUILD_EXTMOD is empty, and it directly compiles all.

3. Makefile Part Two – Preparation Before Compilation

1. Get Host CPU Architecture and Operating System

# Get 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 host operating system
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
      sed -e 's/\(cygwin\).*/cygwin/')

export  HOSTARCH HOSTOS

2. Set Compiler and Configuration File

Do you remember? U-Boot needs to specify the values of ARCH and CROSS_COMPILE before compiling, which will come into play here!

Compare the HOSTARCH obtained in the previous step with the user-specified ARCH:

(1) If equal, it means compiling on the local machine, directly using the local compiler, no cross-compiler needed, set to empty; KCONFIG_CONFIG indicates 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 OS is Darwin (the kernel of MAC OS), related settings are made.

# 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 Generic Definitions

# We need some generic definitions (do not try to remake the file).
# We need some generic 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. Create Variables – Full Name of Compiler

# Make variables (CC, etc...)
# Create 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. If the value of environment variable CROSS_COMPILE is arm-linux-gnueabi-hf-, the resulting compiler will be:

CC = arm-linux-gnueabi-hf-gcc

The names of the other tools are similarly derived.

5. Export Required Variables for 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 these variables have already been defined, while others have not appeared, such as the variables on the second line, which come from the config.mk file 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"

From the actual compilation printout, these variables have the following actual values:

At this point, you should understand why U-Boot needs to set the ARCH and CROSS_COMPILE environment variables before compilation.

export ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

4. The Process Behind make xxx_defconfig

1. %config Target Dependency

First, match if the parameter of the make command is config or xxx_config, then 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 config-targets flag is set to 1, it enters 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

You can see that the xxx_config target is %config, with dependencies being scripts_basic outputmakefile FORCE. The build command is:

$(Q)$(MAKE) $(build)=scripts/kconfig $@

(1) The scripts_basic dependency can be identified in the Makefile:

# Rules shared between *config targets and build targets
# Shared rules between *config targets and build targets
# Basic helpers built in scripts/
PHONY += scripts_basic
scripts_basic:
  $(Q)$(MAKE) $(build)=scripts/basic
  $(Q)rm -f .tmp_quiet_recordmcount

Here, 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, 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

Since KBUILD_SRC is empty, the value of srctree is .

In summary, the build rule for scripts_basic can be expanded as follows:

make -f ./scripts/Makefile.build obj=scripts/basic

This command will further call the Makefile.build script, which we will analyze later.

(2) The outputmakefile dependency can also be identified 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 build rule of this dependency, it first checks if KBUILD_SRC is empty. From previous analysis, this variable is empty, so this dependency build is not useful.

(3) The FORCE dependency can also be identified in the Makefile:

PHONY += FORCE
FORCE:

FORCE has no rules or dependencies, so it will always be regenerated. When FORCE is a dependency of other targets, the rules containing it will always execute because FORCE is always updated.

Now that we have analyzed the three dependencies, we can expand the %config build rule as follows, where #@ is shell script syntax, representing the entire list of parameters passed in.

make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig

It can be seen that the first two commands are building the scripts_basic dependency, while the third command is building the %config target.

2. Makefile.build Script

(1) scripts_basic target

make -f ./scripts/Makefile.build obj=scripts/basic

Function: Compile the 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 its contents to the configuration file .config, generating the .config file.

At this point, you should understand how the make xxx_defconfig generates the .config file before compiling U-Boot.

5. U-Boot Compilation Process

In the previous four chapters, the top-level Makefile of U-Boot has set various variables needed for compilation and generated the .config file. Everything is ready, and now we start compiling U-Boot to generate the executable file.

1. Dependency Relationships

(1) all target

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) Dependencies of the all target

# 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 whether the user is using the 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 u-boot target depends on u-boot-init, u-boot-main, and u-boot.lds.

u-boot-init and u-boot-main are two variables defined 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 corresponding architecture’s sub-Makefile, for example, defined as follows in arch/arm/Makefile:

head-y := arch/arm/cpu/$(CPU)/start.o

libs-y 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))

The code snippet shows that libs-y is a collection of built-in.o files from various subdirectories of U-Boot.

u-boot.lds is located in each architecture directory, for example, arch/arm/cpu/u-boot.lds.

In summary, the u-boot target links u-boot.lds as the linking script, linking arch/arm/cpu/armv7/start.o and built-in.o files from various subdirectories to generate U-Boot.

2. How built-in.o Files Are Generated

Taking driver/gpio/built-in.o as an example, there is a file named .built-in.o.cmd in the drivers/gpio/ directory, which contains the following:

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 drivers/gpio/built-in.o and drivers/gpio/mxc_gpio.o; mxc_gpio.c is the GPIO driver file for the NXP i.MX series.

The -r parameter is a linker parameter indicating the generation of a relocatable output, used for linking a small number of files.

Finally, all built-in.o files from various subdirectories are linked together, and then linked with arch/arm/cpu/armv7/start.o to form the U-Boot target.

Understanding U-Boot Makefile

Leave a Comment