嵌入式linux系统移植-uboot顶层Makefile


本章感觉没有什么现场操作,但需要耐心观看,丰富自己的基础知识。

U-Boot 工程目录分析

U-Boot 工程还包括编译之后的相关文件,文件夹或文件的含义见表:
在这里插入图片描述
在这里插入图片描述
我们要关注一部分文件夹或文件即可:

1、 arch 文件夹

(1)arch 文件夹存放着和架构有关的文件,比如 arm、 avr32、 m68k 等。
(2)arm 文件夹中存放CPU 有关的文件, I.MX6ULL对应imx-common这个文件夹;cpu文件夹也是和 cpu 架
构有关的。
(3)cpu文件夹存放多种 ARM 架构相关的文件夹,I.MX6ULL 使用的 Cortex-A7 内核,Cortex-A7 属于 armv7uboot.lds是uboot的链接脚本文件,需要重点关注。

2、 board 文件夹

(1)board 文件夹就是和具体的板子有关的,I.MX 系列以前属于 freescale,只是freescale 后来被 NXP 收购了。
(2)freescale 文件夹存放相关芯片的,mx6ul开头的表示使用I.MX6UL 芯片的板子,mx6ullevk 是 NXP官方的 I.MX6ULL开发板。

3、 configs 文件夹

configs 文件夹为 uboot 配置文件, uboot 是可配置的,官方做好的配置文件基础上可以来添加自己想要的功能,这些半导体厂商或者开发板厂商制作好的配置文件统一命名为“xxx_defconfig”,xxx 表示开发板名字。
在这里插入图片描述
在编译 uboot 之前一定要使用 defconfig 来配置 uboot!

4、 .u-boot.xxx_cmd 文件

.u-boot.xxx_cmd 是一系列的文件,这些文件都是编译生成的,都是一些命令文件,一般就是.u-boot.xxx.cmd生成u-boot.xxx。
基本步骤都是:编译.c文件—>.elf文件—>链接.o文件

u-boot.imx 是在 u-boot.bin 文件的头部添加了 IVT、 DCD 等信息(IVT、 DCD 等数据保存在了文件
board/freescale/mx6ullevk/imximage-ddr512.cfg.cfgtmp )。MFGTools 工具将u-boot.imx 烧写到开发板。

在这里插入图片描述

5*、 Makefile 文件

顶层 Makefile 文件, Makefile 是支持嵌套的,也就是顶层 Makefile 可以调用子目录中的 Makefile 文件。(俗称套娃)
uboot 源码根目录下的 Makefile 是顶层 Makefile,他会调用其它的模块的 Makefile 文件,比如 drivers/adc/Makefile。

6、 u-boot.xxx 文件

u-boot.xxx 同样也是一系列文件:
u-boot:编译出来的 ELF 格式的 uboot 镜像文件。
u-boot.bin:编译出来的二进制格式的 uboot 可执行镜像文件。
u-boot.cfg: uboot 的另外一种配置文件。
u-boot.imx: u-boot.bin 添加头部信息以后的文件, NXP 的 CPU 专用文件。
u-boot.lds:链接脚本。
u-boot.map: uboot 映射文件,通过查看此文件可以知道某个函数被链接到了哪个地址上。
u-boot.srec: S-Record 格式的镜像文件。
u-boot.sym: uboot 符号文件。
u-boot-nodtb.bin:和 u-boot.bin 一样, u-boot.bin 就是 u-boot-nodtb.bin 的复制文件。
在这里插入图片描述

7、 .config 文件

uboot 配置文件, 使用命令“make xxx_defconfig”配置 uboot 以后就会自动生成。

.config 文件中都是以“CONFIG_”开始的配置项,这些配置项就是 Makefile 中的变量,因此后面都跟有相应的值, **uboot 的顶层 Makefile 或子 Makefile 会调用这些变量值。**在.config 中会有大量的变量值为‘y’,这些为‘y’的变量一般用于控制某项功能是否使能,为‘y’的话就表示功能使能。

比如 cmd/Makefile中:

obj-$(CONFIG_CMD_BOOTM) += bootm.o
#相当于
obj-y += bootm.o

obj-y 包含着所有要编译的文件对应的.o 文件,这里表示需要编译文件 cmd/bootm.c(总的来说就是obj-y变量中存放着需要编译的文件,谁要被编译,就放在这个变量中)。相当于通过“CONFIG_CMD_BOOTD=y”来使能 bootm 这个命令,进而编译 cmd/bootm.c 这个文件,这个文件实现了命令 bootm。在 uboot 和 Linux 内核中都是采用这种方法来选择使能某个功能,编译对应的源码文件。

8、 README

文件描述了 uboot 的详细信息,包括 uboot 该如何编译、 uboot 中各文件夹的含义、相应的命令等等。

VScode 工程创建

本节这是学到一种VScode的使用方法:在工程目录中和搜索内容中,隐藏指定文件。
(1)在根目录新建工作区uboot.code-workspace。
(2)新建.vscode 文件夹下的settings.json的文件:
发现 arch 目录下没有 avr32 这个文件夹,因为被隐藏了。

{
 	"search.exclude": {
 		"**/node_modules": true,
		"**/bower_components": true,
		"arch/avr32":true,
 	},
	"files.exclude": {
 		"**/.git": true,
		"**/.svn": true,
 		"**/.hg": true,
 		"**/CVS": true,
		"**/.DS_Store": true,
		"arch/avr32":true,
	}
}

U-Boot 顶层 Makefile 分析

在阅读 uboot 源码之前,肯定是要先看一下顶层 Makefile,分析 gcc 版本代码的时候一定是先从顶层 Makefile 开始的,然后再是子 Makefile,这样通过层层分析 Makefile 即可了解整个工程的组织结构。

版本号

5 VERSION = 2016
6 PATCHLEVEL = 03
7 SUBLEVEL =
8 EXTRAVERSION =
9 NAME =

VERSION 是主版本号,
PATCHLEVEL 是补丁版本号,
SUBLEVEL 是次版本号,这三个一起构成了 uboot 的版本号,比如当前的 uboot 版本号就是“2016.03”。
EXTRAVERSION 是附加版本信息,
NAME 是和名字有关的,一般不使用这两个。

MAKEFLAGS 变量

主目录中的 Makefile 调用子目录中的 Makefile:
调用子 make:

$(MAKE) -C subdir

$(MAKE)就是调用“make”命令, 
-C 指定子目录。
subdir 子目录

向子 make 传递变量:

export VARIABLE …… #导出变量给子 make 。
unexport VARIABLE…… #不导出变量给子 make。

有两个特殊的变量:“SHELL”和“MAKEFLAGS”,这两个变量除非使用“unexport”声明,否则的话在整个 make的执行过程中,它们的值始终自动的传递给子 make。

命令输出

uboot 可以在终端显示短命令(默认)或完整命令(V=1)。
话不多说,直接上代码:

73 ifeq ("$(origin V)", "command line")
74 	KBUILD_VERBOSE = $(V)
75 endif
76 ifndef KBUILD_VERBOSE
77 	KBUILD_VERBOSE = 0
78 endif
79
80 ifeq ($(KBUILD_VERBOSE),1)
81 	quiet =
82 	Q =
83 else
84 	quiet=quiet_
85 	Q = @
86 endif

通过V的值确定quiet和Q的值,决定命令行输出格式,
对于Q:

$(Q)$(MAKE) $(build)=tools 
#有@不会在终端输出命令
#无@会在终端输出命令

对于quiet:

#变量 quiet 为空的话,整个命令都会输出。
#变量 quiet 为“quiet_”的话,仅输出短版本。
#变量 quiet 为“silent_”的话,整个命令都不会输出()。

设置 V=0 或者在命令行中不定义 V 的话,编译 uboot 的时候终端中显示的短命令,但是还是会有命令输出。啊~ 这 ~ ,目前有个疑惑,暂定为quiet比@优先级高

静默输出

编译的时候使用“make -s”即可实现静默输出,编译 uboot 的时候不需要输出命令。

88 # If the user is running make -s (silent mode), suppress echoing of
89 # commands
90
91 ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
92 	ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
93 		quiet=silent_
94 	endif
95 else # make-3.8x
96 	ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
97 		quiet=silent_
98 	endif
99 endif
100
101 export quiet Q KBUILD_VERBOSE

就是根据make -s,检测到-s,quiet=silent_则静默输出。

设置编译结果输出目录

uboot 可以将编译出来的目标文件输出到单独的目录中,在 make 的时候使用“O”来指定输出目录,

比如 “make O=out”就是设置目标文件输出到 out 目录中为了将源文件和编译产生的文件分开,当然也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在同一个目录内,一般我们不指定 O 参数。来看Makefile顶层文件中的手法:

#判断“O”是否来自于命令行,如果来自命令行的话条件成立, KBUILD_OUTPUT就为$(O),
#因此变量 KBUILD_OUTPUT 就是输出目录。
124 ifeq ("$(origin O)", "command line")
125 	KBUILD_OUTPUT := $(O)
126 endif
#判断 KBUILD_OUTPUT 是否为空。
#调用 mkdir 命令,创建 KBUILD_OUTPUT 目录,并且将创建成功以后的绝对路径赋值给 KBUILD_OUTPUT。至此,通过 O 指定的输出目录就存在了。
135 ifneq ($(KBUILD_OUTPUT),)
......
138 	saved-output := $(KBUILD_OUTPUT)
139	 	KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT)&& /bin/pwd)
......
155 endif # ifneq ($(KBUILD_OUTPUT),)

代码检查

uboot 支持代码检查,使用命令“make C=1”使能代码检查,检查那些需要重新编译的文
件。

判断 C 是否来源于命令行,如果 C 来源于命令行,那就将 C 赋值给变量
KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0。
命令行老手法了!!!

176 ifeq ("$(origin C)", "command line")
177 	KBUILD_CHECKSRC = $(C)
178 endif
179 ifndef KBUILD_CHECKSRC
180 	KBUILD_CHECKSRC = 0
181 endif

模块编译

在 uboot 中允许单独编译某个模块。
使用命令“ make M=dir”即可,旧语法“ make SUBDIRS=dir”也是支持的。

186 ifdef SUBDIRS
187 	KBUILD_EXTMOD ?= $(SUBDIRS)
188 endif
189
190 ifeq ("$(origin M)", "command line")
191 	KBUILD_EXTMOD := $(M)
192 endif

196 PHONY += all
197 ifeq ($(KBUILD_EXTMOD),)
198 	_all: all
199 else
200 	_all: modules
201 endif
202
203 ifeq ($(KBUILD_SRC),)
204 # building in the source tree
205 	srctree := .
206 else
207 	ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
208 	# building in a subdirectory of the source tree
209 		srctree := ..
210 	else
211 		srctree := $(KBUILD_SRC)
212 	endif
213 endif
214 objtree := .
215 src := $(srctree)
216 obj := $(objtree)
217
218 VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
219
220 export srctree objtree VPATH

第 197 行判断 KBUILD_EXTMOD 时为空,如果为空的话目标_all 依赖 all,因此要先编译出 all。否则的话默认目标_all 依赖 modules,要先编译出 modules,也就是编译模块。 一般情况下我们不会在 uboot 中编译模块,所以此处会编译 all 这个目标。
第 203 行判断 KBUILD_SRC 是否为空,如果为空的话就设置变量 srctree 为当前目录,即srctree 为“.” ,一般不设置 KBUILD_SRC。

获取主机架构和系统

顶层 Makefile 会获取主机架构和系统,也就是我们电脑的架构和系统,代码如下:

227 HOSTARCH := $(shell uname -m | \
228 	sed -e s/i.86/x86/ \
229	 		-e s/sun4u/sparc64/ \
230 		-e s/arm.*/arm/ \
231 		-e s/sa110/arm/ \
232 		-e s/ppc64/powerpc/ \
233 		-e s/ppc/powerpc/ \
234 		-e s/macppc/powerpc/\
235 		-e s/sh.*/sh/)
236
237 HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
238 	sed -e 's/\(cygwin\).*/cygwin/')
239
240 export HOSTARCH HOSTOS

第 227 行定义了一个变量 HOSTARCH,用于保存主机架构,这里调用 shell 命令“uname -m”获取架构名称。


shell 中的“|”表示管道,意思是将左边的输出作为右边的输入,
sed -e 是替换命令,“sed -e s/i.86/x86/”表示将管道输入的字符串中的“i.86”替换为“x86”。


第 237 行定义了变量 HOSTOS,此变量用于保存主机 OS 的值,先使用 shell 命令“name -
s”来获取主机 OS。


tr ‘[:upper:]’ ‘[:lower:]’”表示将所有的大写字母替换为小写字母,因此得到“linux”。
“sed -e ‘s/(cygwin).*/cygwin/’”的输入,用于将cygwin.*替换为 cygwin。


设置目标架构、交叉编译器和配置文件

编 译 uboot 的 时 候 需 要 设 置 目 标 板 架 构 和 交 叉 编 译 器 。

“ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置 ARCH 和 CROSS_COMPILE:

244 # set default to nothing for native builds
245 ifeq ($(HOSTARCH),$(ARCH))
246 	CROSS_COMPILE ?=
247 endif
248
249 KCONFIG_CONFIG ?= .config
250 export KCONFIG_CONFIG

第 245 行判断 HOSTARCH 和 ARCH 这两个变量是否相等,主机架构(变量 HOSTARCH)是x86_64,而我们编译的是 ARM 版本 uboot,肯定不相等,所以 CROS_COMPILE= arm-linuxgnueabihf- 。

每次编译 uboot 的时候都要在 make 命令后面设置ARCH 和 CROS_COMPILE,使用起来很麻烦,可以直接修改顶层 Makefile,在里面加入 ARCH和 CROSS_COMPILE 的定义,这就是之前我们遇到过的问题,上一篇笔记编译uboot编译报错的原因就是总找不到交叉编译器


config 默认是没有的,需要使用命令“make xxx_defconfig”对 uboot 进行配置,配置完成以后就会在 uboot 根目录下生成.config。
默认情况下.config 和xxx_defconfig 内容是一样的,因为.config 就是从 xxx_defconfig 复制过来的。如果后续自行调整了 uboot 的一些配置参数,那么这些新的配置参数就添加到了.config 中,而不是 xxx_defconfig。相当于 xxx_defconfig 只是一些初始配置,而.config 里面的才是实时有效的配置。


调用 scripts/Kbuild.include

主 Makefile 会调用文件 scripts/Kbuild.include 这个文件,此文件里面定义了很多变量,后边边用边学。

交叉编译工具变量设置

顶层 Makefile 中相关代码如下:

333 AS = $(CROSS_COMPILE)as

335 ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)

336 	LD = $(CROSS_COMPILE)ld.bfd
337 else
338 	LD = $(CROSS_COMPILE)ld
339 endif
340 CC = $(CROSS_COMPILE)gcc
341 CPP = $(CC) -E
342 AR = $(CROSS_COMPILE)ar
343 NM = $(CROSS_COMPILE)nm
344 LDR = $(CROSS_COMPILE)ldr
345 STRIP = $(CROSS_COMPILE)strip
346 OBJCOPY = $(CROSS_COMPILE)objcopy
347 OBJDUMP = $(CROSS_COMPILE)objdump

这些命令应该很熟悉,在进行裸机开发时,手写Makefile就是为了省事,设置了这些交叉编译工具变量如LD、CC、OBJDUMP、OBJCOPY等。

导出其他变量

在顶层 Makefile 会导出很多变量,重点来看一下下面这几个变量:

ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR

make xxx_defconfig 过程

太多了,目前消受不了,等未知时间后再学习。

make命令流程

在这里插入图片描述
重点是“make xxx_defconfig”和“make”这两个命令的执行流程:
(1)、make xxx_defconfig: 用于配置 uboot,这个命令最主要的目的就是生成.config 文件。
(2)、make:用于编译 uboot,这个命令的主要工作就是生成二进制的 u-boot.bin 文件和其他的一些与 uboot 有关的文件,比如 u-boot.imx 等等。

总结:重点是使用 uboot,而不是 uboot 的研究者,我们要做的是缕清 uboot 的流程。秉承一开始学习stm32时候的原则,能看懂是什么功能,会用就行,太多了,要被劝退了!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值