u-boot-1.1.6顶层目录Makefile详解(转)

 

#

# (C) Copyright 2000-2006 版权所有:2000~2006

# Wolfgang Denk, DENX Software Engineering, wd@denx.de.             Wolfgang Denk一个开发人员的名字DENX Software Engineering公司名称wd@denx.de邮箱

#

# See file CREDITS for list of people who contributed to this            可以看看CREDITS,里面有为uboot做出贡献的所有开发人员

# project.

#

# This program is free software; you can redistribute it and/or       这个程序是一个自由软件,你可以重新发布它,或者在自由软件基金组织所颁布的

# modify it under the terms of the GNU General Public License as       GNU GPLGNU 公共许可协议)的前提下修改它;注意,不论是第二版的GPL还是

# published by the Free Software Foundatio; either version 2 of         任何更新的版本,都可以,这由你选择。

# the License, or (at your option) any later version.

#

# This program is distributed in the hope that it will be useful,               我们之所以发布这个程序,是希望它能够对你有用,但是我们不做任何担保;

# but WITHOUT ANY WARRANTY; without even the implied warranty of      不保证任何的可销售性和任何给予特殊目的的合适性。想要了解详细内容,

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        你可以参考GNU GPL

# GNU General Public License for more details.

#

# You should have received a copy of the GNU General Public License    你应该已经得到了GNU General Public License的一个拷贝,因为它就包含在

# along with this program; if not, write to the Free Software                     这个工程的源代码包中。如果没有,你可以写信给自由软件基金组织,以获取一份。

# Foundation, Inc., 59 Temple Place, Suite 330, Boston,

# MA 02111-1307 USA

#

 

VERSION = 1

PATCHLEVEL = 1

SUBLEVEL = 6

EXTRAVERSION =

U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)     #1.1.6

VERSION_FILE = $(obj)include/version_autogenerated.h                                      #版本文件

 

 

# uname 命令将正在使用的操作系统名写到标准输出中

# -m 显示硬件运行系统的机器 ID

# 定义变量,HOSTARCH里面存储的是机器ID,即主机架构类型

HOSTARCH := $(shell uname -m | /            #可以把shell脚本写到makefile里面,这是make中的shell function,相当于shell中的命令替换。

 sed      -e s/i.86/i386/ /                      #shell uname -m 获得机器硬件名称

     -e s/sun4u/sparc64/ /                #sed -e s/arm.*/arm/ 的意思是把前缀为arm的所有模式替换为arm。结合前面的uname命令来理解就是:

     -e s/arm.*/arm/ /              #uname -m的结果(主机架构类型或者称为机器ID号)通过管道传递给sed命令,然后把前缀为arm的所有模式替换为arm

     -e s/sa110/arm/ /                    #sed的语法:     sed [  -n ] Script [ File ... ]

     -e s/powerpc/ppc/ /              #                   sed [  -n ] [ -e Script ] ... [ -f ScriptFile ] ... [ File ... ]

     -e s/macppc/ppc/)                 #sed 命令根据编辑脚本,去修改指定的 File 文件(这里file是作为一个输入参数的)的行,并将其写到标准输出。

#sed 命令包含很多功能,用于选择要修改的行(请注意,sed是针对行进行操作的),并只对选择的行作更改。

#sed 命令使用两个工作空间来保留修改的行:保留选定行的 "模式空间" 和暂时存储行的 "保留空间"

#这里的编辑脚本由单独的子命令构成,每个单独的行对应着一个子命令。sed子命令的格式如下:[address-range] function[modifiers],即:[地址范围] 函数[修改符]

#sed 命令通过将一个输入行读入模式空间,依次应用所有的 sed 子命令(这些子命令的地址选择了该行),

#并将模式空间写到标准输出来处理每个输入的 File 输入文件(file就是输入参数)。然后清除模式空间,并对输入的 File 中指定的每行重复该过程。

#一些 sed 子命令使用保留空间来保存后继检索的所有的,或部分的模式空间。

#当命令包含地址(行号或搜索模式)时,该命令只会对被寻址的行起作用。否则,该命令适用于所有的行。

#注意,这里的参数"-e"的意思是:使用 Script 变量作为编辑脚本。如果你只使用一个 -e 标志并且不使用-f 标志,则可以省略 -e 标志。

#注意,这里的 "sed -e s/arm.*/arm/"表示把前缀为arm的所有模式替换为arm,也就是sed s/pattern/replacement/flags

# replacement 字符串代替在模式空间中首次出现的pattern 参数。除了空格或换行符,在 s 子命令之后显示的任何字符都能代替 /(斜杠)分隔符。 

 

 

#uname -s 表示:显示系统名,标志缺省为开,即uname命令不带任何参数的输出和加参数-s输出相同,即Linux

#tr是一个shell命令,可以实现许多sed的功能,这里 tr '[:upper:]' '[:lower:]'的意思是把管道中的Linux 中的大写字母L 转换成小写字母l

#下面这句话的意思是定义变量HOSTOSHOSTOS里面存放的是主机安装的,并且当前正在运行的操作系统

HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | /

     sed -e 's//(cygwin/).*/cygwin/')      # 这一句的意思是检测出主机安装的,并且当前正在运行的操作系统名,并把这个系统名中的大写字母转换为小写字母,  

                                      # 然后在通过sed流编辑器匹配所有的,这个系统名中出现的"/(cygwin/).*"模式,然后再用"cygwin"模式替换。

#export HOSTARCH HOSTOS  的意思是输出两个makefile变量HOSTARCHHOSTOS                

 

export HOSTARCH HOSTOS                

 

# Deal with colliding definitions from tcsh etc.   用来处理来自tcsh的互相冲突的定义等等

# 一般来说,shell可以分成两类。第一类是由 Bourne shell 衍生出来的包括 

# shkshbash,与zsh。第二类是由 C shell 衍生出来的,包括 csh 与 

# tcsh。除此之外还有一个rc,有人认为该自成一类,有人认为该归类在Bourne shell。 

 

VENDOR =  #开发商

 

#########################################################################

#

# U-boot build supports producing a object files to the separate external

# U-boot 的编译过程可以支持向一个自己定义的路径生成最终的目标文件

# directory. Two use cases are supported:

# 这里提供了两种用法:

#

# 1) Add O= to the make command line #第一种用法:通过在终端执行命令make O=/dir(即你指定的生成的目标文件的存放目录)

# 'make O=/tmp/build all'

#

# 2) Set environement variable BUILD_DIR to point to the desired location #第二种用法:通过设置环境变量来指定目标文件存放目录,如下所示:

# 'export BUILD_DIR=/tmp/build'

# 'make'

#

# The second approach can also be used with a MAKEALL script #第二种方法也可以写成一个MAKEALL脚本,然后执行MAKEALL,如下所示:

# 'export BUILD_DIR=/tmp/build'

# './MAKEALL'

#

# Command line 'O=' setting overrides BUILD_DIR environent variable.    #命令行'O='设置会覆盖环境变量BUILD_DIR的设置

#

# When none of the above methods is used,the local build is performed and #如果都不采用上面两种方法,那么目标文件放到源码顶层目录,也就是U-BOOT顶层目录

# the object files are placed in the source directory.

#

 

#理解了上面一段英文,这里就不难理解了

#方法1

 

ifdef O                                            #如果变量'O' 已经被定义过

ifeq ("$(origin O)", "command line")   #如果变量'O' 在命令行中定义过

BUILD_DIR := $(O)                         #就把变量'O' 的值(目标文件存放目录)BUILD_DIR

endif

endif

 

 

#方法2

ifneq ($(BUILD_DIR),)              #如果变量BUILD_DIR不为空,即环境变量BUILD_DIR 被定义过

saved-output := $(BUILD_DIR)           #那么把它的值saved-output

 

# Attempt to create a output directory.  #生成一个输出路径,即目标文件存放目录BUILD_DIR

$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})

 

#shell [ -d ${BUILD_DIR} ] 是什么意思?是不是生成一个目录的意思?

 

# Verify if it was successful. 测试目录是否创建成功

BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)  #这又是什么意思,说明对shell还不够理解

$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))

#这里用了一个if函数,意思是如果如果$(BUILD_DIR) 非空,则什么都不执行(返回空),否则执行error函数,输出错误信息

endif

 

# ifneq ($(BUILD_DIR),) #意思是:如果没有定义目标文件存放目录

#Makefile中定义了源码以及生成目标文件存放的目录,目标文件存放目录BUILD_DIR可以通过make O=dir指定。如果没有指定,则设定为源码顶层目录。

#一般编译的时候不指定输出目录,则BUILD_DIR为空。其他目录变量如下:

OBJTREE  := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))  #如果$(BUILD_DIR)不为空,则返回$(BUILD_DIR),并赋给OBJTREE,即自己定制的目标存放目录

SRCTREE  := $(CURDIR) #把当前源码所在目录 $(CURDIR) 赋给SRCTREE

TOPDIR  := $(SRCTREE)    #把当前源码所在目录 $(CURDIR) 赋给SRCTREE

LNDIR  := $(OBJTREE)      #存放生成的目录文件

export TOPDIR SRCTREE OBJTREE

 

 

MKCONFIG := $(SRCTREE)/mkconfig    #MKCONFIG指向源码所在目录(U-BOOT顶层目录)下的mkconfig配置文件

export MKCONFIG

 

#在编译UBOOT之前,我们先要执行:

#make smdk2410_config

#从本Makefile的下文可以判断出smdk2410_configMakefile的一个目标。

#smdk2410_config: unconfig的意思是为smdk2410开发板建立一个编译项。

#显然,执行#make smdk2410_config时,先执行unconfig目标(不指定输出目标时,objsrc变量均为空),unconfig下面的命令主要任务是清理上

#一次执行make *_config生成的头文件和makefile的包含文件

#主要是include/config.hinclude/config.mk

#然后执行命令: @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0

#arm 表示CPU的构架是基于ARM体系的

#arm920t 表示CPU的类型是arm920t

#smdk2410 表示开发板的型号

#NULL 表示开发商或者经销商的名称,这里为空

#s3c24x0 表示基于S3C2410的片上系统

#MKCONFIG指向UBOOT顶层目录下的mkconfig脚本配置文件,后面五个字符串是传入的参数(好像$(@:_config=)也是一个参数)。

#下面来分析一下mkconfig这个脚本配置文件,点击链接:http://zqwt.012.blog.163.com/blog/static/120446842010325102158182/

 

 

ifneq ($(OBJTREE),$(SRCTREE))     #当目标存放目录不是U-BOOT顶层目录(源码目录)时

REMOTE_BUILD  := 1             #定义变量REMOTE_BUILD  := 1  这个变量算是一个flag

export REMOTE_BUILD

endif

 

# $(obj) and (src) are defined in config.mk(顶层目录下) but here in main Makefile

# we also need them before config.mk is included which is the case for

# some targets like unconfig, clean, clobber, distclean, etc.

# $(obj) and $(src)都被定义在顶层目录下的config.mk脚本配置文件里面

# 但是在这个主Makefile里面,我们同样需要他们,

# 因为在主Makefile文件包含config.mk之前,$(obj) and $(src)偶尔地会成为这些目标的case:

# unconfig, clean, clobber, distclean, etc

 

 

ifneq ($(OBJTREE),$(SRCTREE))    #当目标存放目录不是U-BOOT顶层目录(源码目录)时

obj := $(OBJTREE)/             #定义变量obj,让其等于目标存放目录

src := $(SRCTREE)/                 #定义变量src,让其等于uboot顶层目录

else

obj :=                      #否则,这两个变量都定义为空

src :=

endif

export obj src

 

 

#########################################################################

ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk)) #这句话让人费解!

# 通配符在规则中可以自动扩展,但设置在变量中或在函数的参数中通配符一般不能正常扩展

# 如果您需要在这些场合扩展通配符,您应该使用函数wildcard,格式如下:

# $(wildcard pattern...)

 

 

# load ARCH, BOARD, and CPU configuration

#加载ARCH, BOARD, and CPU 配置

include $(OBJTREE)/include/config.mk   # 这时候,才开始包含/include/config.mk

export ARCH CPU BOARD VENDOR SOC

 

 

#指定交叉编译器前缀

ifeq ($(ARCH),arm) 

CROSS_COMPILE = arm-linux-

#这里你可以把交叉编译器的安装路径加到arm-linux-之前,比如你的交叉编译器安装路径是/root/u-boot/usr/local/arm/3.3.2/bin/

#你可以这样定义CROSS_COMPILE = /root/u-boot/usr/local/arm/3.3.2/bin/arm-linux-

#这样一来,你在终端进行编译的时候就不用指定CROSS_COMPILE=arm-linux-

#但请注意:在编译内核的时候,交叉编译器必须安装在/usr/local/arm下,否则会发生错误!!!!!

endif

 

export CROSS_COMPILE

 

 

# load other configuration  加载其他设置,这里是包含顶层目录下的config.mk配置文件,这个文件主要做了三个工作:

# 1、定义了交叉编译器 2、定义了编译选项 3、定义了编译规则

# 对本文件具体的分析,请查看链接:

http://zqwt.012.blog.163.com/blog/static/12044684201032541139914/

 

include $(TOPDIR)/config.mk    

 

#########################################################################

# U-Boot objects....order is important (i.e. start must be first)

# uboot目标...书写顺序很重要,比如start.o必须排在第一位

#########################################################################

OBJS  = cpu/$(CPU)/start.o

#start.o必须放在目标文件的第一位,因为uboot执行的第一段代码就是start.S

#具体原因可以查看链接脚本/u-boot-1.1.6/board/smdk2410/u-boot.lds,点击连接:

http://zqwt.012.blog.163.com/blog/static/120446842010320101137932/

 

 

 

.....................................

 

 

 

OBJS := $(addprefix $(obj),$(OBJS))  #这句的意思是把目标文件存放路径以前缀的形式加到start.O之前,然后再赋给OBJS

 

 

#以下是编译UBOOT需要的库文件

LIBS  = lib_generic/libgeneric.a

 

LIBS += board/$(BOARDDIR)/lib$(BOARD).a # 严重平台依赖的

 

LIBS += cpu/$(CPU)/lib$(CPU).a  # 严重平台依赖的

 

ifdef SOC

LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a # 严重平台依赖的

endif

 

LIBS += lib_$(ARCH)/lib$(ARCH).a # 严重平台依赖的

 

 

LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a /

 fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a

LIBS += net/libnet.a

LIBS += disk/libdisk.a

LIBS += rtc/librtc.a

LIBS += dtt/libdtt.a

LIBS += drivers/libdrivers.a

LIBS += drivers/nand/libnand.a

LIBS += drivers/nand_legacy/libnand_legacy.a

LIBS += drivers/sk98lin/libsk98lin.a

LIBS += post/libpost.a post/cpu/libcpu.a

LIBS += common/libcommon.a

LIBS += $(BOARDLIBS)

 

LIBS := $(addprefix $(obj),$(LIBS))

 

.PHONY : $(LIBS)   # 这是一个伪目标

 

#根据所生成的include/config.mk文件定义的几个变量ARCH, CPU, BOARD, SOC,我们可以

#确定硬件平台依赖的目录文件。smdk2410平台相关(依赖)目录以及对应生成的库文件如下:

#board/smdk2410/: 库文件board/smdk2410/libsmdk2410.a

#cpu/arm920t/: 库文件cpu/arm920t/libarm920t.a

#cpu/arm920t/s3c24x0: 库文件cpu/arm920t/s3c24x0/libs3c24x0.a

#lib_arm 库文件lib_arm/libarm.a

#include/asm-arm: 头文件

#include/cnofigs/smdk2410.h:头文件

 

# Add GCC lib

PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc

 

# The "tools" are needed early, so put this first

# Don't include stuff already done in $(LIBS)   不要包含已经在 $(LIBS) 中的任何东西

#

# 伪目标SUBDIRS:用于执行toolsexamplespostpost/cpu子目录下的make文件

SUBDIRS = tools /

   examples /

   post /

   post/cpu

.PHONY : $(SUBDIRS)

 

ifeq ($(CONFIG_NAND_U_BOOT),y)

NAND_SPL = nand_spl

U_BOOT_NAND = $(obj)u-boot-nand.bin

endif

 

__OBJS := $(subst $(obj),,$(OBJS))

__LIBS := $(subst $(obj),,$(LIBS))

 

#########################################################################

#########################################################################

 

 

 

 

#这里是最终要生成的各种镜像文件u-boot.hexu-boot.srecu-boot.binSystem.mapu-boot.img

ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)

all:  $(ALL)

$(obj)u-boot.hex: $(obj)u-boot

  $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@       # $<$@ 分别表示使用该规则的源文件和目标文件

 

$(obj)u-boot.srec: $(obj)u-boot               

  $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@

 

$(obj)u-boot.bin: $(obj)u-boot

  $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

 

$(obj)u-boot.img: $(obj)u-boot.bin

  ./tools/mkimage -A $(ARCH) -T firmware -C none /

  -a $(TEXT_BASE) -e 0 /

  -n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | /

   sed -e 's/"[  ]*$$/ for $(BOARD) board"/') /

  -d $< $@

 

$(obj)u-boot.dis: $(obj)u-boot

  $(OBJDUMP) -d $< > $@

 

 

 

#此处生成的是ubootELF文件镜像

$(obj)u-boot:  depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)

  UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*/(__u_boot_cmd_.*/)/-u/1/p'|sort|uniq`;/

  cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) /

   --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) /

   -Map u-boot.map -o u-boot

 

 

#依赖目标$(OBJS),也就是cpu/start.o

$(OBJS):

  $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

 

#依赖目标$(LIBS),目标很多,都是每个子目录的库文件*.a,通过执行相应子目录下的make来完成

$(LIBS):

  $(MAKE) -C $(dir $(subst $(obj),,$@))

 

#这里解释一下这个makefile函数 $(dir names...)

#抽取‘names’中每一个文件名的路径部分,文件名的路径部分包括从文件名的开始到最后一个斜杠(含斜杠)

#之前的一切字符。如果文件名中没有斜杠,路径部分是‘./’。如:

#$(dir src/foo.c hacks)

#产生的结果为 src/ ./’。

 

 

$(SUBDIRS):

  $(MAKE) -C $@ all

 

$(NAND_SPL): version

  $(MAKE) -C nand_spl/board/$(BOARDDIR) all

 

$(U_BOOT_NAND): $(NAND_SPL) $(obj)u-boot.bin

  cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin

 

 

#依赖目标version:生成版本信息到版本文件VERSION_FILE

version:

  @echo -n "#define U_BOOT_VERSION /"U-Boot " > $(VERSION_FILE); /

  echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); /

  echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion /

    $(TOPDIR)) >> $(VERSION_FILE); /

  echo "/"" >> $(VERSION_FILE)

 

gdbtools:

  $(MAKE) -C tools/gdb all || exit 1

 

updater:

  $(MAKE) -C tools/updater all || exit 1

 

env:

  $(MAKE) -C tools/env all || exit 1

 

 

 

 

#依赖目标depend:生成各个子目录.depend文件,.depend列出每个目标文件的依赖文件。

#生成方法:调用每个子目录的make_depend

depend dep:

  for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done

 

 

tags ctags:

  ctags -w -o $(OBJTREE)/ctags `find $(SUBDIRS) include /

    lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) /

    fs/cramfs fs/fat fs/fdos fs/jffs2 /

    net disk rtc dtt drivers drivers/sk98lin common /

   /( -name CVS -prune /) -o /( -name '*.[ch]' -print /)`

 

etags:

  etags -a -o $(OBJTREE)/etags `find $(SUBDIRS) include /

    lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) /

    fs/cramfs fs/fat fs/fdos fs/jffs2 /

    net disk rtc dtt drivers drivers/sk98lin common /

   /( -name CVS -prune /) -o /( -name '*.[ch]' -print /)`

 

$(obj)System.map: $(obj)u-boot

  @$(NM) $< | /

  grep -v '/(compiled/)/|/(/.o$$/)/|/( [aUw] /)/|/(/./.ng$$/)/|/(LASH[RL]DI/)' | /

  sort > $(obj)System.map

 

 

 

 

 

#########################################################################

else

all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin /

$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot /

$(SUBDIRS) version gdbtools updater env depend /

dep tags ctags etags $(obj)System.map:

 @echo "System not configured - see README" >&2

 @ exit 1

endif

 

.PHONY : CHANGELOG

CHANGELOG:

 git log --no-merges U-Boot-1_1_5.. | /

 unexpand -a | sed -e 's//s/s*$$//' > $@

 

 

#########################################################################

# 这里就是我们所谓的unconfig,应该比较熟悉了!

# 很明显,它所做的工作就是清除上次生成的include/config.hinclude/config.mk

# 以及开发板目录下的一些临时配置文件

# unconfig:

#  @rm -f $(obj)include/config.h $(obj)include/config.mk /

#   $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp

#########################################################################

 

 

假定我们在u-boot-1.1.6的根目录下编译,则其中的MKCONFIG就是根目录下的mkconfig文件。$(@:_config=)的结果就是将“smdk2410_config”中的“_config”去掉,结果为“smdk2410”。所以“make smdk2410_config”实际上就是执行如下命令:

./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0

 

................................

smdk2400_config : unconfig

 @$(MKCONFIG) $(@:_config=) arm arm920t smdk2400 NULL s3c24x0

 

................................

 

smdk2410_config : unconfig

 @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0

 

 

#我删了一部分内容,那些几乎都是各种不同板子的*_config,也就是目标的定义。

 

最后总结一下,当然这里也参考了前辈们的许多宝贵经验,顶层Makefile的主要任务就是组织整个u-boot工程的编译,概括可以分为一下几个步骤:

 

1、首先通过执行make *_config传入$(@:_config=), ARCH, CPU, BOARD, VENDOR, SOC参数(一共六个参数但不

  

   一定同时存在),给mkconfig

 

2mkconfig接收到传递过来的参数后,将include头文件夹相应的头文件夹链接好,生成config.h

 

3、然后执行make分别调用各个子目录的makefile文件,以生成所有的obj文件(包括start.o)和obj库文件*.a

 

4、最后,通过链接器把所有目标文件链接起来,生成uboot镜像。不同格式的镜像都是调用相应工具,经

 

   elf镜像间接或者直接的生成的。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值