U-Boot源码分析1—Makefile
- 1 分析思路
- 2 u-boot源码目录结构
- 3 Makefile源码
- 3.1 版本号
- 3.2 环境变量
- 3.3 Beautify output
- 3.4 输出文件的目录设置、PHONY目标
- 3.6 目录信息
- 3.5 Source Code Checker
- 3.7 设置单独编译模块、PHONY目标
- 3.8 获取宿主机的架构和系统
- 3.9 设置交叉编译工具链前缀
- 3.10 设置配置文件
- 3.11 定义变量
- 3.12 include文件
- 3.13 设置交叉编译工具链
- 3.14 PHONY += scripts_basic
- 3.15 PHONY += outputmakefile
- 3.16 config相关变量
- 3.17 ifeq ($(mixed-targets),1)
- 3.18 ifeq ($(config-targets),1)
- 3.19 PHONY += scripts
- 3.20 ifeq ($(dot-config),1)
- 3.21 一些include和变量
- 3.22 libs-y u-boot-init u-boot-main
- 3.23 Add GCC lib
- 3.24 LDPPFLAGS
- 3.25 BOARD_SIZE_CHECK
- 3.26 DO_STATIC_RELA
- 3.27 ALL-y
- 3.28 LDFLAGS_u-boot
- 3.29 quiet_cmd和cmd
- 3.30 all
- 3.31 PHONY += dtbs
- 3.32 u-boot.bin
1 分析思路
上一章中,完成了u-boot-2017.11源码的编译、烧写和运行,本章对U-Boot源码进行分析,根据README文件中的描述,一方面从Makefile源码入手,另一方面将U-Boot编译过程的输出保存下来,辅助分析
make CROSS_COMPILE=aarch64-linux-gnu- V=1
set -e; : ' CHK include/config/uboot.release'; mkdir -p include/config/; echo "2017.11$(/bin/bash ./scripts/setlocalversion .)" < include/config/auto.conf > include/config/uboot.release.tmp; if [ -r include/config/uboot.release ] && cmp -s include/config/uboot.release include/config/uboot.release.tmp; then rm -f include/config/uboot.release.tmp; else : ' UPD include/config/uboot.release'; mv -f include/config/uboot.release.tmp include/config/uboot.release; fi
set -e; : ' CHK include/generated/version_autogenerated.h'; mkdir -p include/generated/; (echo \#define PLAIN_VERSION \"2017.11""\"; echo \#define U_BOOT_VERSION \"U-Boot \" PLAIN_VERSION; echo \#define CC_VERSION_STRING \"$(LC_ALL=C aarch64-linux-gnu-gcc --version | head -n 1)\"; echo \#define LD_VERSION_STRING \"$(LC_ALL=C aarch64-linux-gnu-ld.bfd --version | head -n 1)\"; ) < include/config/uboot.release > include/generated/version_autogenerated.h.tmp; if [ -r include/generated/version_autogenerated.h ] && cmp -s include/generated/version_autogenerated.h include/generated/version_autogenerated.h.tmp; then rm -f include/generated/version_autogenerated.h.tmp; else : ' UPD include/generated/version_autogenerated.h'; mv -f include/generated/version_autogenerated.h.tmp include/generated/version_autogenerated.h; fi
set -e; : ' CHK include/generated/timestamp_autogenerated.h'; mkdir -p include/generated/; (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"'; LC_ALL=C ${DATE} -u -d "${SOURCE_DATE}" +'#define U_BOOT_BUILD_DATE 0x%Y%m%d'; 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"'; LC_ALL=C date +'#define U_BOOT_BUILD_DATE 0x%Y%m%d'; fi) < Makefile > include/generated/timestamp_autogenerated.h.tmp; if [ -r include/generated/timestamp_autogenerated.h ] && cmp -s include/generated/timestamp_autogenerated.h include/generated/timestamp_autogenerated.h.tmp; then rm -f include/generated/timestamp_autogenerated.h.tmp; else : ' UPD include/generated/timestamp_autogenerated.h'; mv -f include/generated/timestamp_autogenerated.h.tmp include/generated/timestamp_autogenerated.h; fi
make -f ./scripts/Makefile.build obj=scripts/basic
rm -f .tmp_quiet_recordmcount
make -f ./scripts/Makefile.build obj=.
mkdir -p lib/aarch64-linux-gnu-gcc -Wp,-MD,lib/.asm-offsets.s.d -nostdinc -isystem /usr/lib/gcc-cross/aarch64-linux-gnu/11/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -fshort-wchar -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -Werror=date-time -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -D__LINUX_ARM_ARCH__=8 -I./arch/arm/mach-sunxi/include -DDO_DEPS_ONLY -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(asm_offsets)" -D"KBUILD_MODNAME=KBUILD_STR(asm_offsets)" -fverbose-asm -S -o lib/asm-offsets.s lib/asm-offsets.c
....
2 u-boot源码目录结构
U-Boot源码目录结构如下
根据u-boot-2017.11根目录下README文件的说明,各目录内容如下
目录/文件 名称 | 描述 |
---|---|
/arch | Architecture specific files |
/arch/arm | Files generic to ARM architecture |
/api | Machine/arch independent API for external apps |
/board | Board dependent files |
/cmd | U-Boot commands functions |
/common | Misc architecture independent functions |
/configs | Board default configuration files |
/disk | Code for disk drive partition handling |
/doc | Documentation (don’t expect too much) |
/drivers | Commonly used device drivers |
/dts | Contains Makefile for building internal U-Boot fdt |
/examples | Example code for standalone applications, etc |
/fs | Filesystem code (cramfs, ext2, jffs2, etc.) |
/include | Header Files |
/lib | Library routines generic to all architectures |
Licenses | Various license files |
/net | Networking code |
/post | Power On Self Test |
/scripts | Various build scripts and Makefiles |
/test | Various unit test files |
/tools | Tools to build S-Record or U-Boot images, etc. |
config.mk | This file is included from ./Makefile and spl/Makefile |
Kbuild | Generate generic-asm-offsets.h, asm-offsets.h |
MAINTAINERS | Maintainers List |
Makefile | 顶层Makefile |
3 Makefile源码
以u-boot-2017.11源码根目录下Makefile为例,逐行分析。考虑行号对应,使用截图显示代码
3.1 版本号
第5行~9行是u-boot版本号
VSERION为主版本号,PATCHLEVEL为补丁版本,SUBLEVEL为子版本号,EXTRAVERSION为附加版本信息,NAME为名字。
从2008年10月之后,U-Boot中VSERION表示发布年份,PATCHLEVEL为月份,EXTRAVERSION表示候选版本,其它基本不用。
3.2 环境变量
第20行~29行是一些环境变量的定义
在Makefile中,通过export关键字将变量传递到下层Makefile中,或者通过unexport关键字不让某些变量传递到下层Makefile。但SHELL和MAKEFLAGS两个变量不论是否export,总会传递到下层Makefile
第20行,在MAKEFLAGS变量后追加
-rR --include-dir=$(CURDIR)
- -r表示禁止make使用任何隐含规则;
- -R表示禁止make使用任何作用于变量上的隐含规则
- –include-dir=< dir >或者-I < dir >用于指定一个被包含makefile的搜索目标。可以使用多个-I参数来指定多个目录
- $(CURDIR)表示当前目录
第23行~29行,不传递LC_ALL、GREP_OPTIONS变量,并将LC_COLLATE、LC_NUMERIC变量均赋值为C,并进行传递。LC_COLLATE、LC_NUMERIC是有关区域支持的规则变量。
变量 | 说明 |
---|---|
LC_COLLATE | 字符串排序顺序 |
LC_CTYPE | 字符分类(什么是一个字符?它的大写形式是否等效?) |
LC_MESSAGES | 消息使用的语言Language of messages |
LC_MONETARY | 货币数量使用的格式 |
LC_NUMERIC | 数字的格式 |
LC_TIME | 日期和时间的格式 |
区域支持指的是应用遵守文化偏好的问题,包括字母表、排序、数字格式等。如果你想让系统表现得象没有区域支持,那么使用特殊的区域名C或者等效的POSIX。使用
locale -a
可以查看所有支持区域的列表
3.3 Beautify output
第73行~101行对编译过程中的信息输出进行控制。U-Boot的顶层Makefile中可以根据命令行参数控制编译过程中的信息输出,如V=1等。
这里使用origin函数,此函数不操作变量的值,返回变量的来源,比如来源于命令行、环境变量等
$(origin <varible>)
origin函数返回值 | 说明 |
---|---|
undefined | 从来没有定义过 |
default | 默认变量 |
environment | 环境变量,且当Makefile被执行时,-e参数没有被打开 |
file | 被定义在Makefile中 |
command line | 命令行 |
override | 变量是被override指示符重新定义的 |
automatic | 自动化变量 |
第73行~86行,使用origin函数判断变量V的来源,如果来源于命令行,则将KBUILD_VERBOSE赋值为V的值,否则为0;后续对KBUILD_VERBOSE的值进行判断,如果KBUILD_VERBOSE=1,则变量quiet和Q均为空,否则quiet=quiet_,Q = @,并将KBUILD_VERBOSE、quiet和Q传递到下层Makefile(第101行)。
第91行~99行,使用filter函数判断make版本,filter函数以< pattern >模式过滤< text >字符串中的单词,保留符合模式< pattern >的单词,可以有多个模式,返回符合< pattern >的字符串
$(filter <pattern...>,<text>)
则91行语句
ifneq ($(filter 4.%,$(MAKE_VERSION)),)
即使用filter函数获取MAKE_VERSION中类似4.%模式的字符串,在终端中查看make版本可知为4.3,即filter函数返回值为"4.3"。因此这条语句即为判断filter返回值是否不等于空值,很明显满足判断条件,执行下一行语句
第92行
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
这里与上一行语句类似,不过使用了firstword函数获取MAKEFLAGS变量的第一个单词
如果不为空则继续向下执行,quiet变量赋值为silent_
这里在Makefile中增加几行代码(图中103行~108行)看看变量MAKEFLAGS以及firstword输出的值(注意@echo必须以tab开头),后续编译U-Boot时记得删除这几行代码
因此,当使用默认参数编译时,quiet=quiet_,Q=@,编译信息输出较少
tzw@tzw-virtual-machine:~/arm/u-boot/u-boot-2017.11$ make CROSS_COMPILE=aarch64-linux-gnu-
MAKEFLAGS=rR -I/home/tzw/arm/u-boot/u-boot-2017.11 --no-print-directory -- CROSS_COMPILE=aarch64-linux-gnu-
firstword=xrR
quiet=quiet_
Q=@
KBUILD_VERBOSE=0
增加-s(静默编译)时,quiet=silent_,Q=@此时不输出信息
tzw@tzw-virtual-machine:~/arm/u-boot/u-boot-2017.11$ make CROSS_COMPILE=aarch64-linux-gnu- -s
MAKEFLAGS=rRs -I/home/tzw/arm/u-boot/u-boot-2017.11 --no-print-directory -- CROSS_COMPILE=aarch64-linux-gnu-
firstword=xrRs
quiet=silent_
Q=@
KBUILD_VERBOSE=0
增加V=1(显示所有编译过程输出信息)时,quiet和Q均为空,此时输出完整信息
tzw@tzw-virtual-machine:~/arm/u-boot/u-boot-2017.11$ make CROSS_COMPILE=aarch64-linux-gnu- V=1
MAKEFLAGS=rR -I/home/tzw/arm/u-boot/u-boot-2017.11 --no-print-directory -- V=1 CROSS_COMPILE=aarch64-linux-gnu-
firstword=xrR
quiet=
Q=
KBUILD_VERBOSE=1
通过变量quiet和Q的值对输出信息进行控制,通常make会把其要执行的命令行在命令执行前进行输出,当在make命令前使用@字符则不会输出显示
$(Q)$(MAKE) XXX
对于quiet变量的使用则不同,通常相同的命令有两种版本,quiet_cmd_xxx输出信息少,cmd_xxx则输出完整的信息,如果变量quiet为空的话,整个命令都会输出,如果变量quiet为“quiet_”的话,仅输出信息少的版本,如果变量quiet为“silent_”的话,整个命令都不会输出。
quiet_cmd_clean = CLEAN $(obj)cmd_clean = rm -f $(__clean-files)
3.4 输出文件的目录设置、PHONY目标
第120行~156行用于设置编译输出文件保存的目录,这个功能一般很少使用
第120行判断KBUILD_SRC是否为空,满足条件(每次执行总是空),执行后续代码
第124行使用origin函数,判断O的来源是否为命令行,如果是那么将KBUILD_OUTPUT赋值为O的值,即可以通过
make O=/xxx/xxx/xxx
指定KBUILD_OUTPUT输出目录output_dir=/xxx/xxx/xxx
第129行指定PHONY
目标,依赖于_all
,而_all
下为空
这里补充一下Makefile的规则格式,其中targets
为目标,这里是_all
,冒号后接目标的必要条件(依赖文件),这里没有,下一行为命令,必须以tab开头,这里也为空
targets: prerequisitescommand
一个简单的Makefile如下,第一行的All
即为make的默认目标,这里同时是伪目标,即不会生成All文件,但是All的依赖文件将会生成;而All的依赖文件如何生成呢,在第6行可以找到,hello
依赖于hello.o,并且第7行以tab开头,给出生成hello这个目标所需执行的命令,即gcc hello.o -o hello
;同理第3行给出了hello.o
目标的规则,即依赖于hello.c,通过执行命令gcc -c hello.c -o hello.o
来生成hello.o
All: hellohello.o: hello.cgcc -c hello.c -o hello.ohello: hello.ogcc hello.o -o helloclean:rm -rf hello hello.o
而第9行的clean
目标没有依赖文件,仅有执行的命令,即rm -rf hello hello.o
,删除生成的所有文件;这里clean不是默认目标,因此执行时须通过指定目标,即make clean
指定执行的目标为clean才会执行
因此第129行为Makefile中首次出现目标,即PHONY
为其默认目标,此时对应的依赖文件为_all
,而_all
为空
第133行是一个特殊的语法,它被用于取消Makefile默认的隐含规则。这个语句的作用是阻止Make命令在当前目录下自动查找和使用默认的隐含规则来构建目标
一般来说,Makefile: 这个目标是用来制定如何创建或更新名为 “Makefile” 的目标的规则。当在命令行运行 “make” 命令或者 “make Makefile” 命令时,这个目标就会被构建。
然而在$(CURDIR)/Makefile Makefile: ;
这条规则中,“Makefile:” 后面并没有跟随任何命令,因此这实际上是一个空规则,用途在于取消 Make 的隐含规则,而没有指向具体的构建方法。
特别地,如果对一个项目使用 “make” 命令,而该项目的 Makefile 中包含了类似你给出的规则,那么 “make” 命令就不会尝试寻找并运行任何默认的或者隐含的规则去构建名为 “Makefile” 的目标。
这样就避免了可能存在的,由默认或隐含规则产生的意外构建行为
说白了,直接在Makefile文件所在的目录运行命令make时,如果命中了 make Makefile,就会调用一条空命令什么都不做
第135行~140行,首先判断KBUILD_OUTPUT是否不为空,即如果通过O=/xxx/xxx/xxx
指定了输出目录路径,则将路径保存到saved-output变量中(后续用于信息输出),然后使用shell函数执行mkdir和cd,并将创建成功后的绝对路径赋值给KBUILD_OUTPUT,然后进入此目录
shell函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号`是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回
注意,这个函数会新生成一个Shell程序来执行命令,所以需要注意其运行性能,如果Makefile中有一些比较复杂的规则,并大量使用了这个函数,那么对于系统性能是有害的。特别是Makefile的隐晦的规则可能会让shell函数执行的次数比想像的多得多
/bin/pwd --help
Usage: /bin/pwd [OPTION]...
Print the full filename of the current working directory.-L, --logical use PWD from environment, even if it contains symlinks-P, --physical avoid all symlinks--help display this help and exit--version output version information and exitIf no option is specified, -P is assumed.NOTE: your shell may have its own version of pwd, which usually supersedes
the version described here. Please refer to your shell's documentation
for details about the options it supports.GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Full documentation <https://www.gnu.org/software/coreutils/pwd>
or available locally via: info '(coreutils) pwd invocation'
第141行,如果创建不成功,则输出相关错误信息
第144~147行,在伪目标后添加$ (MAKECMDGOALS)和sub-make伪目标(这里MAKECMDGOALS为空,可以使用@echo输出查看),然后使用反过滤函数filter-out,返回$ (MAKECMDGOALS)中去除_all、sub-make、$(CURDIR)/Makefile的内容
make的环境变量叫MAKECMDGOALS 这个变量中会存放指定的最终目标的列表,如果在命令行上没有指定目标,那么这个变量是空值。
第149~151行,确定sub-make的规则,即
$(Q)$(MAKE) -C output_dir KBUILD_SRC='pwd' -f 'pwd'/Makefile [Targets]
其中'pwd'
为当前目录绝对路径即~/arm/u-boot/u-boot-2017.11
。即指定输出目录output_dir=/xxx/xxx/xxx进行make
第154行,将skip-makefile的值覆盖为1,此时根据第159行的判断,skip-makefile的值不为空,不满足条件,第159行~1699行的内容不会执行,Makefile将提前结束
这一段的功能一般很少使用,因此后续继续执行第159行~1699行的内容。则PHONY
的依赖文件依旧只有_all all
3.6 目录信息
第164行,在MAKEFLAGS变量后增加--no-print-directory
选项,在编译过程中不会输出"Entering directory ..."
之类的信息
3.5 Source Code Checker
第156行~181行,是否启用代码检查功能
首先还是通过origin函数判断是否从命令行输入C参数,即make C=1
或者make C=2
如果有的话将C的值赋值给KBUILD_CHECKSRC,然后判断是否定义了KBUILD_CHECKSRC,否则将KBUILD_CHECKSRC的值赋为0
这里根据166行~174行的注释,可知C=1时仅检查重新编译的文件,C=2时对所有源文件进行检查
3.7 设置单独编译模块、PHONY目标
第186行~223行,设置单独的模块编译,并设置一些变量
第186行~188行,判断是否定义了SUBDIRS,如果定义了SUBDIRS,变量KBUILD_EXTMOD=SUBDIRS,这里是为了支持老语法make SUBIDRS=dir
第190行~192行,同样使用origin函数判断命令行是否输入参数M,所有则将KBUILD_EXTMOD赋值为M的值,即使用make M=/dir
方式定义所需编译的模块
第196行添加all
为PHONY
的依赖文件,则此时PHONY
依赖文件为_all all
第197行~201行,判断KBUILD_EXTMOD是否为空,即是否定义了需要编译的模块,若无则_all规则依赖于all,因此将先编译all;否则将_all规则依赖modules,进行模块编译。模块编译一般很少用,因此会编译all
第203行~213行,判断KBUILD_SRC是否为空,若为空则将当前位置的srctree值赋值为.
,即当前目录;否则根据KBUILD_SRC值对srctree进行赋值,这里KBUILD_SRC为空,srctree值赋值为.
第214行~216行,设置src和obj的值,均为当前目录.
第218行,由于KBUILD_EXTMOD为空,因此VPATH的值为srctree的值,即当前目录.
第220行,传递srctree objtree VPATH
三个变量的值
第223行,不传递CDPATH的值
3.8 获取宿主机的架构和系统
第227行~240行,获取宿主机架构和系统
首先介绍几个命令
获取主机的架构uname -m
或uname --machine
,获取主机系统uname -s
或者uname --kernel-name
sed是linux的一个流编辑器,每次从输入中读取一行数据,根据所提供的编辑器命令匹配数据,并返回新的数据或输出到STDOUT。其中s命令如下
sed s/pattern/replacement/flags
即使用replacement替换数据中的pattern
而-e选项用于多个sed指令的情况
tr命令,将输入的数据中SET1全部转换为SET2,而[:lower:]
表示所有小写字母,[:upper:]
表示所有大写字母
tr [option] 'SET1' 'SET2'
因此对于第227行~235行,首先通过uname -m
命令获取宿主机系统架构,这里为x86_64
,并通过sed
命令,将其中的i.86
替换为x86
,其它行同理,这里最终结果仍为HOSTARCH=x86_64
第237行~238行,首先通过uname -s
命令获取宿主机系统名称,这里为Linux
,然后通过tr
命令将其转变为小写,后续的sed
命令因没有匹配的数据则不执行,最终结果为HOSTOS=linux
第240行,传递HOSTARCH
和HOSTOS
变量
3.9 设置交叉编译工具链前缀
第245行~247行,设置交叉编译工具链
这里首先对Makefile的几种赋值方式进行说明
赋值方式 | 说明 |
---|---|
= | 直接赋值,与位置无关,系统将自动推导,将最终的赋值作为该变量的值 |
:= | 覆盖式赋值,只能推导该符号位置之前的值 |
+= | 追加赋值,旧值保持不变,将新值增加到旧值后面 |
?= | 当某变量前面已经定义赋值过,则不执行本次定义赋值,否则执行本次赋值 |
这里第245行判断HOSTARCH
与ARCH
是否相等,肯定不相等,因此对CROSS_COMPILE
进行赋值,即执行make
命令时,总是需要指定交叉编译工具链,否则CROSS_COMPILE
为空,后续执行将报错
make CROSS_COMPILE=aarch64-linux-gnu-
3.10 设置配置文件
第249行~255行,设置配置文件、设置所使用的shell
第249行~250行,将KCONFIG_CONFIG
的值设置为.config
,即通过make xxx_defconfig
在u-boot-2017.11根目录下生成的配置文件
第253行~255行,使用shell函数,通过if -x FILE
判断FILE
存在且是可执行的则为真,这里CONFIG_SHELL
最终为/bin/bash
(可以使用@echo输出查看)
3.11 定义变量
第257行~261行,定义HOSTCC
、HOSTCXX
、HOSTCFLAGS
、HOSTCXXFLAGS
4个变量
后续第263行~295行,判断HOSTOS
是否为cygwin
或者darwin
,将对上述变量值进行修改,这里HOSTOS=linux
,因此上述变量值不变,最终为
HOSTCC = cc
HOSTCXX = c++
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTCXXFLAGS = -O2
第300行~326行,对KBUILD_MODULES
、KBUILD_BUILTIN
、KBUILD_CHECKSRC
、KBUILD_SRC
、KBUILD_EXTMOD
变量进行赋值
结合前述分析,最终值为
KBUILD_MODULES =
KBUILD_BUILTIN = 1
KBUILD_CHECKSRC = 0
KBUILD_SRC =
KBUILD_EXTMOD =
3.12 include文件
第329行~330行,调用scripts/Kbuild.include
,引入其中定义的变量
3.13 设置交叉编译工具链
第334行~391行,设置交叉编译工具链以及一些变量值,并传递相关变量
第334行~348行,定义所需的交叉编译工具链
其中第336行用到一个特殊的设备/dev/null
,是一个空设备文件,向其写入的东西将被丢弃
这里首先使用shell
函数新生成一个shell执行后续命令,即执行
aarch64-linux-gnu-ld.bfd -v 2> /dev/null
其中aarch64-linux-gnu-ld.bfd -v
表示取aarch64-linux-gnu-ld.bfd的版本号,后续的2>
意为将标准错误输出(stderr)重定向至/dev/null
,即丢弃所有错误输出(2
为stderr的文件描述符,stdout文件描述符为1
,stdin文件描述符为0
)
因此336行语句意为,检查aarch64-linux-gnu-ld.bfd的版本,如果报错则将所有错误信息丢弃,因此会返回空值。也就是通过检查版本判断系统是否有aarch64-linux-gnu-ld.bfd,如果有则将LD
的赋值为aarch64-linux-gnu-ld.bfd
第349行~368行,定义一些工具和变量
这里编辑Makefile文件,将关心的变量进行输出,然后通过make CROSS_COMPILE=aarch64-linux-gnu- PrintInfo
命令,将Target
指定为PrintInfo
,仅输出需要的打印信息
这些变量在顶层Makefile中没有定义,因此必定在其它文件中定义,然后在Makefile中使用,后续结合具体的make编译过程进行分析
3.14 PHONY += scripts_basic
第397行~403行,在PHONY
后添加scripts_basic
依赖项,此时PHONY
依赖项为_all all scripts_basic
对于scripts_basic
的执行语句,根据前述分析,这里Q的值为空或者@
,build的值在Makefile中没有出现,应该在其它文件中有定义。根据名称来看,大概率在scripts目录下,执行搜索匹配,其中-r
表示在所有子目录下递归搜索,-w
表示全字匹配
grep -rw "build"
从结果中可以看到,在/scripts/Kbuild.include中对build进行了赋值,根据3.12节,可知顶层Makefile确实引用了这个文件,查看其内容,第181行将build赋值为build := -f $(srctree)/scripts/Makefile.build obj
由3.7节中的分析,可知srctree的值为.
因此这里scripts_basic
的执行语句最终为
scripts_basic:make -f ./scripts/Makefile.build obj=scripts/basicrm -f .tmp_quiet_recordmcount
3.15 PHONY += outputmakefile
第405行~414行,在PHONY
后添加outputmakefile
依赖项,此时PHONY
依赖项为_all all scripts_basic outputmakefile
由于未定义KBUILD_SRC
的值,因此outputmakefile
执行命令为空
3.16 config相关变量
第424行~448行,定义一些变量
include/generated/version_autogenerated.h
和include/generated/timestamp_autogenerated.h
两个文件是自动生成的文件,包含版本信息和时间信息
第435行~448行,根据make的输入目标对config-targets
、mixed-targets
、dot-config
三个变量的值进行修改
当执行make xxx_defconfig
时
config-targets = 1
mixed-targets = 0
dot-config = 1
当执行make CROSS_COMPILE=aarch64-linux-gnu-
时
config-targets = 0
mixed-targets = 0
dot-config = 1
3.17 ifeq ($(mixed-targets),1)
第450行,根据上述分析,不满足条件,因此执行else后面的语句
3.18 ifeq ($(config-targets),1)
第467行,根据上述分析,当执行make xxx_defconfig
时,满足条件,因此继续执行472行~479行
当执行make CROSS_COMPILE=aarch64-linux-gnu-
时,不满足条件,因此执行else分支后续的语句
3.19 PHONY += scripts
第489行~491行,在PHONY
后添加scripts
依赖项,此时PHONY
依赖项为_all all scripts_basic outputmakefile scripts
scripts
依赖项为scripts_basic include/config/auto.conf
,scripts_basic
在前已经分析过,include/config/auto.conf
文件是执行make xxx_defconfig
时自动生成的,保存了各个配置内容
scripts
执行语句如下,$(@)
表示规则中的目标文件集
make -f ./scripts/Makefile.build obj=$(@)
3.20 ifeq ($(dot-config),1)
第493行,根据上述分析,满足条件,因此执行后续语句
3.21 一些include和变量
第495行~524行
include/config/auto.conf
文件是执行make xxx_defconfig
时自动生成的,保存了各个配置内容
include/config/auto.conf.cmd
文件中保存了所有Kconfig文件的路径,Kconfig文件主要是各种配置界面的源文件
KCONFIG_CONFIG
变量的值在前面分析过,为.config
include/autoconf.mk
文件中保存了各项配置内容
include/autoconf.mk.dep
文件中保存了所有需要的头文件的路径
config.mk
文件保存了与ARCH、CPU、BOARD等相关的参数
第530行~578行
其中EFI_LDS
、EFI_CRT0
、EFI_RELOC
在/arch/arm/cpu/armv8/config.mk
中定义,CFLAGS_EFI
、CFLAGS_NON_EFI
在arch/arm/config.mk
中定义
EFI_LDS=elf_aarch64_efi.lds
EFI_CRT0=crt0_aarch64_efi.o
EFI_RELOC=reloc_aarch64_efi.o
CFLAGS_EFI=-fpic -fshort-wchar
CFLAGS_NON_EFI=-fno-pic -ffixed-r9 -ffunction-sections -fdata-sections
EFI_TARGET=
LDSCRIPT=./arch/arm/cpu/armv8/u-boot.lds
BOARDDIR=sunxi
CPUDIR=arch/arm/cpu/armv8
ARCH=arm
第584行判断,未定义CONFIG_XTENSA
,因此满足条件,LDPPFLAGS += -ansi
第588行~624行,
这里KBUILD_CFLAGS
在前面已经进行了赋值,588行前其值为
KBUILD_CFLAGS = -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -fshort-wchar
查看是否有CONFIG_CC_OPTIMIZE_FOR_SIZE
的定义
因此执行KBUILD_CFLAGS += -Os
随后继续在后添加-fno-stack-protector
、-fno-delete-null-pointer-checks
、-g
选项
第603行,满足条件
因此添加-fstack-usage
选项
随后添加-Wno-format-nonliteral
、-Werror=date-time
选项
因此,最终KBUILD_CFLAGS
的值为-Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -fshort-wchar -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -Werror=date-time
第628行~643行
UBOOTINCLUDE = -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h
NOSTDINC_FLAGS = -nostdinc -isystem /usr/lib/gcc-cross/aarch64-linux-gnu/11/include
CHECKFLAGS = -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise -Wno-return-void -D__CHECK_ENDIAN__ -nostdinc -isystem /usr/lib/gcc-cross/aarch64-linux-gnu/11/include
cpp_flags = -D__KERNEL__ -D__UBOOT__ -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -D__LINUX_ARM_ARCH__=8 -I./arch/arm/mach-sunxi/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -nostdinc -isystem /usr/lib/gcc-cross/aarch64-linux-gnu/11/include
c_flags = -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -fshort-wchar -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -Werror=date-time -D__KERNEL__ -D__UBOOT__ -D__ARM__ -fno-pic -mstrict-align -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -fno-common -ffixed-x18 -pipe -march=armv8-a -D__LINUX_ARM_ARCH__=8 -I./arch/arm/mach-sunxi/include -Iinclude -I./arch/arm/include -include ./include/linux/kconfig.h -nostdinc -isystem /usr/lib/gcc-cross/aarch64-linux-gnu/11/include
3.22 libs-y u-boot-init u-boot-main
第648行~713行,主要为获取配置的libs-y
的值
这里有一个巧妙的用法,libs-$(CONFIG_CMD_NAND)
其中CONFIG_CMD_NAND
一般在XXXconfig文件中定义CONFIG_CMD_NAND=y
,因此若配置的时候定义了,那么libs-$(CONFIG_CMD_NAND)
的值即为libs-y
根据配置将所有用到的库路径保存在libs-y
中,然后第704行通过sort
函数按照字母顺序升序排序,第710行通过patsubst
函数将libs-y
末尾的/
替换成/built-in.o
各值如下
libs-y = arch/arm/cpu/built-in.o arch/arm/cpu/armv8/built-in.o arch/arm/lib/built-in.o arch/arm/mach-sunxi/built-in.o board/sunxi/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 drivers/i2c/built-in.o drivers/mtd/built-in.o drivers/mtd/onenand/built-in.o drivers/mtd/spi/built-in.o drivers/net/built-in.o drivers/net/phy/built-in.o drivers/pci/built-in.o drivers/power/built-in.o drivers/power/battery/built-in.o drivers/power/domain/built-in.o drivers/power/fuel_gauge/built-in.o drivers/power/mfd/built-in.o drivers/power/pmic/built-in.o drivers/power/regulator/built-in.o drivers/serial/built-in.o drivers/spi/built-in.o drivers/usb/common/built-in.o drivers/usb/dwc3/built-in.o drivers/usb/emul/built-in.o drivers/usb/eth/built-in.o drivers/usb/gadget/built-in.o drivers/usb/gadget/udc/built-in.o drivers/usb/host/built-in.o drivers/usb/musb-new/built-in.o drivers/usb/musb/built-in.o drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o env/built-in.o fs/built-in.o lib/built-in.o net/built-in.o test/built-in.o test/dm/built-in.ou-boot-dirs =arch/arm/cpu arch/arm/cpu/armv8 arch/arm/lib arch/arm/mach-sunxi board/sunxi cmd common disk drivers drivers/dma drivers/gpio drivers/i2c drivers/mtd drivers/mtd/onenand drivers/mtd/spi drivers/net drivers/net/phy drivers/pci drivers/power drivers/power/battery drivers/power/domain drivers/power/fuel_gauge drivers/power/mfd drivers/power/pmic drivers/power/regulator drivers/serial drivers/spi drivers/usb/common drivers/usb/dwc3 drivers/usb/emul drivers/usb/eth drivers/usb/gadget drivers/usb/gadget/udc drivers/usb/host drivers/usb/musb-new drivers/usb/musb drivers/usb/phy drivers/usb/ulpi env fs lib net test test/dm tools examplesu-boot-alldirs=api arch/arm/cpu arch/arm/cpu/armv8 arch/arm/lib arch/arm/mach-sunxi board/sunxi cmd common disk drivers drivers/ddr/altera drivers/ddr/fsl drivers/dma drivers/gpio drivers/i2c drivers/mtd drivers/mtd/nand drivers/mtd/onenand drivers/mtd/spi drivers/mtd/ubi drivers/net drivers/net/fm drivers/net/phy drivers/pci drivers/power drivers/power/battery drivers/power/domain drivers/power/fuel_gauge drivers/power/mfd drivers/power/pmic drivers/power/regulator drivers/serial drivers/spi drivers/usb/common drivers/usb/dwc3 drivers/usb/emul drivers/usb/eth drivers/usb/gadget drivers/usb/gadget/udc drivers/usb/host drivers/usb/musb drivers/usb/musb-new drivers/usb/phy drivers/usb/ulpi dts env examples fs lib net post test test/dm test/env test/overlay toolshead-y=arch/arm/cpu/armv8/start.ou-boot-init=arch/arm/cpu/armv8/start.ou-boot-main= arch/arm/cpu/built-in.o arch/arm/cpu/armv8/built-in.o arch/arm/lib/built-in.o arch/arm/mach-sunxi/built-in.o board/sunxi/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 drivers/i2c/built-in.o drivers/mtd/built-in.o drivers/mtd/onenand/built-in.o drivers/mtd/spi/built-in.o drivers/net/built-in.o drivers/net/phy/built-in.o drivers/pci/built-in.o drivers/power/built-in.o drivers/power/battery/built-in.o drivers/power/domain/built-in.o drivers/power/fuel_gauge/built-in.o drivers/power/mfd/built-in.o drivers/power/pmic/built-in.o drivers/power/regulator/built-in.o drivers/serial/built-in.o drivers/spi/built-in.o drivers/usb/common/built-in.o drivers/usb/dwc3/built-in.o drivers/usb/emul/built-in.o drivers/usb/eth/built-in.o drivers/usb/gadget/built-in.o drivers/usb/gadget/udc/built-in.o drivers/usb/host/built-in.o drivers/usb/musb-new/built-in.o drivers/usb/musb/built-in.o drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o env/built-in.o fs/built-in.o lib/built-in.o net/built-in.o test/built-in.o test/dm/built-in.o
3.23 Add GCC lib
第717行~724行,
没有定义CONFIG_USE_PRIVATE_LIBGCC=y
,第717行不满足条件,因此PLATFORM_LIBGCC = -L /usr/lib/gcc-cross/aarch64-linux-gnu/11 -lgcc
,PLATFORM_LIBS = -L /usr/lib/gcc-cross/aarch64-linux-gnu/11 -lgcc
,表示使用GCC的支持库libgcc.a
3.24 LDPPFLAGS
第729行~733行,给定LDPPFLAGS的值,LDPPFLAGS=-ansi -include ./include/u-boot/u-boot.lds.h -DCPUDIR=arch/arm/cpu/armv8
3.25 BOARD_SIZE_CHECK
第738行~751行,设定BOARD_SIZE_CHECK的值,这里为空
3.26 DO_STATIC_RELA
**第757行765行**,这里`CONFIG_STATIC_RELA`在`./include/autoconfg.mk`中定义为`y`,因此DO_STATIC_RELA为760行762行所定义的内容
3.27 ALL-y
第768行~812行,定义ALL-y
的内容
ALL-y = checkarmreloc u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check spl/u-boot-spl.bin u-boot.img u-boot.dtb u-boot-dtb.img u-boot.itb
3.28 LDFLAGS_u-boot
第814行~821行,设置LDFLAGS_u-boot
的值
LDFLAGS_u-boot = -pie --gc-sections -Bstatic --no-dynamic-linker -Ttext 0x4a000000
LDFLAGS_FINAL = --gc-sections -Bstatic
CONFIG_SYS_TEXT_BASE = 0x4a000000
3.29 quiet_cmd和cmd
第824行~857行,定义一些命令
3.30 all
第859行~870行,定义all
的依赖项
3.31 PHONY += dtbs
第872行~876行,添加dtbs
依赖项,此时PHONY
依赖项为_all all scripts_basic outputmakefile scripts dtbs
3.32 u-boot.bin
第881行~904行,定义一些目标
未定义CONFIG_MULTI_DTB_FIT
,因此第881行不满足条件,进入第895行的判断,查找可知在配置的时候定义了CONFIG_OF_SEPARATE
,因此执行此分支
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)
本章先分析到此处,后续根据具体的编译过程进行分析,或留待以后补充
完结撒花✿✿ヽ(°▽°)ノ✿