首页 > 系统相关 >Linux顶层 Makefile 详解

Linux顶层 Makefile 详解

时间:2024-05-30 09:33:19浏览次数:17  
标签:顶层 arch scripts Makefile vmlinux Linux true arm

makefile分析:

分析linux makefile文件时候我们需要在vscode下设置屏蔽下不用的文件夹。具体来说可以先建立一个.vscode文件夹和setting.json。具体内容为:

{"search.exclude": {
    "**/node_modules": true,
    "**/bower_components": true,
    "**/*.o": true,
    "**/*.su": true,
    "**/*.cmd": true,
    "Documentation": true,
    /* 屏蔽不用的架构相关的文件 */
    "arch/alpha": true,
    "arch/arc": true,
    "arch/arm64": true,
    "arch/avr32": true,
    "arch/[b-z]*": true,
    "arch/arm/plat*": true,
    "arch/arm/mach-[a-h]*": true,
    "arch/arm/mach-[n-z]*": true,
    "arch/arm/mach-i[n-z]*": true,
    "arch/arm/mach-m[e-v]*": true,
    "arch/arm/mach-k*": true,
    "arch/arm/mach-l*": true,
    /* 屏蔽排除不用的配置文件 */
    "arch/arm/configs/[a-h]*": true,
    "arch/arm/configs/[j-z]*": true,
    "arch/arm/configs/imo*": true,
    "arch/arm/configs/in*": true,
    "arch/arm/configs/io*": true,
    "arch/arm/configs/ix*": true,
    /* 屏蔽掉不用的 DTB 文件 */
    "arch/arm/boot/dts/[a-h]*": true,
    "arch/arm/boot/dts/[k-z]*": true,
    "arch/arm/boot/dts/in*": true,
    "arch/arm/boot/dts/imx1*": true,
    "arch/arm/boot/dts/imx7*": true,
    "arch/arm/boot/dts/imx2*": true,
    "arch/arm/boot/dts/imx3*": true,
    "arch/arm/boot/dts/imx5*": true,
    "arch/arm/boot/dts/imx6d*": true,
    "arch/arm/boot/dts/imx6q*": true,
    "arch/arm/boot/dts/imx6s*": true,
    "arch/arm/boot/dts/imx6ul-*": true,
    "arch/arm/boot/dts/imx6ull-9x9*": true,
    "arch/arm/boot/dts/imx6ull-14x14-ddr*": true
},
"files.exclude": {
    "**/.git": true,
    "**/.svn": true,
    "**/.hg": true,
    "**/CVS": true,
    "**/.DS_Store": true,
    "**/*.o": true,
    "**/*.su": true,
    "**/*.cmd": true,
    "Documentation": true,
    /* 屏蔽不用的架构相关的文件 */
    "arch/alpha": true,
    "arch/arc": true,
    "arch/arm64": true,
    "arch/avr32": true,
    "arch/[b-z]*": true,
    "arch/arm/plat*": true,
    "arch/arm/mach-[a-h]*": true,
    "arch/arm/mach-[n-z]*": true,
    "arch/arm/mach-i[n-z]*": true,
    "arch/arm/mach-m[e-v]*": true,
    "arch/arm/mach-k*": true,
    "arch/arm/mach-l*": true,
    /* 屏蔽排除不用的配置文件 */
    "arch/arm/configs/[a-h]*": true,
    "arch/arm/configs/[j-z]*": true,
    "arch/arm/configs/imo*": true,
    "arch/arm/configs/in*": true,
    "arch/arm/configs/io*": true,
    "arch/arm/configs/ix*": true,
    /* 屏蔽掉不用的 DTB 文件 */
    "arch/arm/boot/dts/[a-h]*": true,
    "arch/arm/boot/dts/[k-z]*": true,
    "arch/arm/boot/dts/in*": true,
    "arch/arm/boot/dts/imx1*": true,
    "arch/arm/boot/dts/imx7*": true,
    "arch/arm/boot/dts/imx2*": true,
    "arch/arm/boot/dts/imx3*": true,
    "arch/arm/boot/dts/imx5*": true,
    "arch/arm/boot/dts/imx6d*": true,
    "arch/arm/boot/dts/imx6q*": true,
    "arch/arm/boot/dts/imx6s*": true,
    "arch/arm/boot/dts/imx6ul-*": true,
    "arch/arm/boot/dts/imx6ull-9x9*": true,
    "arch/arm/boot/dts/imx6ull-14x14-ddr*": true
}
}

1、版本号

顶层 Makefile 一开始就是 Linux 内核的版本号,如下所示:

VERSION = 4
PATCHLEVEL = 1
SUBLEVEL = 15
EXTRAVERSION =
NAME = Series 4800

Linux 内核版本号为 4.1.15。

2、 MAKEFLAGS 变量

MAKEFLAGS 变量设置如下所示:
在 Makefile 中使用 MAKEFLAGS 变量是一种向 make 命令传递额外选项的方式。这些选项可以影响 make 的行为。下面是对您提供的 MAKEFLAGS 设置的详细解释:

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

    • MAKEFLAGS 是一个环境变量,用于指定全局的 make 选项。这些选项会被自动应用于当前 Makefile 中的所有 make 调用,包括从当前 Makefile 调用的任何子 make 进程。
  2. +=

    • 这个操作符用于向变量追加值,而不是覆盖原有的值。这意味着新指定的选项会添加到 MAKEFLAGS 变量现有内容之后。
  3. -rR

    • -r-R 是两个相关的选项,通常一起使用,它们的作用是:
      • -r--no-builtin-rules):禁用所有内置的隐式规则。这可以减少 make 的处理时间,因为它不需要考虑任何内置规则。
      • -R--no-builtin-variables):禁用所有内置的变量设置(例如 CC, CFLAGS 等)。这确保了环境中的变量或 Makefile 中显式设置的变量不会被默认值覆盖。
  4. --include-dir=$(CURDIR)

    • --include-dir 选项用于指定一个额外的目录,make 将在此目录中查找被包含(include)的 Makefile 文件。
    • $(CURDIR) 是一个自动变量,代表当前正在执行 make 的目录的绝对路径。这意味着 make 将会在当前目录中查找包含的 Makefile 文件。

3、命令输出

Linux 编译的时候也可以通过“V=1”来输出完整的命令,这个和 uboot 一样,相关代码如下所示:

ifeq ("$(origin V)", "command line")
  KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
  KBUILD_VERBOSE = 0
endif

ifeq ($(KBUILD_VERBOSE),1)
  quiet =
  Q =
else
  quiet=quiet_
  Q = @
endif

4、静默输出

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

示例代码 35.5.4 顶层 Makefile 代码段
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

5、设置编译结果输出目录

Linux 编译的时候使用“O=xxx”即可将编译产生的过程文件输出到指定的目录中,相关代码如下:

示例代码 35.5.5 顶层 Makefile 代码段
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

6、代码检查

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

示例代码 35.5.6 顶层 Makefile 代码段
172 ifeq ("$(origin C)", "command line")
173 KBUILD_CHECKSRC = $(C)
174 endif
175 ifndef KBUILD_CHECKSRC
176 KBUILD_CHECKSRC = 0
177 endif

7、模块编译

Linux 允许单独编译某个模块,使用命令“make M=dir”即可,旧语法“make SUBDIRS=dir”也是支持的。顶层 Makefile 中的代码如下:

示例代码 35.5.7 顶层 Makefile 代码段
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

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

同 uboot 一样, Linux 编译的时候需要设置目标板架构ARCH 和交叉编译器 CROSS_COMPILE,在顶层 Makefile 中代码如下:

示例代码 35.5.8 顶层 Makefile 代码段
252 ARCH ?= $(SUBARCH)
253 CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)

为了方便,一般直接修改顶层 Makefile 中的 ARCH 和 CROSS_COMPILE,直接将其设置为对应的架构和编译器,比如本教程将 ARCH 设置为为 arm, CROSS_COMPILE 设置为 armlinux-gnueabihf-,如下所示:
示例代码 35.5.9 顶层 Makefile 代码段

252 ARCH ?= arm
253 CROSS_COMPILE ?= arm-linux-gnueabihf-

9、调用 scripts/Kbuild.include 文件

同 uboot 一样, Linux 顶层 Makefile 也会调用文件 scripts/Kbuild.include,顶层 Makefile 相应代码如下:

示例代码 35.5.10 顶层 Makefile 代码段
348 # We need some generic definitions (do not try to remake the file).
349 scripts/Kbuild.include: ;
350 include scripts/Kbuild.include

10、交叉编译工具变量设置

顶层 Makefile 中其他和交叉编译器有关的变量设置如下:

示例代码 35.5.11 顶层 Makefile 代码段
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

11、头文件路径变量

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

12、导出变量

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

make xxx_defconfig 过程

第一次编译 Linux 之前都要使用“make xxx_defconfig”先配置 Linux 内核,在顶层 Makefile中有“%config”这个目标,如下所示:
在这里插入图片描述

示例代码 35.5.1.1 顶层 Makefile 代码段
490 config-targets := 0
491 mixed-targets := 0
492 dot-config := 1
493
494 ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
495 ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
496 dot-config := 0
497 endif
498 endif
499
500 ifeq ($(KBUILD_EXTMOD),)
501 ifneq ($(filter config %config,$(MAKECMDGOALS)),)
502 config-targets := 1
503 ifneq ($(words $(MAKECMDGOALS)),1)
504 mixed-targets := 1
505 endif
506 endif
507 endif
508
509 ifeq ($(mixed-targets),1)
510 # =================================================================
511 # We're called with mixed targets (*config and build targets).
512 # Handle them one by one.
513
514 PHONY += $(MAKECMDGOALS) __build_one_by_one
515
516 $(filter-out __build_one_by_one, $(MAKECMDGOALS)):
__build_one_by_one
517 @:
518
519 __build_one_by_one:
520 $(Q)set -e; \
521 for i in $(MAKECMDGOALS); do \
522 $(MAKE) -f $(srctree)/Makefile $$i; \
523 done
524
525 else
526 ifeq ($(config-targets),1)
527 # ================================================================
528 # *config targets only - make sure prerequisites are updated, and
529 # descend in scripts/kconfig to make the *config target
530
531 # Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.
532 # KBUILD_DEFCONFIG may point out an alternative default
533 # configuration used for 'make defconfig'
534 include arch/$(SRCARCH)/Makefile
535 export KBUILD_DEFCONFIG KBUILD_KCONFIG
536
537 config: scripts_basic outputmakefile FORCE
538 $(Q)$(MAKE) $(build)=scripts/kconfig $@
539
540 %config: scripts_basic outputmakefile FORCE
541 $(Q)$(MAKE) $(build)=scripts/kconfig $@
542
543 else
......
563 endif # KBUILD_EXTMOD

第 490~507 行和 uboot 一样,都是设置定义变量 config-targets、 mixed-targets 和 dot-config
的值,最终这三个变量的值为:
config-targets= 1
mixed-targets= 0
dot-config= 1
因为 config-targets=1,因此第 534 行~541 行成立。第 534 行引用 arch/arm/Makefile 这个文件,这个文件很重要,因为 zImage、 uImage 等这些文件就是由 arch/arm/Makefile 来生成的。
第 535 行导出变量 KBUILD_DEFCONFIG KBUILD_KCONFIG。
第 537 行,没有目标与之匹配,因此不执行。
第 540 行,“make xxx_defconfig”与目标“%config”匹配,因此执行。“%config”依赖scripts_basic、outputmakefile 和 FORCE,“%config”真正有意义的依赖就只有 scripts_basic,scripts_basic 的规则如下:

示例代码 35.5.1.2 顶层 Makefile 代码段
448 scripts_basic:
449 $(Q)$(MAKE) $(build)=scripts/basic
450 $(Q)rm -f .tmp_quiet_recordmcount

build 定义在文件 scripts/Kbuild.include 中,值为 build := -f $(srctree)/scripts/Makefile.build obj,因此将示例代码 35.5.1.2 展开就是:

scripts_basic:
@make -f ./scripts/Makefile.build obj=scripts/basic //也可以没有@,视配置而定
@rm -f . tmp_quiet_recordmcount //也可以没有@

接着回到示例代码 35.5.1.1 的目标“%config”处,内容如下:

%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@

将命令展开就是:

@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig

Makefile.build 脚本分析

从上一小节可知,“ make xxx_defconfig“配置 Linux 的时候如下两行命令会执行脚本

scripts/Makefile.build:
@make -f ./scripts/Makefile.build obj=scripts/basic
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig

1、 scripts_basic 目标对应的命令
scripts_basic 目标对应的命令为: @make -f ./scripts/Makefile.build obj=scripts/basic。打开文件 scripts/Makefile.build,有如下代码:

示例代码 35.5.2.1 Makefile.build 代码段
41 # The filename Kbuild has precedence over Makefile
42 kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
43 kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuilddir)/Kbuild,$(kbuild-dir)/Makefile)
44 include $(kbuild-file)

将 kbuild-dir 展开后为:

kbuild-dir=./scripts/basic

将 kbuild-file 展开后为:

kbuild-file= ./scripts/basic/Makefile

最后将44行展开,即:

include ./scripts/basic/Makefile

继续分析 scripts/Makefile.build,如下代码:

示例代码 35.5.2.2 Makefile.build 代码段
94 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target)
$(extra-y)) \
95 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
96 $(subdir-ym) $(always)
97 @:

__build 是默认目标,因为命令“@make -f ./scripts/Makefile.build obj=scripts/basic”没有指定目标,所以会使用到默认目标__build。在顶层 Makefile 中, KBUILD_BUILTIN 为 1,KBUILD_MODULES 为空,因此展开后目标__build 为:

__build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)
@:

可以看出目标__build 有 5 个依赖: builtin-target、 lib-target、 extra-y、 subdir-ym 和 always。这 5 个依赖的具体内容如下:

builtin-target =
lib-target =
extra-y =
subdir-ym =
always = scripts/basic/fixdep scripts/basic/bin2c

只有 always 有效,因此__build 最终为:__build: scripts/basic/fixdep scripts/basic/bin2c
@:__build 依赖于 scripts/basic/fixdep 和 scripts/basic/bin2c,所以要先将 scripts/basic/fixdep 和
scripts/basic/bin2c.c 这两个文件编译成 fixdep 和 bin2c。
综上所述, scripts_basic 目标的作用就是编译出 scripts/basic/fixdep 和 scripts/basic/bin2c 这两个软件。
2、 %config 目标对应的命令
%config 目 标 对 应 的 命 令 为 : @make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig,此命令会使用到的各个变量值如下:

src= scripts/kconfig
kbuild-dir = ./scripts/kconfig
kbuild-file = ./scripts/kconfig/Makefile
include ./scripts/kconfig/Makefile

可以看出, Makefile.build 会读取 scripts/kconfig/Makefile 中的内容,此文件有如下所示内容:

示例代码 35.5.2.3 scripts/kconfig/Makefile 代码段
113 %_defconfig: $(obj)/conf
114 $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@
$(Kconfig)

目标%_defconfig 与 xxx_defconfig 匹配,所以会执行这条规则,将其展开就是:

%_defconfig: scripts/kconfig/conf
@ scripts/kconfig/conf --defconfig=arch/arm/configs/%_defconfig Kconfig

%_defconfig依赖scripts/kconfig/conf,所以会编译scripts/kconfig/conf.c生成conf 这个软件。此软件就会将%_defconfig 中的配置输出到.config 文件中,最终生成 Linux kernel 根目录下的.config 文件。

make 过程

使用命令“make xxx_defconfig”配置好 Linux 内核以后就可以使用“make”或者“make all”命令进行编译。顶层 Makefile 有如下代码:

示例代码 35.5.3.1 顶层 Makefile 代码段
125 PHONY := _all
126 _all:
......
192 PHONY += all
193 ifeq ($(KBUILD_EXTMOD),)
194 _all: all
195 else
196 _all: modules
197 endif
......
608 all: vmlinux

第 126 行, _all 是默认目标,如果使用命令“make”编译 Linux 的话此目标就会被匹配。
第 193 行,如果 KBUILD_EXTMOD 为空的话 194 行的代码成立。
第 194 行,默认目标_all 依赖 all。
第 608 行,目标 all 依赖 vmlinux,所以接下来的重点就是 vmlinux!
顶层 Makefile 中有如下代码:

示例代码 35.5.3.2 顶层 Makefile 代码段
904 # Externally visible symbols (used by link-vmlinux.sh)
905 export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
906 export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y)
$(net-y)
907 export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
908 export LDFLAGS_vmlinux
909 # used by scripts/pacmage/Makefile
910 export KBUILD_ALLDIRS := $(sort $(filter-out arch/%,$(vmlinuxalldirs)) arch Documentation include samples scripts tools virt)
911
912 vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT)
$(KBUILD_VMLINUX_MAIN)
913
914 # Final link of vmlinux
915 cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS)
$(LDFLAGS_vmlinux)
916 quiet_cmd_link-vmlinux = LINK $@
917
918 # Include targets which we want to
919 # execute if the rest of the kernel build went well.
920 vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
921 ifdef CONFIG_HEADERS_CHECK
922 $(Q)$(MAKE) -f $(srctree)/Makefile headers_check
923 endif
924 ifdef CONFIG_SAMPLES
925 $(Q)$(MAKE) $(build)=samples
926 endif
927 ifdef CONFIG_BUILD_DOCSRC
928 $(Q)$(MAKE) $(build)=Documentation
929 endif
930 ifdef CONFIG_GDB_SCRIPTS
931 $(Q)ln -fsn `cd $(srctree) && /bin/pwd`/scripts/gdb/vmlinuxgdb.py
932 endif
933 +$(call if_changed,link-vmlinux)

从第 920 行可以看出目标 vmlinux 依赖 scripts/link-vmlinux.sh $(vmlinux-deps) FORCE。第
912 行定义了 vmlinux-deps,值为:

vmlinux-deps= $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)

第 905 行, KBUILD_VMLINUX_INIT= $(head-y) $(init-y)
第 906 行, KBUILD_VMLINUX_MAIN = $(core-y) $(libs-y) $(drivers-y) $(net-y)
第 907 行, KBUILD_LDS= arch/$(SRCARCH)/kernel/vmlinux.lds,其中 SRCARCH=arm,因此KBUILD_LDS= arch/arm/kernel/vmlinux.lds
综上所述, vmlinux 的依赖为: scripts/link-vmlinux.sh、 $(head-y) 、 $(init-y)、 $(core-y) 、$(libs-y) 、 $(drivers-y) 、 $(net-y)、 arch/arm/kernel/vmlinux.lds 和 FORCE

第 933 行的命令用于链接生成 vmlinux。重点来看一下$(head-y) 、 $(init-y)、 $(core-y) 、 $(libs-y) 、 $(drivers-y) 和$(net-y)这六个变量的值。
1、 head-y
head-y 定义在文件 arch/arm/Makefile 中,内容如下:

示例代码 35.5.3.3 arch/arm/Makefile 代码段
135 head-y := arch/arm/kernel/head$(MMUEXT).o

当不使能 MMU 的话 MMUEXT=-nommu,如果使能 MMU 的话为空,因此 head-y 最终的
值为:
head-y = arch/arm/kernel/head.o
2、 init-y、 drivers-y 和 net-y
在顶层 Makefile 中有如下代码:

示例代码 35.5.3.4 顶层 Makefile 代码段
558 init-y := init/
559 drivers-y := drivers/ sound/ firmware/
560 net-y := net/
......
896 init-y := $(patsubst %/, %/built-in.o, $(init-y))
898 drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
899 net-y := $(patsubst %/, %/built-in.o, $(net-y))

从示例代码 35.5.3.4 可知, init-y、 libs-y、 drivers-y 和 net-y 最终的值为:

init-y = init/built-in.o
drivers-y = drivers/built-in.o sound/built-in.o firmware/built-in.o
net-y = net/built-in.o

3、 libs-y
libs-y 基本和 init-y 一样,在顶层 Makefile 中存在如下代码:

示例代码 35.5.3.5 顶层 Makefile 代码段
561 libs-y := lib/
......
900 libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
901 libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
902 libs-y := $(libs-y1) $(libs-y2)

根据示例代码 35.5.3.5 可知, libs-y 应该等于“lib.a built-in.o”,这个只正确了一部分!因为在 arch/arm/Makefile 中会向 libs-y 中追加一些值,代码如下:

示例代码 35.5.3.6 arch/arm/Makefile 代码段
286 libs-y := arch/arm/lib/ $(libs-y)

arch/arm/Makefile 将 libs-y 的值改为了: arch/arm/lib $(libs-y),展开以后为:libs-y = arch/arm/lib lib/
因此根据示例代码 35.5.3.5 的第 900~902 行可知, libs-y 最终应该为:libs-y = arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o

4、 core-y
core-y 和 init-y 也一样,在顶层 Makefile 中有如下代码:

示例代码 35.5.3.7 顶层 Makefile 代码段
532 core-y := usr/
......
887 core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
但是在 arch/arm/Makefile 中会对 core-y 进行追加,代码如下:
示例代码 35.5.3.8 arch/arm/Makefile 代码段
269 core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/
270 core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ)
271 core-$(CONFIG_VFP) += arch/arm/vfp/
272 core-$(CONFIG_XEN) += arch/arm/xen/
273 core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/
274 core-$(CONFIG_VDSO) += arch/arm/vdso/
275
276 # If we have a machine-specific directory, then include it in the
build.
277 core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
278 core-y += arch/arm/probes/
279 core-y += arch/arm/net/
280 core-y += arch/arm/crypto/
281 core-y += arch/arm/firmware/
282 core-y += $(machdirs) $(platdirs)

第 269~274 行根据不同的配置向 core-y 追加不同的值,比如使能 VFP 的话就会在.config中有 CONFIG_VFP=y 这一行,那么 core-y 就会追加“arch/arm/vfp/”。
第 277~282 行就是对 core-y 直接追加的值。在顶层 Makefile 中有如下一行:

示例代码 35.5.3.9 顶层 Makefile 代码段
897 core-y := $(patsubst %/, %/built-in.o, $(core-y))

经过上述代码的转换,最终 core-y 的值为:

core-y = usr/built-in.o arch/arm/vfp/built-in.o \
arch/arm/vdso/built-in.o arch/arm/kernel/built-in.o \
arch/arm/mm/built-in.o arch/arm/common/built-in.o \
arch/arm/probes/built-in.o arch/arm/net/built-in.o \
arch/arm/crypto/built-in.o arch/arm/firmware/built-in.o \
arch/arm/mach-imx/built-in.o kernel/built-in.o\
mm/built-in.o fs/built-in.o \
ipc/built-in.o security/built-in.o \
crypto/built-in.o block/built-in.o

关于 head-y 、 init-y、 core-y 、 libs-y 、 drivers-y 和 net-y 这 6 个变量就讲解到这里。这些变量都是一些 built-in.o 或.a 等文件,这个和 uboot 一样,都是将相应目录中的源码文件进行编译,然后在各自目录下生成 built-in.o 文件,有些生成了.a 库文件。最终将这些 built-in.o 和.a 文件进行链接即可形成 ELF 格式的可执行文件,也就是 vmlinux!但是链接是需要链接脚本的,vmlinux 的依赖 arch/arm/kernel/vmlinux.lds 就是整个 Linux 的链接脚本。
示例代码 35.5.3.2 第 933 行的命令“ +$(call if_changed,link-vmlinux)”表示将$(callif_changed,link-vmlinux)的结果作为最终生成 vmlinux 的命令,前面的“+”表示该命令结果不可忽略。 $(call if_changed,link-vmlinux)是调用函数 if_changed, link-vmlinux 是函数 if_changed 的参数,函数 if_changed 定义在文件 scripts/Kbuild.include 中,如下所示:

示例代码 35.5.3.10 scripts/Kbuild.include 代码段
247 if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
248 @set -e; \
249 $(echo-cmd) $(cmd_$(1)); \
250 printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)

any-prereq 用于检查依赖文件是否有变化,如果依赖文件有变化那么 any-prereq 就不为空,否则就为空。 arg-check 用于检查参数是否有变化,如果没有变化那么 arg-check 就为空。
第 248 行,“@set -e”告诉 bash,如果任何语句的执行结果不为 true(也就是执行出错)的话就直接退出。
第 249 行, $(echo-cmd)用于打印命令执行过程,比如在链接 vmlinux 的时候就会输出“LINK vmlinux”$(cmd_$(1))中的\((1)表示参数,也就是 link-vmlinux,因此(cmd_\)(1))表示执行 cmd_link-vmlinux 的内容。 cmd_link-vmlinux 在顶层 Makefile 中有如下所示定义:

示例代码 35.5.3.11 顶层 Makefile 代码段
914 # Final link of vmlinux
915 cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS)
$(LDFLAGS_vmlinux)
916 quiet_cmd_link-vmlinux = LINK $@

第 915 行就是 cmd_link-vmlinux 的值,其中 CONFIG_SHELL=/bin/bash, $<表示目标 vmlinux的第一个依赖文件,根据示例代码 35.5.3.2 可知,这个文件为 scripts/link-vmlinux.sh。LD= arm-linux-gnueabihf-ld -EL, LDFLAGS 为空。 LDFLAGS_vmlinux 的值由顶层 Makefile 和arch/arm/Makefile 这两个文件共同决定,最终 LDFLAGS_vmlinux=-p --no-undefined -X --picveneer --build-id。因此 cmd_link-vmlinux 最终的值为:
cmd_link-vmlinux = /bin/bash scripts/link-vmlinux.sh arm-linux-gnueabihf-ld -EL -p --noundefined -X --pic-veneer --build-id cmd_link-vmlinux 会调用 scripts/link-vmlinux.sh 这个脚本来链接出 vmlinux!在 linkvmlinux.sh 中有如下所示代码:

示例代码 35.5.3.12 scripts/link-vmlinux.sh 代码段
51 vmlinux_link()
52 {
53 local lds="${objtree}/${KBUILD_LDS}"
54
55 if [ "${SRCARCH}" != "um" ]; then
56 ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} \
57 -T ${lds} ${KBUILD_VMLINUX_INIT} \
58 --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}
59 else
60 ${CC} ${CFLAGS_vmlinux} -o ${2} \
61 -Wl,-T,${lds} ${KBUILD_VMLINUX_INIT} \
62 -Wl,--start-group \
63 ${KBUILD_VMLINUX_MAIN} \
64 -Wl,--end-group \
65 -lutil ${1}
66 rm -f linux
67 fi
68 }
......
216 info LD vmlinux
217 vmlinux_link "${kallsymso}" vmlinux

vmliux_link 就是最终链接出 vmlinux 的函数,第 55 行判断 SRCARCH 是否等于“um”,如果不相等的话就执行 56~58 行的代码。因为 SRCARCH=arm,因此条件成立,执行 56~58 行的代 码 。 这 三 行 代 码 就 应 该 很 熟 悉 了 ! 就 是 普 通 的 链 接 操 作 , 连 接 脚 本 为lds= ./arch/arm/kernel/vmlinux.lds , 需 要 链 接 的文件 由 变 量 KBUILD_VMLINUX_INIT 和KBUILD_VMLINUX_MAIN 来决定,这两个变量在示例代码 35.5.3.2 中已经讲解过了.
第 217 行调用 vmlinux_link 函数来链接出 vmlinux。使用命令“make V=1”编译 Linux,会有如图 35.5.3.1 所示的编译信息:

make zImage 过程

1、 vmlinux、 Image, zImage、 uImage 的区别
前面几小节重点是讲 vmlinux 是如何编译出来的, vmlinux 是 ELF 格式的文件,但是在实际中我们不会使用 vmlinux,而是使用 zImage 或 uImage 这样的 Linux 内核镜像文件。那么vmlinux、 zImage、 uImage 他们之间有什么区别呢?
①、 vmlinux 是编译出来的最原始的内核文件,是未压缩的,比如正点原子提供的 Linux 源码编译出来的 vmlinux 差不多有 16MB,如图 35.5.5.1 所示:
图 35.5.5.1 vmlinux 信息
②、 Image 是 Linux 内核镜像文件,但是 Image 仅包含可执行的二进制数据。 Image 就是使
用 objcopy 取消掉 vmlinux 中的一些其他信息,比如符号表什么的。但是 Image 是没有压缩过
的, Image 保存在 arch/arm/boot 目录下,其大小大概在 12MB 左右如图 35.5.5.2 所示:
图 35.5.5.2 Image 镜像信息
相比 vmlinux 的 16MB, Image 缩小到了 12MB。
③、 zImage 是经过 gzip 压缩后的 Image,经过压缩以后其大小大概在 6MB 左右,如图
35.5.5.3 所示:
图 35.5.5.3 zImage 镜像信息
④、 uImage 是老版本 uboot 专用的镜像文件, uImag 是在 zImage 前面加了一个长度为 64
字节的“头”,这个头信息描述了该镜像文件的类型、加载位置、生成时间、大小等信息。但是
新的 uboot 已经支持了 zImage 启动!所以已经很少用到 uImage 了,除非你用的很古老的 uboot。
使用“ make”、“ make all”、“ make zImage”这些命令就可以编译出 zImage 镜像,在
arch/arm/Makefile 中有如下代码

标签:顶层,arch,scripts,Makefile,vmlinux,Linux,true,arm
From: https://www.cnblogs.com/bathwind/p/18221678

相关文章

  • 在Linux中,如何进行SSH服务配置?
    在Linux中配置SSH(SecureShell)服务,通常涉及安装SSH服务器软件(OpenSSH是最常见的选择)、修改配置文件以符合特定需求、管理密钥对以及确保服务的安全性。下面是详细的步骤指南:1.安装OpenSSH服务器大多数Linux发行版已经预装了OpenSSH服务器(sshd),但如果没有,可以通过包管理器安装。......
  • QShop商城-快速开始-Linux使用宝塔面板发布.Net6/7
    QShop商城-快速开始-Linux使用宝塔面板发布.Net6/7安装宝塔面板宝塔官方安装教程宝塔面板yuminstall-ywget&&wget-Oinstall.shhttp://download.bt.cn/install/install_6.0.sh&&shinstall.sh安装后将显示访问地址及账号密码,请妥善保存浏览器进入后将弹出如下......
  • 一本关于深入理解linux内核的书
    以下目录中所述关于深入理解linux内核:http://iteralink.top/resource/detail/7180573456050688000第一章走进Linux11.1GNU与Linux的成长 11.2Linux的开发模式和运作机制 21.3走进Linux内核 41.3.1Linux内核的特征 41.3.2Linux内核版本的变化 51.4......
  • Linux常考知识点——(二)
    #Linux第二章1.   Linux 惟独被授权的用户才可以使用系统命令。2.    Linux系统提供的命令需要在shell环境下运行。3.    使用bash命令时,应注意以下7点:(1)    命令名必须是小写英文字母。(2)    方括号里面的部份是可选的。(3)    选......
  • 【Linux进程篇】Linux进程管理——进程创建与终止
    W...Y的主页......
  • Linux之命令一
    [21:07:22root@Rocky85~]#uname-r查看内核版本[21:08:16root@Rocky85~]#yum-yinstallredhat-lsb-core[21:08:51root@Rocky85~]#lsb_release-a查看发行版本[21:08:58root@Rocky85~]#ls-lh-all/root列出/root目录下的所有文件,并按详细的格式显示......
  • Linux-防火墙
    1.防火墙种类硬件:整个企业入口三层路由:H3C华为Cisco(思科)防火墙:深信服,绿盟,奇安信.....Juniper软件:开源软件网站内部封ip封ipiptables写入到Linux内核中,以后服务docker工作在4层(大部分)firewalldC7nftalbesC8ufw(ubuntufirewall)Ubuntu云......
  • linux定时删除历史日志
    在Linux系统中,日志文件是记录系统、应用程序或服务的运行信息、错误消息和警告的重要工具。然而,随着时间的推移,这些日志文件会不断积累,占用大量的磁盘空间。如果不及时清理,可能会导致磁盘空间不足,从而影响系统的正常运行。因此,定时删除历史日志成为了Linux系统管理中不可或缺的......
  • linux 查看csv文件,按指定列聚合 排序
    在Linux中,你可以使用awk工具来查看CSV文件的内容,并按照指定的列进行聚合。awk是一种强大的文本处理工具,它可以处理文本文件中的数据,并根据条件执行相应的操作。以下是一个示例,假设你有一个名为data.csv的CSV文件,其中包含三列数据:姓名、年龄和性别,内容如下:姓名,年龄,性别张......
  • Linux入门第六章LVM与磁盘配额
    一.LVM逻辑卷管理    许多Linux操作系统的使用者在安装操作系统时都会遇到这样的困境:如何精确评估和分配各个硬盘分区的容量。如果当初估计不准确,一旦系统分区不够用就可能不得不备份、删除相关数据,甚至被迫重新规划分区并重装操作系统,以满足应用系统的需要。1.LV......