u-boot-2016.09 make compilation process Analysis (i) overview
U-boot has introduced the Kbuild system since the v2014.10 version, and makefile management and organization are very different from previous versions of the code, and their makefile is more complex. The whole makefile, nested a lot of other different uses of the makefile, a variety of goals and dependencies are also many, make analysis is easy to fall into, let a person feel confused.
U-boot's compilation, like Kernel compilation, is performed in two steps:
-First step: Configure, execute make xxx_defconfig to configure, generate. config file
-Part II: Compile, execute make to compile, generate executable binaries U-boot.bin or u-boot.elf
The previous blog, "u-boot-2016.09 make configuration Process analysis," explained in detail the first step, in which U-boot performs the configuration command make Xxx_defconfig collects all the default Kconfig configurations and then specifies the XXX on the command line The _defconfig configuration is updated and exported to the. config file in the root directory.
This article looks at the second step, that is, the process of executing the make command to generate a binary file after the configuration is complete, and because of the many dependencies and commands involved, the make compilation process analysis is divided into two parts, target dependencies, and command execution.
The core of makefile is dependency and command. For each goal, the dependency is checked first, and if the dependency exists, the command update target is executed, and if the dependency does not exist, the dependency is the target, and the husband is dependent, and after the build, the command generation target is executed. The first part, the target dependence
Now to analyze the dependencies of the U-boot compilation execution make command.
The goal-dependent analysis takes a top-down approach, starting at the top level and decomposing each layer of dependency down and down until the location cannot be decomposed. 1. The top level target relies on a). _all and all dependencies on $ (all-y)
Starting from the top makefile, the first thing to find is the _all pseudo target:
# That's our we default target when none is given on the command line
phony: = _all
_all:
Then the dependency on the _all pseudo target is added to the all pseudo target:
# If Building an external module we don't care about the All:rule
# but instead _all depend on modules
phony = All
ifeq ($ (kbuild_extmod),)
_all:all
else
_all:modules
endif
All self reliance on $ (ALL-Y)
All: $ (all-y)
(b). $ (ALL-Y) reliance on U-boot target files
$ (ALL-Y) defines the final need to generate all files:
# Always Append all so this arch config.mk ' s can add custom ones all-y + + u-boot.srec u-boot.bin u-boot.sym system.map u-b Oot.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 # is included. So the image has to is built explicitly all-$ (CONFIG_RAMBOOT_PBL) + = U-BOOT.PBL endif endif all-$ (CONFIG_SPL) + = Spl/u-boo T-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 # Enable combined SPL/U-BOOT/DTB rules for Tegra ifeq ( Config_tegra) $ (CONFIG_SPL), yy) all-y + = U-boot-tegra.bin u-boot-nodtb-tegra.bin all-$ (config_of_separate) =
U-boot-dtb-tegra.bin endif
The above $ (ALL-Y) target looks very complex, but except for the common goal of the first line, the rest of the goals are generated only under special conditions, which are omitted. Analyze only common target dependencies:
All-y + u-boot.srec u-boot.bin u-boot.sym system.map u-boot.cfg Binary_size_check
I. Reliance on U-boot.srec
Dependent U-boot.srec:
U-boot.hex u-boot.srec:u-boot FORCE
$ (call if_changed,objcopy)
Ii. reliance on U-boot.bin
Dependent U-boot.bin:
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
If device tree support is turned on, there is a dependency:
U-boot.bin--> u-boot-dtb.bin--> u-boot-nodtb.bin + DTS/DT.DTB
Device tree support is not turned on here, so:
U-boot.bin--> U-boot-nodtb.bin
Further, for U-boot-nodtb.bin, its rules are:
U-boot-nodtb.bin:u-boot FORCE
$ (call if_changed,objcopy)
$ (Call do_static_rela,$<,$@,$ (config_sys_text _base))
$ (Board_size_check)
Iii. reliance on U-boot.sym
Dependent U-boot.sym:
U-boot.sym:u-boot FORCE
$ (call If_changed,sym)
Iv. reliance on System.map
Dependent System.map:
System.map:u-boot
@$ (call system_map,$<) > $@
V. RELIANCE on U-BOOT.CFG
Dependent u-boot.cfg:
U-boot.cfg:include/config.h FORCE
$ (call if_changed,cpp_cfg)
VI. RELIANCE on Binary_size_check
Dependent Binary_size_check:
Binary_size_check:u-boot-nodtb.bin FORCE
@file_size =$ (Shell wc-c u-boot-nodtb.bin | awk ' {print $$1} '); \
MAP_ size=$ (Shell cat U-boot.map | \
awk '/_image_copy_start/{start = $$1}/_image_binary_end/{end = $$1} end {if start != "" && End!= "") print ibase=16; "ToUpper"-"ToUpper (Start)}"
| Sed ' s/0x//g '
| BC); \
If [""!= "$ $map _size"]; Then \
if test $ $map _size-ne $ $file _size then \
echo "U-boot.map shows a binary size of $ $map _size" >&2; \
echo " but U-boot-nodtb.bin shows $ $file _size" >&2; \
exit 1; \
fi \
fi
Obviously, there are the following dependencies for Binary_size_check:
Binary_size_check--> u-boot-nodtb.bin--> u-boot VII. $ (ALL-Y) reliance on goals in common
The above common goal $ (ALL-Y) dependency has one thing in common, except that u-boot.cfg relies on include/ Config.h, the rest of the goals are all dependent on the u-boot (in fact, in addition to relying on u-boot, but also rely on force, because force dependence itself is an empty target, for convenience, this omitted the description of force dependency), as follows:
2. U-boot document target dependency a). Reliance on U-boot
Dependent U-boot:
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
where $ (u-boot-init) and $ (u-boot-main) are respectively defined as:
U-boot-init: = $ (head-y)
U-boot-main: = $ (libs-y)
I. Reliance $ (HEAD-Y)
$ (head-y) in Arch/arm/makefile is defined as:
Head-y: = arch/arm/cpu/$ (CPU)/START.O
Ii. Reliance $ (LIBS-Y)
Search for $ (libs-y) in the top-level makefile, which is defined as a set of BUILD-IN.O in each layer driver directory:
ygu@ubuntu:/opt/work/u-boot/u-boot-2016.09$ grep-nw libs-y Makefile 629:libs-y + = lib/632:libs-y + = fs/633:libs-y + NE t/634:libs-y = Disk/635:libs-y = drivers/636:libs-y + = drivers/dma/637:libs-y + + + drivers/gpio/638:libs-y /i2c/639:libs-y = Drivers/mmc/640:libs-y = drivers/mtd/642:libs-y + = drivers/mtd/onenand/644:libs-y + = drivers/mtd/s Pi/645:libs-y + + drivers/net/646:libs-y + + drivers/net/phy/647:libs-y + + drivers/pci/648:libs-y + = drivers/power/\ 65 5:libs-y = Drivers/spi/659:libs-y = drivers/serial/660:libs-y + = drivers/usb/dwc3/661:libs-y + = DRIVERS/USB/COMMON/6 62:libs-y = Drivers/usb/emul/663:libs-y = drivers/usb/eth/664:libs-y + = drivers/usb/gadget/665:libs-y + = Drivers/usb /gadget/udc/666:libs-y + = drivers/usb/host/667:libs-y + = drivers/usb/musb/668:libs-y + = drivers/usb/musb-new/669: libs-y = Drivers/usb/phy/670:libs-y = drivers/usb/ulpi/671:libs-y + = cmd/672:libs-y + + + common/675:libs-y 6:libs-y + + test/dm/680:libs-Y + = $ (if $ (boarddir), board/$ (Boarddir)/) 682:libs-y: = $ (sort $ (libs-y)) 684:u-boot-dirs: = $ (patsubst%/,%,$ (filter%/, $ (libs-y)) examples 688:libs-y: = $ (patsubst%/,%/BUILT-IN.O, $ (libs-y)) 691:u-boot-main: = $ (libs-y)
The first line of search command GREP-NW libs-y makefile parameters:
-N indicates that the search results show line numbers
-W indicates that only the completed words are searched
-Makefile represents only search in file Makefile in the current directory
Above search results, all matching rows from 629~682 are included in the drive directory, and line No. 688 adds build-in.o after each directory name, such as the MTD driver directory in libs-y drivers/mtd/becomes drivers/mtd/ BUILD-IN.O, which is only equivalent to linking the BUILD-IN.O file in each drive directory.
Why only the BUILD-IN.O files in each directory? The answer is to combine multiple *.O output files from the same directory at compile time to generate a BUILD-IN.O file, followed by another blog that specifically describes it. b). Reliance on U-boot.lds
Dependent U-boot.lds:
U-boot.lds: $ (ldscript) Prepare FORCE
$ (call If_changed_dep,cpp_lds)
The definition of $ (ldscript) is as follows:
# IF Board code explicitly specified ldscript or config_sys_ldscript, use # that (or fail If absent).
Otherwise, search for a linker script in a # standard location. Ifndef ldscript #LDSCRIPT: = $ (Srctree)/board/$ (boarddir)/u-boot.lds.debug ifdef config_sys_ldscript # nee D to strip off double quotes Ldscript: = $ (Srctree)/$ (config_sys_ldscript: "%" =%) endif endif # If There is no Specified link script, we look at a number of places for it ifndef Ldscript ifeq ($ (wildcard $ (ldscript)),) L Dscript: = $ (srctree)/board/$ boarddir/u-boot.lds endif ifeq ($ (wildcard $ (ldscript)), Ldscript: = $ (src Tree)/$ (cpudir)/u-boot.lds endif ifeq ($ (wildcard $ (ldscript)), Ldscript: = $ (Srctree)/arch/$ (arch)/cpu/u -boot.lds endif endif
If Ldscript and config_sys_ldscript are not defined, the U-boot's own LDS file is used by default. A custom LDS file for board or CPUs, including board/$ (BOARDIDR) and $ (cpudir) directories, and the default arch/$ in/cpu (ARCH) u-boot.lds directory If no custom LDS file is used
We analyze the raspberry sent 3 generation platform, its configuration rpi_3_32b_defconfig does not correspond to any specific LDS files, so use the default file Arch/arm/cpu/u-boot.lds dependent prepare
Another dependency of U-boot.lds is pseudo target prepare. U-boot file target Dependencies
The U-boot file target depends on this as a whole:
3. Prepare series target relies on a). Rules for prepare series dependencies
In fact, prepare is a series of prepare pseudo targets and actions to complete the preparation before compiling:
# Things we need to doing before we recursively start building the kernel # or the modules are in ' listed '. # A Multi level approach is used.
Preparen is processed before prepareN-1. # Archprepare is used in arch makefiles and if processed ASM symlink, # Version.h and Scripts_basic is Processed/creat
Ed. # listed in dependency order phony = Prepare Archprepare prepare0 prepare1 prepare2 prepare3 # prepare3 is used to check If we are building in a separate output directory, # and if I do: # 1) Check This make has not been executed in the Kern El src $ (srctree) Prepare3:include/config/uboot.release ifneq ($ (KBUILD_SRC),) @$ (Kecho) ' Using $ (srctree) as Sourc E for U-boot ' $ (Q) if [F $ (Srctree)/.config-o-D $ (srctree)/include/config]; Then \ Echo >&2 "$ (srctree) isn't clean, please run ' make mrproper '";
\ Echo >&2 "in the ' $ (srctree) ' directory."; \ /bin/false;
\ fi; endif # Prepare2 creates a makefile if using a SeparaTe output directory prepare2:prepare3 outputmakefile prepare1:prepare2 $ (version_h) $ (timestamp_h) \
Include/config/auto.conf ifeq ($ (wildcard $ (ldscript)) @echo >&2 "could not find linker script."
@/bin/false endif archprepare:prepare1 scripts_basic prepare0:archprepare $ (Q) $ (make) $ (build) =.
# all the Preparing. Prepare:prepare0
The dependencies between pseudo target prepare,prepare0,archprepare,prepare1,prepare2,prepare3 are as follows:
b). Prepare series Other dependency rules
In the Prepare1 list of dependencies, in addition to the include/config/auto.conf, there are $ (version_h) and $ (timestamp_h), and their dependencies are:
$ (Version_h): Include/config/uboot.release FORCE
$ (call Filechk,version.h)
$ (timestamp_h): $ (srctree)/ Makefile FORCE
$ (call filechk,timestamp.h)
The two Filechk function calls here dynamically generate Version.h and timestamp.h.
For the innermost prepare3 dependency include/config/uboot.release, there is the next level of dependency:
# Store (new) Ubootrelease string in Include/config/uboot.release
include/config/uboot.release:include/config/ Auto.conf FORCE
$ (call filechk,uboot.release)
There is a matching rule for include/config/auto.conf,makefile:
# If. config is newer than include/config/auto.conf, someone tinkered # with it and forgot to run make oldconfig. # I F auto.conf.cmd is missing then we are probably into a cleaned tree so # We execute the ' config step ' be sure to catch UPDA Ted Kconfig Files include/config/%.conf: $ (kconfig_config) Include/config/auto.conf.cmd $ (Q) $ (make)-F $ (Srctree)/Make File Silentoldconfig @# If The following part fails, include/config/auto.conf should is @# deleted so ' make silent
Oldconfig "'ll 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 the "Make Silentoldconfig".
@# We need to touch the include/config/auto.conf so it gets newer @#.
@# Otherwise, ' make silentoldconfig ' would to be invoked twice. $ (Q) Touch include/config/auto.conf
So include/config/auto.conf relies on $ (kconfig_config) and Include/config/auto.conf.cmd.
-$ (Kconfig_config) is actually a. CONFIG file;
-Include/config/auto.conf.cmd is a dependent file generated by FIXDEP at compile time; Prepare series pseudo-goal integrity dependencies
The dependencies of the entire prepare section are as follows:
4. Complete goal-dependent
By putting the above dependencies together, you get a complete u-boot target dependency graph:
(The complete diagram is large, you can drag the picture to the other browser window to see the larger picture)
After the goal-dependent analysis is completed, the rest is based on the complete target dependency graph, starting at the bottom of the dependencies and running the commands on a layer-by-tier basis until the top-level target is generated.