uboot Makefile解析

一、uboot的编译

首先来回顾一下uboot如何编译。

(1)设置临时环境变量

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

(2)指定板级配置,生成.config文件

make xxx_defconfig

(3)编译

make -j8

(4)清除构建

make distclean

接下来,我们分析uboot根目录下的顶层Makefile文件,来探讨这三条编译命令背后发生了什么。

二、Makefile第一部分——make相关设置

1. uboot版本信息

VERSION = 2016
PATCHLEVEL = 03
SUBLEVEL =
EXTRAVERSION =
NAME =
# VERSION:主版本号
# PATCHLEVEL:补丁版本号
# SUBLEVEL :次版本号
# EXTRAVERSION:附加版本信息
# 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)

#  +=:MAKEFLAGS变量追加值
#  -rR:禁止使用make内置的隐含规则和变量定义
#  --include-dir:指明搜索路径
#  $(CURDIR):表示当前目录

3. 递归构建设置

# Avoid funny character set dependencies
# 避免有趣的字符集依赖
unexport LC_ALL
LC_COLLATE=C
LC_NUMERIC=C
export LC_COLLATE LC_NUMERIC

# Avoid interference with shell env settings
# 避免干扰shell环境设置
unexport GREP_OPTIONS

make支持递归构建,调用子目录下的Makefile来完成子目录的编译,使用-C参数指定子目录,语法如下:

$(MAKE) -C subdir

在调用子Makefile时,可以用export指定传递哪些变量,使用unexport指定不传递哪些变量。但是SHELL变量和MAKEFLAGS这两个变量默认会传递给子Makefile,除非使用unexport显示指定不传递。

4. 美化输出

# Use 'make V=1' to see the full commands
# 使用 'make V=1' 来看到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
# 如果用户运行make -s(静默模式),则禁止命令回显
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)判断用户是否指定变量V的值

Makefile中函数origin用来获取变量是哪里来的,如果变量V是在命令行定义的,那么它的来源就是"command line",进而将变量V的值赋给KBUILD_VERBOSE,设定日志输出是否详细。

如果用户没有定义变量V,则默认KBUILD_VERBOSE的值等于0。

(2)控制make日志输出

Makefile中用了一个比较骚的操作,来用变量quier和Q控制是否在终端输出完整的命令,比如:

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

如果V=1的情况下Q为空,这条命令执行时会被完整的输出在终端上;当V=0的情况下,Q=@,命令变为:

@make $(build) = scripts/basic

这时命令执行就不会被输出到终端上了。

(3)静默输出

在V=0的情况下,uboot终端中显示的是短命令,但还是会输出日志,使用make -s参数即可设置静默输出,任何日志都没有。

(4)导出变量 quiet、Q 、KBUILD_VERBOSE给子Makefile,使他们保持一样的日志设置。

5. 设置编译输出目录

# 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)
# 在OBJ目录中调用make时设置KBUILD SRC
# KBUILD SRC不打算被普通用户使用(目前)

ifeq ($(KBUILD_SRC),)

# OK, Make called in directory where kernel src resides
# Do we want to locate output files in a separate directory?
# 设置 KBUILD_OUTPUT 环境变量

ifeq ("$(origin O)", "command line")
  KBUILD_OUTPUT := $(O)
endif

# That's our default target when none is given on the command line
# 当命令行没有传递参数时,这是我们默认的目标

PHONY := _all
_all:

# Cancel implicit rules on top Makefile
# 取消顶部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
# 在输出目录中调用第二个make,传递相关变量检查输出目录是否确实存在
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支持保存编译输出的文件到单独的目录,为了在单独的目录中定位输出文件,支持两种语法:

(1)O=
make O=dir/to/store/output/files/
(2)设置KBUILD_OUTPUT环境变量
export KBUILD_OUTPUT=dir/to/store/output/files/
make

使用O=指定要比 KBUILD_OUTPUT 环境变量的优先级要高。

6. 代码检查

# Call a source code checker (by default, "sparse") as part of the
# C compilation.
# 调用源代码检查器(默认情况下是“sparse”)作为C编译的一部分。
#
# 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

使用参数C=来使能代码检查:
1:检查需要重新编译的文件
2:检查所有的源码文件

同样,如果参数C来源于命令行,就将C赋值给环境变量 KBUILD_CHECKSRC,如果没有则变量KBUILD_CHECKSRC为0。

7. 模块编译

# If building an external module we do not care about the all: rule
# but instead _all depend on modules
# 如果指定了模块编译,则先编译模块,再编译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

使用参数M=dir来指定外部模块编译输出目录,如果命令行有M参数,则将M的值赋给KBUILD_EXTMOD,若无,则KBUILD_EXTMOD为空,直接编译all。

三、Makefile第二部分——编译前的准备

1. 获取主机的CPU架构和操作系统

# 获取主机架构
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/)

# 获取主机操作系统
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
      sed -e 's/\(cygwin\).*/cygwin/')

export  HOSTARCH HOSTOS

2. 设置编译器和配置文件

还记得吗?uboot在编译前要指定ARCH和CROSS_COMPILE的值,这里就要派上用场了!

比较上一步获得的HOSTARCH和用户指定的ARCH:

(1)相等则表示在本机编译,直接使用本地编译器,交叉编译器不需要,设置为空;KCONFIG_CONFIG表示使用.config配置文件。

# 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)如果主机操作是darwin(MAC OS的内核),则进行相关设置。

# 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. 引入通用的一些定义

# We need some generic definitions (do not try to remake the file).
# 我们需要一些通用的定义
scripts/Kbuild.include: ;
include scripts/Kbuild.include

Kbuild.include文件中都是后续编译过程中会用到的一些定义。

4. 制作变量——编译器完整名称

# Make variables (CC, etc...)
# 制作变量

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

如果是交叉编译,这里就显得非常重要,环境变量CROSS_COMPILE的值为arm-linux-gnueabi-hf-,那么制作出的编译器就是:

CC = arm-linux-gnueabi-hf-gcc

其余工具名称同理。

5. 导出编译需要的变量给子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

这其中有的变量已经定义了,有的变量从未出现,比如第二行的变量,而这几个变量就是从根目录下的config.mk来的:

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

这里面的CONFIG_SYS_xxx变量是从配置文件.config来的,如下:

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"

经过实际编译时候打印,这些变量的实际值如下:

至此,你应该已经理解了,uboot编译之前为什么要设置ARCH和CROSS_COMPILE两个环境变量。

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

四、make xxx_defconfig背后的过程

1. %config目标依赖

首先进行匹配,如果匹配到make命令的参数是config或者xxx_config,则将标志位config-targets置1:

ifeq ($(KBUILD_EXTMOD),)
        ifneq ($(filter config %config,$(MAKECMDGOALS)),)
                config-targets := 1
                ifneq ($(words $(MAKECMDGOALS)),1)
                        mixed-targets := 1
                endif
        endif
endif

如果标志位config-targets为1,则进入对应目标:

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

可以看到,xxx_config的目标是%config,依赖是scripts_basic outputmakefile FORCE这三项,构建命令是:

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

(1)scripts_basic依赖,在Makefile中可以找出该依赖的定义:

# Rules shared between *config targets and build targets
# *config目标和编译目标之间的共享规则

# Basic helpers built in scripts/
PHONY += scripts_basic
scripts_basic:
  $(Q)$(MAKE) $(build)=scripts/basic
  $(Q)rm -f .tmp_quiet_recordmcount

其中,Q是用于日志打印的、MAKE就是make,build 在scripts/Kbuild文件中定义:

###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj

srctree变量是由顶层Makefile传递过来的,定义如下:

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

因为KBUILD_SRC变量为空,所以srctree变量的值为.。

综上,scripts_basic的构建规则可以展开如下:

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

这行命令会进而调用Makefile.build脚本,后面分析。

(2)outputmakefile依赖,同样在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

该依赖的构建规则中,进来首先判断 KBUILD_SRC 是否为空,经过前面的分析该变量是空的,所以该依赖构建没啥用。

(3)FORCE依赖,同样在Makefile中可以找出该依赖的定义:

PHONY += FORCE
FORCE:

FORCE是没有规则和依赖的,所以每次都会重新生成FORCE,当FORCE作为其它目标的依赖时,由于FORCE总是被更新过的,所以依赖所在的规则总是会执行的。

好了,分析完三个依赖,我们将%config的构建规则展开如下吗,其中#@是shell脚本语法,表示传进来的所有参数列表。

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

可以看到,这条命令同样也是调用 ./scripts/Makefile.build脚本进行构建,后续再分析。

d465474f518995533031a54e358de4dc.png

可以看到,第1、2行命令是在构建scripts_basic依赖,第3行命令是在构建%config目标。

2.Makefile.build脚本

(1)scripts_basic目标

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

作用:编译出script/basic/fixdep 这个软件。

(2)%config目标

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

作用:使用xxx_defconfig文件,将xxx_defconfig文件的内容输出到配置文件.config中,生成.config中。

至此,你应该已经理解了,uboot编译之前make xxx_defconfig是如何生成.config文件的。

4fa5ff762c8769c0d348866e90dc8d55.png

五、uboot编译过程

前面的四个章节,uboot的顶层Makefile设置好了各种编译需要的变量,生成了.config文件,万事俱备,接下来开始编译uboot,生成可执行文件。

1. 依赖关系

(1)all目标

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目标依赖的ALL-y

# 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目标

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

这里区分了用户使用设备树,本文中imx6ull未用到,所以依赖于u-boot-nodtb.bin文件。

(4)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)

(5)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

目标u-boot依赖于u-boot-init、u-boot-main和u-boot.lfs。

u-boot-init、u-boot-main是两个变量,在顶层Makefile中:

u-boot-init := $(head-y)
u-boot-main := $(libs-y)

head-y没有定义,该变量和CPU架构有关,在相关架构下的子Makefile中定义,比如arch/arm/Makefile中定义如下:

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

libs-y在顶层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))

代码段可以看出,libs-y是uboot各子目录中built-in.o的集合。

u-boot.lds在各个架构目录下,比如arch/arm/cpu/u-boot.lds。

综上,u-boot目标是以u-boot.lds为链接脚本,将arch/arm/cpu/armv7/start.o和各个子目录下的built-in.o链接在一起生成u-boot。

2. built-in.o文件如何生成

以driver/gpio/built-in.o为例,在drivers/gpio/目录下有个名为.built-in.o.cmd的文件,内容如下:

cmd_drivers/gpio/built-in.o :=  arm-linux-gnueabihf-ld.bfd     -r -o drivers/gpio/built-in.o drivers/gpio/mxc_gpio.o

可以看出,built-in.o是由 drivers/gpio/built-in.o drivers/gpio/mxc_gpio.o 文件进一步链接而来的,mxc_gpio.c是NXP i.MX 系列的GPIO驱动文件。

-r参数是链接器参数,表示产生可重定向的输出,用于小部分文件链接。

最后,将各个子目录的所有built-in.o文件链接在一起,再与 arch/arm/cpu/armv7/start.o 链接上,就形成了u-boot目标。

fab58fc4816df88cd2deafc8523345a9.jpeg

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值