随笔 - 342  文章 - 0 评论 - 49 阅读 - 147万
< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

    uboot的源文件众多,学习庞然大物首先找到脊椎--顶层的makfile,逐一破解。但是,uboot的makefile同样是一个庞然大物,所以也要找到它的主线。倘若过分专注部分细节,很难做到把握全局,实际上也不可能很好理解细节。

    介于此,笔者已经写了一篇uboot makefile整体解析,可以先从主体上把握makefile。然后,再读这篇makefile强大功能实现的细节,才能做到循序渐进。

    说明:uboot顶层makefile的注释机会全部源码都搬上来了,而注释都是黑体加粗以与源码有强烈的区别。

VERSION = 1            //主版本号

PATCHLEVEL = 1       //次级版本号

SUBLEVEL = 6

EXTRAVERSION =     //版本号扩展

U_BOOT_VERSION = (VERSION).(PATCHLEVEL).(SUBLEVEL)(EXTRAVERSION)   //这个Uboot的版本为1.1.6

VERSION_FILE = $(obj)include/version_autogenerated.h

//生成uboot的版本信息

HOSTARCH := $(shell uname -m | \

       sed -e s/i.86/i386/ \

           -e s/sun4u/sparc64/ \

           -e s/arm.*/arm/ \

           -e s/sa110/arm/ \

           -e s/powerpc/ppc/ \

           -e s/macppc/ppc/)

//获取主机架构,笔者电脑是酷睿双核,#uname –m输出结构是i686,执行替换命令sed -e s/i.86/i386/后,就变成了i386,

//并且将这个值保存在HOSTARCH变量中。

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

           sed -e 's/cygwin.*/cygwin/')

//获取主机操作系统,执行#uname -s可以查看自己的操作系统类型,笔者使用的操作系统是Linux。tr '[:upper:]' '[:lower:]'

//具有大写转小写的功能,所以最终HOSTOS的值为linux。

export     HOSTARCH HOSTOS

//将两个变量导出,可以供嵌套的makefile调用

# Deal with colliding definitions from tcsh etc.

VENDOR=

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

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

# directory. Two use cases are supported:

# 1) Add O= to the make command line

# '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

# 'export BUILD_DIR=/tmp/build'

# './MAKEALL'

# Command line 'O=' setting overrides BUILD_DIR environent variable.

# When none of the above methods is used the local build is performed and

# the object files are placed in the source directory.

//上边的注释讲的是uboot支持编译时,将目标文件生成在其他的目录中,这样可以保持源文件的干净,另外从目标

//文件的生成目录中查看生成的文件时一目了然。如果想要这样做,提供了两种方法。第一种方法是执行命令

//#make O=/tmp/build all;第二种方法,可以先在命令行模式定义环境变量export BUILD_DIR=/tmp/build',

//然后再执行make就行了。通常懒惰的编译方式就是让生成的目标文件和源文件混在一起,那么BUILD_DIR就会没定义。

ifdef O

ifeq ("$(origin O)", "command line")

BUILD_DIR := $(O)

endif

endif

//这是第一种方法的实现过程,$(origin O)是输出变量O的定义来源,假设在命令行模式输//入#make O=/tmp/build all

//来编译,那么O的定义来源是命令行,函数的输出是command line。

ifneq ($(BUILD_DIR),)

//倘若BUILD_DIR定义过了,也就是不希望目标文件与源文件混在一起,那么直到“endif # //ifneq ($(BUILD_DIR),)”

//的内容都有效;倘若没有定义BUILD_DIR,那么这部分代码将不起作用。

saved-output := $(BUILD_DIR)

// 将BUILD_DIR保存在saved-output变量中

# Attempt to create a output directory.

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

//-d是判断BUILD_DIR是否存在,倘若不存在就就创建。mkdir的-p参数代表若路径中的

//某些目录不存在,也一并创建

# Verify if it was successful.

//核查BUILD_DIR是否已经创建

BUILD_DIR := (shellcd(BUILD_DIR) && /bin/pwd)

//试图进入$(BUILD_DIR),倘若能进入,则将它的路径赋给BUILD_DIR,注意这时的BUILD_DIR已经是一个真实存在

//目录的代言人,而之前的只是希望创建的目录。需要说明的是倘若BUILD_DIR还没有创建,那么cd $(BUILD_DIR)将执

//行错误,返回值是空,虽然这时发生错误,但是编译会忽略这个错误还能继续进行

(if(BUILD_DIR),,(erroroutputdirectory"(saved-output)" does not exist))

// 如果没有创建成功,就执行error函数,输出信息output directory "$(saved-output)" does not exist),然后编译终止

endif # ifneq ($(BUILD_DIR),)

 

OBJTREE             := (if(BUILD_DIR),(BUILDDIR),(CURDIR))

//如果BUILD_DIR不为空,目标目录就等于BUILD_DIR;倘若没定义,就取为当前目录

SRCTREE   := $(CURDIR)//源文件目录等于当前文件夹

TOPDIR         := $(SRCTREE)//顶层目录等于源文件目录

LNDIR           := $(OBJTREE)  //连接目录等于BUILD_DIR

export     TOPDIR SRCTREE OBJTREE//将这三个变量导出

 

MKCONFIG   := $(SRCTREE)/mkconfig//指定mkconfig的位置

export MKCONFIG //将MKCONFIG变量导出

 

ifneq ((OBJTREE),(SRCTREE)) //如果目标目录和源文件目录不相等

REMOTE_BUILD        := 1  //就定义REMOTE_BUILD变量并取值为1

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和src的定义也出现在了顶层目录的config.mk中,但是config.mk中的定义由于受下边//红色加粗判断语句的影响,只有

//在“make *_config”执行后($(OBJTREE)/include/config.mk就会存在),再执行的make程序才能将config.mk包含进顶

//层makefile中。而在没有先执行“make *_config”或者$(OBJTREE)/include/config.mk不存在的情况下,如果想执行unconfig

//, clean, clobber, distclean,而这些命令用到了变量obj、src,所以这里提前包含进去。

//但是我也有疑惑,为什么不能将顶层目录的config.mk包含在全局中,设计者为什么要把它放在条件执行里边。

ifneq ((OBJTREE),(SRCTREE))

obj := $(OBJTREE)/

src := $(SRCTREE)/

else

obj :=

src :=

endif

export obj src

//定义变量obj和src,并将这两个变量导出,obj是编译目标文件的前缀,从而实现生成的目标文件在于源文件相区别的目录中

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

ifeq ((OBJTREE)/include/config.mk,(wildcard $(OBJTREE)/include/config.mk))

//这个ifeq横跨的范围非常广,用红色加粗字体表明。只有$(OBJTREE)/include/config.mk存在(也就是说已经

//执行了make *.config)的情况下,这部分包含的内容才有效

# load ARCH, BOARD, and CPU configuration

include $(OBJTREE)/include/config.mk   //将make *_config生成的config.mk文件包含进来

export     ARCH CPU BOARD VENDOR SOC //导出5个变量以供其他子目录的makefile调用

 

ifndef CROSS_COMPILE

ifeq ($(HOSTARCH),ppc)

CROSS_COMPILE =

else

ifeq ($(ARCH),ppc)

CROSS_COMPILE = powerpc-linux-

endif

ifeq ($(ARCH),arm)

CROSS_COMPILE = arm-linux- //根据变量ARCH可以使这行满足条件,确定了交叉编译使//用arm-linux-

endif

ifeq ($(ARCH),i386)

ifeq ($(HOSTARCH),i386)

CROSS_COMPILE =

else

CROSS_COMPILE = i386-linux-

endif

endif

ifeq ($(ARCH),mips)

CROSS_COMPILE = mips_4KC-

endif

ifeq ($(ARCH),nios)

CROSS_COMPILE = nios-elf-

endif

ifeq ($(ARCH),nios2)

CROSS_COMPILE = nios2-elf-

endif

ifeq ($(ARCH),m68k)

CROSS_COMPILE = m68k-elf-

endif

ifeq ($(ARCH),microblaze)

CROSS_COMPILE = mb-

endif

ifeq ($(ARCH),blackfin)

CROSS_COMPILE = bfin-elf-

endif

ifeq ($(ARCH),avr32)

CROSS_COMPILE = avr32-

endif

endif

endif

 

export     CROSS_COMPILE  //导出交叉编译变量

 

# load other configuration

include $(TOPDIR)/config.mk //将顶层的config.mk也包含进来

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

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

 

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

ifeq ($(CPU),i386)

OBJS += cpu/$(CPU)/start16.o

OBJS += cpu/$(CPU)/reset.o

endif

ifeq ($(CPU),ppc4xx)

OBJS += cpu/$(CPU)/resetvec.o

endif

ifeq ($(CPU),mpc83xx)

OBJS += cpu/$(CPU)/resetvec.o

endif

ifeq ($(CPU),mpc85xx)

OBJS += cpu/$(CPU)/resetvec.o

endif

ifeq ($(CPU),mpc86xx)

OBJS += cpu/$(CPU)/resetvec.o

endif

ifeq ($(CPU),bf533)

OBJS += cpu/(CPU)/start1.ocpu/(CPU)/interrupt.o  cpu/$(CPU)/cache.o

OBJS += cpu/(CPU)/cplbhdlr.ocpu/(CPU)/cplbmgr.o   cpu/$(CPU)/flush.o

endif

//确定目标文件构成

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

//给待生成的目标文件带上路径

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)

//将带生成的库文件作为伪目标来处理

# Add GCC lib

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

//添加系统标准库

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

# Don't include stuff already done in $(LIBS)

SUBDIRS      = tools \

         examples \

         post \

         post/cpu

.PHONY : $(SUBDIRS)

//定义make执行要首先处理的目录

ifeq ($(CONFIG_NAND_U_BOOT),y)

NAND_SPL = nand_spl

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

endif

//假若定义了CONFIG_NAND_U_BOOT,那么将总目标all中添加$(obj)u-boot-nand.bin,否则不添加

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

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

//OBJS、LIBS除去$(obj)部分的路径

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

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

//总目标的构成,也可以人为添加其他的目标

all:          $(ALL)

//定义总目标all

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

              (OBJCOPY){OBJCFLAGS} -O ihex <@

//生成16进制的可执行程序

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

              (OBJCOPY){OBJCFLAGS} -O srec <@

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

              (OBJCOPY){OBJCFLAGS} -O binary <@

//生成2进制的可执行程序

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

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

              -a $(TEXT_BASE) -e 0 \

              -n (shellsednes/.UBOOTVERSION//p(VERSION_FILE) | \

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

              -d <@

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

              (OBJDUMP)d< > $@

//生成反汇编文件u-boot.dis

(obj)uboot:dependversion(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)UNDEFSYM(__OBJS) \

                     --start-group (__LIBS) --end-group(PLATFORM_LIBS) \

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

//生成elf格式的u-boot文件

$(OBJS):

              (MAKE)Ccpu/(CPU) (if(REMOTE_BUILD),@,(notdir $@))

//中间目标文件生成

$(LIBS):

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

//中间库文件生成

$(SUBDIRS):

              (MAKE)C@ all

//SUBDIRS目录处理

$(NAND_SPL):     version

              (MAKE)Cnandspl/board/(BOARDDIR) all

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

              cat (obj)nandspl/ubootspl16k.bin(obj)u-boot.bin > $(obj)u-boot-nand.bin

version:

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

              echo -n "(UBOOTVERSION)">>(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 dep:

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

//执行make depend或者make dep都能触发命令

tags ctags:

              ctags -w -o (OBJTREE)/ctagsfind(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 \

                     nameCVSprune -o name.[ch]print`

etags:

              etags -a -o (OBJTREE)/etagsfind(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 \

                     nameCVSprune -o name.[ch]print`

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

              @(NM)< | \

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

              sort > $(obj)System.map

//生成map文件

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

else

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

(obj)uboot.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

//倘若没有include $(OBJTREE)/include/config.mk 文件,将打印出错误信息“System not configured

//- see README”,并且make程序也结束

.PHONY : CHANGELOG

CHANGELOG:

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

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

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

unconfig:

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

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

//删除配置文件,即与make *_config过程相反

//剩下的*_config,属于相似内容,只举一个常见的smdk2410_config,以作说明

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

//执行make smdk2410_config,就会调用顶层目录中的mkconfig(shell脚本),同时给这个脚本传入参数

//arm arm920t smdk2410 NULL s3c24x0。mkconfig脚本根据这些传入的参数生成与开发板相适应的一系列配置文件。

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

//不要忘记makfile的最后边,还有一部分很重要的内容

clean: //clean删除
find (OBJTREE)typef \(namecoreoname.bakoname  oname.ooname.a\)print |xargsrmfrmf(obj)examples/hello_world (obj)examples/timer (obj)examples/eepro100_eeprom (obj)examples/sched (obj)examples/mem_to_mem_idma2intr (obj)examples/82559eeprom (obj)examples/smc91111_eeprom (obj)examples/interrupt (obj)examples/test_burst
rm -f (obj)tools/img2srec(obj)tools/mkimage (obj)tools/envcrc (obj)tools/gen_eth_addr
rm -f (obj)tools/mpc86xclk(obj)tools/ncb
rm -f (obj)tools/easylogo/easylogo(obj)tools/bmp_logo
rm -f (obj)tools/gdb/astest(obj)tools/gdb/gdbcont (obj)tools/gdb/gdbsendrmf(obj)tools/env/fw_printenv (obj)tools/env/fwsetenvrmf(obj)board/cray/L1/bootscript.c (obj)board/cray/L1/bootscript.imagermf(obj)board/netstar/eeprom (obj)board/netstar/crcek(obj)board/netstar/crcit
rm -f (obj)board/netstar/.srec(obj)board/netstar/*.bin
rm -f (obj)board/trab/trabfkt(obj)board/voiceblue/eeprom
rm -f (obj)board/integratorap/uboot.lds(obj)board/integratorcp/u-boot.lds
rm -f (obj)include/bmplogo.hrmf(obj)nand_spl/u-boot-spl $(obj)nand_spl/u-boot-spl.map

clobber: clean //除了调用clean删除,还要再执行额外的删除命令
find (OBJTREE)typef\(name.depend oname.sreconame.binonameuboot.img\) print0 |xargs0rmfrmf(OBJS) (obj).bak(obj)ctags (obj)etags(obj)TAGS (obj)include/versionautogenerated.hrmfr(obj)*.*~
rm -f (obj)uboot(obj)u-boot.map (obj)uboot.hex(ALL)
rm -f (obj)tools/crc32.c(obj)tools/environment.c (obj)tools/env/crc32.crmf(obj)tools/inca-swap-bytes (obj)cpu/mpc824x/bedbug603e.crmf(obj)include/asm/proc (obj)include/asm/arch(obj)include/asm
[ ! -d (OBJTREE)/nandspl]||find(obj)nand_spl -lname "*" -print | xargs rm -f

ifeq ((OBJTREE),(SRCTREE))
mrproper \
distclean: clobber unconfig //倘若目标目录与源目录相同,distclean调用clobber unconfig来删除
else
mrproper \
distclean: clobber unconfig //倘若目标目录与源目录不相同,distclean调用clobber unconfig来删除
rm -rf $(OBJTREE)/*         //而且,还要删除OBJTREE目录下的所有内容
endif

backup: //打包备份
F=`basename $(TOPDIR)` ; cd .. ; \                         //跳到当前目录的外边,然后打包整个文件夹
gtar --force-local -zcvf `date "+F-%Y-%m-%d-%T.tar.gz"` F //打包,名字中插入日期

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

//最后,插入编译过程中重要的打印信息(要深入理解u-boot的makefile工作原理,必须做实际的实验,实际的make一下,

//看看你正想了解的地方发生了什么)

start.o

libsmdk2410.a

libarm920t.a

all目标的实现

 

参考博客:U-Boot Makefile分析

posted on   amanlikethis  阅读(3397)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示