u-boot-2016.09 make編譯過程分析(二)

u-boot-2016.09 make編譯過程分析(二)

綜述

u-bootv2014.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)
  • 1
  • 2
  • 1
  • 2

這裏展開後爲:

rpi_3_32b_defconfig: scripts/kconfig/conf
    scripts/kconfig/conf  --defconfig=arch/../configs/rpi_3_32b_defconfig Kconfig
  • 1
  • 2
  • 1
  • 2

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

調用make -f ./Makefile silentoldconfig的最終結果是執行scripts/kconfig/Makefile中的規則:

silentoldconfig: $(obj)/conf
    $(Q)mkdir -p include/config include/generated
    $< $(silent) --$@ $(Kconfig)
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

這個規則展開爲:

silentoldconfig: scripts/kconfig/conf
    mkdir -p include/config include/generated
    scripts/kconfig/conf --silentoldconfig Kconfig
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

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)/.configinclude/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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

除了執行$(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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

此處沒有設置CONFIG_SPL=yCONFIG_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.hrpi_3_32b_defconfig配置只需要include/autoconf.mk):

include/autoconf.mk include/autoconf.mk.dep \
    spl/include/autoconf.mk tpl/include/autoconf.mk: include/config.h
  • 1
  • 2
  • 1
  • 2

實際上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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

最終生成的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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
save_snippets.png
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

生成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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

註釋已經很好解釋了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 > [email protected] && { \
        sed -n -f $(srctree)/tools/scripts/define2mk.sed [email protected] |       \
        while read line; do                         \
            if [ -n "${KCONFIG_IGNORE_DUPLICATES}" ] ||         \
               ! grep -q "$${line%=*}=" include/config/auto.conf; then  \
                echo "$$line";                      \
            fi                              \
        done > $@;                              \
        rm [email protected];                              \
    } || {                                      \
        rm [email protected]; false;                           \
    }

include/autoconf.mk: FORCE
    $(call cmd,autoconf)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

這個規則比較簡單,由於autoconf.mkcommon.hauto.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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

命令$(call filechk,uboot.release)展開後就是調用宏filechk_uboot.release,最終將字符串2016.09寫入include/config/uboot.release中。

1.4 timestamp.hversion.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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 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"
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
  • 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
  • 2
  • 3
  • 4
save_snippets.png
  • 1
  • 2
  • 3
  • 4

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 如果編譯沒有設置O,即輸出和代碼都在同一個目錄下,則outputmakefile的規則什麼都不做;
  • 如果編譯指定了輸出目錄O,則調用scripts/mkmakefileO選項指定的目錄下生成一個簡單的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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

scripts_basic的執行結果就是編譯生成scripts/basic/fixdep工具,該工具是u-boot編譯系統中最常用的工具,用於在編譯過程中修正每一個生成文件的依賴關係。

1.7 parepare0的規則

prepare0: archprepare FORCE
    $(Q)$(MAKE) $(build)=.
  • 1
  • 2
save_snippets.png
  • 1
  • 2

展開後爲:

prepare0: archprepare FORCE
    make -f ./scripts/Makefile.build obj=.
  • 1
  • 2
  • 1
  • 2

編譯時,命令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.mkautoconf.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
  • 1
  • 1

在頂層makefile中被指定給變量u-boot-init

u-boot-init := $(head-y)
  • 1
  • 1

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

以上腳本中,先將$(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 
  • 1
  • 2
  • 1
  • 2

從生成命令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'.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

簡單說來,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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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__)
...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

生成了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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

調用objcopy命令,通過-O ihex-O srec指定生成u-boot.hexu-boot.srec格式文件。

  • u-boot.sym
quiet_cmd_sym ?= SYM     $@
      cmd_sym ?= $(OBJDUMP) -t $< > $@
u-boot.sym: u-boot FORCE
    $(call if_changed,sym)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

調用$(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,$<) > $@
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

調用$(NM)命令打印u-boot文件的符號表,並用grep -v處理後得到System.map文件,裏面包含了最終使用到的各個符號的位置信息。

  • u-boot.binu-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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

由於這裏沒有使用device tree設置,即編譯沒有定義CONFIG_OF_SEPARATE,因此u-boot.binu-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)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

顯然,u-boot-nodtb.binu-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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

因此,閱讀源碼時如果不確定某個宏的值,可以檢查u-boot.cfg文件。

自此,生成了所有的目標文件,完成了整個編譯過程的分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章