imx6ull:顶层Makefile分析

Linux 的顶层 Makefile 和 uboot 的顶层 Makefile 非常相似,因为 uboot 参考了 Linux,前 602行几乎一样,所以前面部分我们大致看一下就行了。

版本号

MAKEFLAGS变量

MAKEFLAGS += -rR --include-dir=$(CURDIR)

命令输出

  • Linux 编译的时候也可以通过“V=1”来输出完整的命令,这个和 uboot 一样

实现源码

69 ifeq ("$(origin V)", "command line")
70 KBUILD_VERBOSE = $(V)
71 endif
72 ifndef KBUILD_VERBOSE
73 KBUILD_VERBOSE = 0
74 endif
75
76 ifeq ($(KBUILD_VERBOSE),1)
77 quiet =
78 Q =
79 else
80 quiet=quiet_
81 Q = @
82 endif

静默输出

  • Linux 编译的时候使用“make -s”就可实现静默编译,编译的时候就不会打印任何的信息,同 uboot 一样

实现源码

87 ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
88 ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
89 quiet=silent_
90 endif
91 else # make-3.8x
92 ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
93 quiet=silent_
94 endif
95 endif
96
97 export quiet Q KBUILD_VERBOSE

设置编译结果输出目录

  • Linux 编译的时候使用“O=xxx”即可将编译产生的过程文件输出到指定的目录中

实现源码

116 ifeq ($(KBUILD_SRC),)
117
118 # OK, Make called in directory where kernel src resides
119 # Do we want to locate output files in a separate directory?
120 ifeq ("$(origin O)", "command line")
121 KBUILD_OUTPUT := $(O)
122 endif

代码检查

  • Linux 也支持代码检查,使用命令“make C=1”使能代码检查,检查那些需要重新编译的文件。“make C=2”用于检查所有的源码文件

实现源码

172 ifeq ("$(origin C)", "command line")
173 KBUILD_CHECKSRC = $(C)
174 endif
175 ifndef KBUILD_CHECKSRC
176 KBUILD_CHECKSRC = 0
177 endif

模块编译

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

实现源码

179 # Use make M=dir to specify directory of external module to build
180 # Old syntax make ... SUBDIRS=$PWD is still supported
181 # Setting the environment variable KBUILD_EXTMOD take precedence
182 ifdef SUBDIRS
183 KBUILD_EXTMOD ?= $(SUBDIRS)
184 endif
185
186 ifeq ("$(origin M)", "command line")
187 KBUILD_EXTMOD := $(M)
188 endif
189
190 # If building an external module we do not care about the all: rule
191 # but instead _all depend on modules
192 PHONY += all
193 ifeq ($(KBUILD_EXTMOD),)
194 _all: all
195 else
196 _all: modules
197 endif
198
199 ifeq ($(KBUILD_SRC),)
200 # building in the source tree
201 srctree := .
202 else
203 ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
204 # building in a subdirectory of the source tree
205 srctree := ..
206 else
207 srctree := $(KBUILD_SRC)
208 endif
209 endif
210 objtree := .
211 src := $(srctree)
212 obj := $(objtree)
213
214 VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
215
216 export srctree objtree VPATH

外部模块编译过程和 uboot 也一样,最终导出 srctree、objtree 和 VPATH 这三个变量的值,其中 srctree=.,也就是当前目录,objtree 同样为“.”

设置目标架构和交叉编译器

  • 同uboot一样,Linux编译的时候需要设置目标板架构ARCH和交叉编译器CROSS_COMPILE

实现源码

252 ARCH ?= $(SUBARCH)
253 CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)

调用scripts/Kbuild.include文件

  • 同 uboot 一样,Linux 顶层 Makefile 也会调用文件 scripts/Kbuild.include

实现源码

348 # We need some generic definitions (do not try to remake the file).
349 scripts/Kbuild.include: ;
350 include scripts/Kbuild.include

交叉编译工具变量设置

  • LA、LD、CC 等这些都是交叉编译器所使用的工具

实现源码

353 AS = $(CROSS_COMPILE)as
354 LD = $(CROSS_COMPILE)ld
355 CC = $(CROSS_COMPILE)gcc
356 CPP = $(CC) -E
357 AR = $(CROSS_COMPILE)ar
358 NM = $(CROSS_COMPILE)nm
359 STRIP = $(CROSS_COMPILE)strip
360 OBJCOPY = $(CROSS_COMPILE)objcopy
361 OBJDUMP = $(CROSS_COMPILE)objdump

头文件路径变量

  • 顶层 Makefile 定义了两个变量保存头文件路径:USERINCLUDE 和 LINUXINCLUDE

实现源码

381 USERINCLUDE := \
382 -I$(srctree)/arch/$(hdr-arch)/include/uapi \
383 -Iarch/$(hdr-arch)/include/generated/uapi \
384 -I$(srctree)/include/uapi \
385 -Iinclude/generated/uapi \
386 -include $(srctree)/include/linux/kconfig.h
387
388 # Use LINUXINCLUDE when you must reference the include/ directory.
389 # Needed to be compatible with the O= option
390 LINUXINCLUDE := \
391 -I$(srctree)/arch/$(hdr-arch)/include \
392 -Iarch/$(hdr-arch)/include/generated/uapi \
393 -Iarch/$(hdr-arch)/include/generated \
394 $(if $(KBUILD_SRC), -I$(srctree)/include) \
395 -Iinclude \
396 $(USERINCLUDE)
  • 第 381~386 行是 USERINCLUDE 是 UAPI 相关的头文件路径
  • 第 390~396 行是LINUXINCLUDE 是 Linux 内核源码的头文件路径
  • 其中srctree=.,hdr-arch=arm,KBUILD_SRC 为空,因此,将 USERINCLUDE 和 LINUXINCLUDE 展开以后为:
USERINCLUDE := \
-I./arch/arm/include/uapi \
-Iarch/arm/include/generated/uapi \
-I./include/uapi \
-Iinclude/generated/uapi \
-include ./include/linux/kconfig.h

LINUXINCLUDE := \
-I./arch/arm/include \
-Iarch/arm/include/generated/uapi \
-Iarch/arm/include/generated \
-Iinclude \
-I./arch/arm/include/uapi \
-Iarch/arm/include/generated/uapi \
-I./include/uapi \
-Iinclude/generated/uapi \
-include ./include/linux/kconfig.h

导出变量

  • 顶层 Makefile 会导出很多变量给子 Makefile 使用

实现源码

417 export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
418 export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC
419 export CPP AR NM STRIP OBJCOPY OBJDUMP
420 export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE
421 export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
422
423 export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
424 export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KASAN
425 export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
426 export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
427 export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
428 export KBUILD_ARFLAGS

make xxx_defconfig  过程

 

make过程

 

make zImage  过程

vmlinux 、Image ,zImage 、uImage 的区别

vmlinux 是 ELF 格式的文件,但是在实际中我们不会使用 vmlinux,而是使用 zImage 或 uImage 这样的 Linux 内核镜像文件

vmlinux 是编译出来的最原始的内核文件,包含符号表等信息并且是未压缩的。vmlinux 差不多有 16MB。

Image 是 Linux 内核镜像文件,但是 Image 仅包含可执行的二进制数据。Image 就是使用 objcopy 取消掉 vmlinux 中的一些其他信息,比如符号表什么的。但是 Image 是没有压缩过的,Image 保存在 arch/arm/boot 目录下。其大小大概在 12MB 左右。

zImage 是经过 gzip 压缩后的 Image,经过压缩以后其大小大概在 6MB 左右

uImage 是老版本 uboot 专用的镜像文件,uImag 是在 zImage 前面加了一个长度为 64字节的“头”,这个头信息描述了该镜像文件的类型、加载位置、生成时间、大小等信息。但是新的 uboot 已经支持了 zImage 启动!所以已经很少用到 uImage 了,除非你用的很古老的 uboot。

使用“make”、“make all”、“make zImage”这些命令就可以编译出 zImage 镜像

310 BOOT_TARGETS = zImage Image xipImage bootpImage uImage
......
315 $(BOOT_TARGETS): vmlinux
316 $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
  • 第 310 行,变量 BOOT_TARGETS 包含 zImage,Image,xipImage 等镜像文件
  • 第 315 行,BOOT_TARGETS 依赖 vmlinux,因此如果使用“make zImage”编译的 Linux 内核的话,首先肯定要先编译出 vmlinux
  • 第 316 行,具体的命令,比如要编译 zImage,那么命令展开以后如下所示:
    @ make -f ./scripts/Makefile.build obj=arch/arm/boot MACHINE=arch/arm/boot/zImage
    看来又是使用 scripts/Makefile.build 文件来完成 vmliux 到 zImage 的转换。
上一篇:前缀树


下一篇:Chapter 2- 微服务解决方案之 Spring Cloud