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 的转换。