Linux 内核 Makefiles

本文档描述了 Linux 内核 Makefiles。

概述

Makefiles 分为五个部分

Makefile                    the top Makefile.
.config                     the kernel configuration file.
arch/$(SRCARCH)/Makefile    the arch Makefile.
scripts/Makefile.*          common rules etc. for all kbuild Makefiles.
kbuild Makefiles            exist in every subdirectory

顶层 Makefile 读取 .config 文件,该文件来自内核配置过程。

顶层 Makefile 负责构建两个主要产品:vmlinux(驻留内核镜像)和模块(任何模块文件)。它通过递归地进入内核源代码树的子目录来构建这些目标。

要访问的子目录列表取决于内核配置。顶层 Makefile 以文本方式包含一个名为 arch/$(SRCARCH)/Makefile 的架构 Makefile。架构 Makefile 向顶层 Makefile 提供架构特定的信息。

每个子目录都有一个 kbuild Makefile,它执行从上面传递下来的命令。kbuild Makefile 使用 .config 文件中的信息来构建各种文件列表,kbuild 使用这些文件列表来构建任何内置或模块化目标。

scripts/Makefile.* 包含用于基于 kbuild makefiles 构建内核的所有定义/规则等。

谁做什么

人们与内核 Makefiles 有四种不同的关系。

用户是构建内核的人。这些人键入诸如 make menuconfigmake 之类的命令。他们通常不读取或编辑任何内核 Makefiles(或任何其他源文件)。

普通开发者是从事设备驱动程序、文件系统和网络协议等特性工作的人。这些人需要维护他们正在工作的子系统的 kbuild Makefiles。为了有效地做到这一点,他们需要一些关于内核 Makefiles 的总体知识,以及关于 kbuild 公共接口的详细知识。

架构开发者是从事整个架构(如 sparc 或 x86)工作的人。架构开发者需要了解架构 Makefile 以及 kbuild Makefiles。

Kbuild 开发者是从事内核构建系统本身工作的人。这些人需要了解内核 Makefiles 的所有方面。

本文档面向普通开发者和架构开发者。

kbuild 文件

内核中的大多数 Makefiles 都是使用 kbuild 基础设施的 kbuild Makefiles。本章介绍 kbuild makefiles 中使用的语法。

kbuild 文件的首选名称是 Makefile,但可以使用 Kbuild,并且如果同时存在 MakefileKbuild 文件,则将使用 Kbuild 文件。

章节 目标定义 是一个快速介绍;后面的章节提供了更多细节,并附有实际示例。

目标定义

目标定义是 kbuild Makefile 的主要部分(核心)。这些行定义了要构建的文件、任何特殊的编译选项以及要递归进入的任何子目录。

最简单的 kbuild makefile 包含一行

示例

obj-y += foo.o

这告诉 kbuild 该目录中有一个名为 foo.o 的对象。foo.o 将从 foo.c 或 foo.S 构建。

如果 foo.o 应构建为模块,则使用变量 obj-m。因此,经常使用以下模式

示例

obj-$(CONFIG_FOO) += foo.o

$(CONFIG_FOO) 的值为 y(对于内置)或 m(对于模块)。如果 CONFIG_FOO 既不是 y 也不是 m,则该文件将不会被编译或链接。

内置对象目标 - obj-y

kbuild Makefile 在 $(obj-y) 列表中指定 vmlinux 的目标文件。这些列表取决于内核配置。

Kbuild 编译所有 $(obj-y) 文件。然后调用 $(AR) rcSTP 将这些文件合并到一个 built-in.a 文件中。这是一个没有符号表的精简归档文件。它将稍后由 scripts/link-vmlinux.sh 链接到 vmlinux 中。

$(obj-y) 中文件的顺序很重要。列表中允许重复:第一个实例将被链接到 built-in.a 中,而后面的实例将被忽略。

链接顺序很重要,因为某些函数(module_init() / __initcall)将在启动期间按照它们出现的顺序被调用。因此请记住,更改链接顺序可能会更改您的 SCSI 控制器的检测顺序,从而导致您的磁盘重新编号。

示例

#drivers/isdn/i4l/Makefile
# Makefile for the kernel ISDN subsystem and device drivers.
# Each configuration option enables a list of files.
obj-$(CONFIG_ISDN_I4L)         += isdn.o
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

可加载模块目标 - obj-m

$(obj-m) 指定构建为可加载内核模块的目标文件。

一个模块可以从一个源文件或多个源文件构建。在单个源文件的情况下,kbuild makefile 只是将该文件添加到 $(obj-m) 中。

示例

#drivers/isdn/i4l/Makefile
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

注意:在本例中,$(CONFIG_ISDN_PPP_BSDCOMP) 的值为 “m”

如果内核模块是从多个源文件构建的,您需要以与上述相同的方式指定要构建模块。但是,kbuild 需要知道要从哪些目标文件构建模块,因此您必须通过设置 $(<module_name>-y) 变量来告诉它。

示例

#drivers/isdn/i4l/Makefile
obj-$(CONFIG_ISDN_I4L) += isdn.o
isdn-y := isdn_net_lib.o isdn_v110.o isdn_common.o

在本例中,模块名称将为 isdn.o。Kbuild 将编译 $(isdn-y) 中列出的对象,然后对这些文件的列表运行 $(LD) -r 以生成 isdn.o。

由于 kbuild 识别用于复合对象的 $(<module_name>-y),因此您可以使用 CONFIG_ 符号的值来有选择地将目标文件作为复合对象的一部分包含进来。

示例

#fs/ext2/Makefile
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o dir.o file.o ialloc.o inode.o ioctl.o \
  namei.o super.o symlink.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o \
  xattr_trusted.o

在本例中,只有当 $(CONFIG_EXT2_FS_XATTR) 的值为 “y” 时,xattr.o、xattr_user.o 和 xattr_trusted.o 才是复合对象 ext2.o 的一部分。

注意:当然,当您将对象构建到内核中时,上面的语法也有效。因此,如果您有 CONFIG_EXT2_FS=y,kbuild 将为您构建一个由各个部分组成的 ext2.o 文件,然后将其链接到 built-in.a 中,正如您所期望的那样。

库文件目标 - lib-y

使用 obj-* 列出的对象用于模块,或者组合到该特定目录的 built-in.a 中。还可以列出将包含在库 lib.a 中的对象。所有使用 lib-y 列出的对象都组合到该目录的单个库中。在 obj-y 中列出并且还列在 lib-y 中的对象将不包含在库中,因为无论如何都可以访问它们。为了保持一致性,lib-m 中列出的对象将包含在 lib.a 中。

请注意,同一个 kbuild makefile 可能会列出要内置的文件和要作为库一部分的文件。因此,同一个目录可能同时包含 built-in.a 和 lib.a 文件。

示例

#arch/x86/lib/Makefile
lib-y    := delay.o

这将创建一个基于 delay.o 的库 lib.a。为了让 kbuild 实际识别到正在构建 lib.a,该目录应在 libs-y 中列出。

另请参见 列出下降时要访问的目录

lib-y 的使用通常仅限于 lib/arch/*/lib

在目录中向下递归

Makefile 仅负责构建其自身目录中的对象。子目录中的文件应由这些子目录中的 Makefiles 处理。构建系统将自动在子目录中递归调用 make,前提是您让它知道这些子目录。

为此,使用了 obj-y 和 obj-m。ext2 位于单独的目录中,fs/ 中的 Makefile 告诉 kbuild 使用以下赋值向下递归。

示例

#fs/Makefile
obj-$(CONFIG_EXT2_FS) += ext2/

如果 CONFIG_EXT2_FS 设置为 “y”(内置)或 “m”(模块化),则将设置相应的 obj- 变量,并且 kbuild 将向下递归到 ext2 目录中。

Kbuild 不仅使用此信息来决定是否需要访问该目录,还决定是否将该目录中的对象链接到 vmlinux 中。

当 Kbuild 使用 “y” 进入该目录时,该目录中的所有内置对象都将合并到 built-in.a 中,最终将链接到 vmlinux 中。

相反,当 Kbuild 使用 “m” 进入该目录时,该目录中的任何内容都不会链接到 vmlinux 中。如果该目录中的 Makefile 指定了 obj-y,则这些对象将成为孤立对象。这很可能是 Makefile 或 Kconfig 中依赖项的错误。

Kbuild 还支持用于进入子目录的专用语法 subdir-y 和 subdir-m。当您知道它们根本不包含内核空间对象时,这是一个很好的选择。一个典型的用法是让 Kbuild 进入子目录以构建工具。

示例

# scripts/Makefile
subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins
subdir-$(CONFIG_MODVERSIONS) += genksyms
subdir-$(CONFIG_SECURITY_SELINUX) += selinux

与 obj-y/m 不同,subdir-y/m 不需要尾部斜杠,因为此语法始终用于目录。

良好的做法是在分配目录名称时使用 CONFIG_ 变量。如果相应的 CONFIG_ 选项既不是 “y” 也不是 “m”,则这允许 kbuild 完全跳过该目录。

非内置 vmlinux 目标 - extra-y

extra-y 指定构建 vmlinux 所需的目标,但未合并到 built-in.a 中。

示例有

  1. vmlinux 链接器脚本

    vmlinux 的链接器脚本位于 arch/$(SRCARCH)/kernel/vmlinux.lds

示例

# arch/x86/kernel/Makefile
extra-y       += vmlinux.lds

extra-y 现在已弃用,因为这等效于

always-$(KBUILD_BUILTIN) += vmlinux.lds

$(extra-y) 应仅包含 vmlinux 所需的目标。

当 vmlinux 显然不是最终目标时,Kbuild 会跳过 extra-y。(例如 make modules,或构建外部模块)

如果您打算无条件地构建目标,则 always-y(在下一节中解释)是正确的语法。

始终构建的目标 - always-y

always-y 指定当 Kbuild 访问 Makefile 时,实际上始终构建的目标。

示例

# ./Kbuild
offsets-file := include/generated/asm-offsets.h
always-y += $(offsets-file)

编译标志

ccflags-y、asflags-y 和 ldflags-y

这三个标志仅适用于分配它们的 kbuild makefile。它们用于递归构建期间发生的所有常规 cc、as 和 ld 调用。

ccflags-y 指定使用 $(CC) 进行编译的选项。

示例

# drivers/acpi/acpica/Makefile
ccflags-y                           := -Os -D_LINUX -DBUILDING_ACPICA
ccflags-$(CONFIG_ACPI_DEBUG)        += -DACPI_DEBUG_OUTPUT

此变量是必需的,因为顶层 Makefile 拥有变量 $(KBUILD_CFLAGS) 并将其用于整个树的编译标志。

asflags-y 指定汇编器选项。

示例

#arch/sparc/kernel/Makefile
asflags-y := -ansi

ldflags-y 指定使用 $(LD) 进行链接的选项。

示例

#arch/cris/boot/compressed/Makefile
ldflags-y += -T $(src)/decompress_$(arch-y).lds
subdir-ccflags-y、subdir-asflags-y

上面列出的两个标志类似于 ccflags-y 和 asflags-y。区别在于 subdir- 变体对存在它们的 kbuild 文件和所有子目录都有效。使用 subdir-* 指定的选项将添加到命令行,然后添加使用非 subdir 变体指定的选项。

示例

subdir-ccflags-y := -Werror
ccflags-remove-y、asflags-remove-y

这些标志用于删除编译器、汇编器调用中的特定标志。

示例

ccflags-remove-$(CONFIG_MCOUNT) += -pg
CFLAGS_$@、AFLAGS_$@

CFLAGS_$@ 和 AFLAGS_$@ 仅适用于当前 kbuild makefile 中的命令。

$(CFLAGS_$@) 指定 $(CC) 的每个文件选项。$@ 部分有一个字面值,用于指定它的文件。

CFLAGS_$@ 具有比 ccflags-remove-y 更高的优先级;CFLAGS_$@ 可以重新添加被 ccflags-remove-y 删除的编译器标志。

示例

# drivers/scsi/Makefile
CFLAGS_aha152x.o =   -DAHA152X_STAT -DAUTOCONF

此行指定 aha152x.o 的编译标志。

$(AFLAGS_$@) 是汇编语言源文件的类似功能。

AFLAGS_$@ 具有比 asflags-remove-y 更高的优先级;AFLAGS_$@ 可以重新添加被 asflags-remove-y 删除的汇编器标志。

示例

# arch/arm/kernel/Makefile
AFLAGS_head.o        := -DTEXT_OFFSET=$(TEXT_OFFSET)
AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312
AFLAGS_iwmmxt.o      := -Wa,-mcpu=iwmmxt

依赖关系跟踪

Kbuild 跟踪以下依赖关系

  1. 所有必需文件(*.c*.h

  2. 所有必需文件中使用的 CONFIG_ 选项

  3. 用于编译目标的命令行

因此,如果您更改 $(CC) 的选项,所有受影响的文件都将被重新编译。

自定义规则

当 kbuild 基础设施不提供所需的支持时,使用自定义规则。一个典型的例子是在构建过程中生成的头文件。另一个例子是架构特定的 Makefiles,它需要自定义规则来准备引导镜像等。

自定义规则编写为普通的 Make 规则。Kbuild 不在 Makefile 所在的目录中执行,因此所有自定义规则都应使用相对于先决条件文件和目标文件的相对路径。

定义自定义规则时使用两个变量

$(src)

$(src) 是 Makefile 所在的目录。在引用 src 树中的文件时,始终使用 $(src)。

$(obj)

$(obj) 是保存目标的目录。在引用生成的文件时,始终使用 $(obj)。对于需要在生成文件和真实源文件上工作的模式规则,请使用 $(obj)(VPATH 将有助于不仅在对象树中查找先决条件,而且在源树中查找先决条件)。

示例

#drivers/scsi/Makefile
$(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl
$(CPP) -DCHIP=810 - < $< | ... $(src)/script_asm.pl

这是一个自定义规则,遵循 make 所需的普通语法。

目标文件取决于两个先决条件文件。对目标文件的引用以 $(obj) 为前缀,对先决条件的引用以 $(src) 为前缀(因为它们不是生成的文件)。

$(srcroot)

$(srcroot) 指的是您正在构建的源的根目录,这可以是内核源或外部模块源,具体取决于是否设置了 KBUILD_EXTMOD。这可以是相对路径或绝对路径,但如果设置了 KBUILD_ABS_SRCTREE=1,则它始终是绝对路径。

$(srctree)

$(srctree) 指的是内核源树的根目录。构建内核时,这与 $(srcroot) 相同。

$(objtree)

$(objtree) 指的是内核对象树的根目录。构建内核时它是 .,但在构建外部模块时它不同。

$(kecho)

在规则中向用户回显信息通常是一种好的做法,但是当执行 make -s 时,除了警告/错误之外,人们不希望看到任何输出。为了支持这一点,kbuild 定义了 $(kecho),除非使用 make -s,否则它会将 $(kecho) 后面的文本回显到 stdout。

示例

# arch/arm/Makefile
$(BOOT_TARGETS): vmlinux
        $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
        @$(kecho) '  Kernel: $(boot)/$@ is ready'

当 kbuild 在 KBUILD_VERBOSE 未设置的情况下执行时,通常只显示命令的简写形式。为了对自定义命令启用此行为,kbuild 要求设置两个变量

quiet_cmd_<command> - what shall be echoed
      cmd_<command> - the command to execute

示例

# lib/Makefile
quiet_cmd_crc32 = GEN     $@
      cmd_crc32 = $< > $@

$(obj)/crc32table.h: $(obj)/gen_crc32table
        $(call cmd,crc32)

当更新 $(obj)/crc32table.h 目标时,行

GEN     lib/crc32table.h

将与 make KBUILD_VERBOSE= 一起显示。

命令更改检测

当评估规则时,会比较目标及其先决条件文件之间的时间戳。当任何先决条件比目标新时,GNU Make 会更新目标。

当自上次调用以来命令行发生更改时,也应重新构建目标。Make 本身不支持这一点,因此 Kbuild 通过一种元编程来实现这一点。

if_changed 是用于此目的的宏,形式如下

quiet_cmd_<command> = ...
      cmd_<command> = ...

<target>: <source(s)> FORCE
        $(call if_changed,<command>)

任何使用 if_changed 的目标都必须在 $(targets) 中列出,否则命令行检查将失败,并且目标将始终被构建。

如果目标已经以识别的语法(如 obj-y/m、lib-y/m、extra-y/m、always-y/m、hostprogs、userprogs)列出,则 Kbuild 会自动将其添加到 $(targets)。否则,必须将目标显式添加到 $(targets)。

对 $(targets) 的赋值没有 $(obj)/ 前缀。if_changed 可以与 自定义规则 中定义的自定义规则结合使用。

注意:忘记 FORCE 先决条件是一个典型的错误。另一个常见的陷阱是空格有时很重要;例如,下面将失败(注意逗号后的额外空格)

target: source(s) FORCE

错误! $(call if_changed, objcopy)

注意

if_changed 不应每个目标使用多次。它将执行的命令存储在相应的 .cmd 文件中,并且当目标是最新的并且只有对更改命令的测试触发命令执行时,多次调用将导致覆盖和不希望的结果。

$(CC) 支持函数

内核可以使用几个不同版本的 $(CC) 构建,每个版本都支持一组独特的功能和选项。kbuild 提供了基本支持来检查 $(CC) 的有效选项。$(CC) 通常是 gcc 编译器,但也有其他替代方案可用。

as-option

as-option 用于检查 $(CC) -- 当用于编译汇编器 (*.S) 文件时 -- 是否支持给定的选项。如果不支持第一个选项,则可以指定一个可选的第二个选项。

示例

#arch/sh/Makefile
cflags-y += $(call as-option,-Wa$(comma)-isa=$(isa-y),)

在上面的示例中,如果 $(CC) 支持 -Wa$(comma)-isa=$(isa-y),则将为 cflags-y 分配选项 -Wa$(comma)-isa=$(isa-y)。第二个参数是可选的,如果提供,则在不支持第一个参数时使用。

as-instr

as-instr 检查汇编器是否报告特定的指令,然后输出 option1 或 option2 C 转义在测试指令中受支持。注意:as-instr-option 使用 KBUILD_AFLAGS 作为汇编器选项

cc-option

cc-option 用于检查 $(CC) 是否支持给定的选项,如果不支持,则使用一个可选的第二个选项。

示例

#arch/x86/Makefile
cflags-y += $(call cc-option,-march=pentium-mmx,-march=i586)

在上面的示例中,如果 $(CC) 支持,则将为 cflags-y 分配选项 -march=pentium-mmx,否则为 -march=i586。cc-option 的第二个参数是可选的,如果省略,则在不支持第一个选项时,将不会为 cflags-y 分配任何值。注意:cc-option 使用 KBUILD_CFLAGS 作为 $(CC) 选项

cc-option-yn

cc-option-yn 用于检查 $(CC) 是否支持给定的选项,如果支持则返回 “y”,否则返回 “n”。

示例

#arch/ppc/Makefile
biarch := $(call cc-option-yn, -m32)
aflags-$(biarch) += -a32
cflags-$(biarch) += -m32

在上面的示例中,如果 $(CC) 支持 -m32 选项,则将 $(biarch) 设置为 y。当 $(biarch) 等于 “y” 时,扩展的变量 $(aflags-y) 和 $(cflags-y) 将分别被赋值为 -a32 和 -m32。

注意:cc-option-yn 使用 KBUILD_CFLAGS 作为 $(CC) 选项

cc-disable-warning

cc-disable-warning 检查 $(CC) 是否支持给定的警告,并返回禁用它的命令行开关。需要这个特殊函数,因为 gcc 4.4 及更高版本接受任何未知的 -Wno-* 选项,并且只有当源文件中存在另一个警告时才会发出警告。

示例

KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable)

在上面的示例中,只有当 $(CC) 真正接受 -Wno-unused-but-set-variable 时,才会将其添加到 KBUILD_CFLAGS 中。

gcc-min-version

gcc-min-version 测试 $(CONFIG_GCC_VERSION) 的值是否大于或等于提供的值,如果是,则评估为 y。

示例

cflags-$(call gcc-min-version, 110100) := -foo

在此示例中,如果 $(CC) 是 gcc 并且 $(CONFIG_GCC_VERSION) >= 11.1,则 cflags-y 将被赋值为 -foo。

clang-min-version

clang-min-version 测试 $(CONFIG_CLANG_VERSION) 的值是否大于或等于提供的值,如果是,则评估为 y。

示例

cflags-$(call clang-min-version, 110000) := -foo

在此示例中,如果 $(CC) 是 clang 并且 $(CONFIG_CLANG_VERSION) >= 11.0.0,则 cflags-y 将被赋值为 -foo。

cc-cross-prefix

cc-cross-prefix 用于检查路径中是否存在带有列出的前缀之一的 $(CC)。返回路径中存在 prefix$(CC) 的第一个前缀 - 如果未找到 prefix$(CC),则不返回任何内容。

其他前缀在 cc-cross-prefix 的调用中用单个空格分隔。

此功能对于尝试将 CROSS_COMPILE 设置为众所周知的值但可能需要在多个值之间进行选择的架构 Makefiles 很有用。

建议仅在交叉构建(主机架构与目标架构不同)时才尝试设置 CROSS_COMPILE。如果 CROSS_COMPILE 已经设置,则将其保留为旧值。

示例

#arch/m68k/Makefile
ifneq ($(SUBARCH),$(ARCH))
        ifeq ($(CROSS_COMPILE),)
                CROSS_COMPILE := $(call cc-cross-prefix, m68k-linux-gnu-)
        endif
endif

$(RUSTC) 支持函数

rustc-min-version

rustc-min-version 测试 $(CONFIG_RUSTC_VERSION) 的值是否大于或等于提供的值,如果是,则评估为 y。

示例

rustflags-$(call rustc-min-version, 108500) := -Cfoo

在此示例中,如果 $(CONFIG_RUSTC_VERSION) >= 1.85.0,则 rustflags-y 将被赋值为 -Cfoo。

$(LD) 支持函数

ld-option

ld-option 用于检查 $(LD) 是否支持提供的选项。ld-option 接受两个选项作为参数。

第二个参数是一个可选选项,如果 $(LD) 不支持第一个选项,则可以使用该选项。

示例

#Makefile
LDFLAGS_vmlinux += $(call ld-option, -X)

脚本调用

Make 规则可能会调用脚本来构建内核。这些规则应始终提供适当的解释器来执行脚本。它们不应依赖于设置的执行位,并且不应直接调用脚本。为了便于手动脚本调用,例如调用 ./scripts/checkpatch.pl,建议仍然在脚本上设置执行位。

Kbuild 提供了变量 $(CONFIG_SHELL)、$(AWK)、$(PERL) 和 $(PYTHON3) 来引用各个脚本的解释器。

示例

#Makefile
cmd_depmod = $(CONFIG_SHELL) $(srctree)/scripts/depmod.sh $(DEPMOD) \
        $(KERNELRELEASE)

主机程序支持

Kbuild 支持在主机上构建可执行文件,以便在编译阶段使用。

要使用主机可执行文件,需要两个步骤。

第一步是告诉 kbuild 存在主机程序。这是利用变量 hostprogs 完成的。

第二步是向可执行文件添加显式依赖项。这可以通过两种方式完成。要么在规则中添加依赖项,要么利用变量 always-y。以下将描述这两种可能性。

简单主机程序

在某些情况下,需要在运行构建的计算机上编译和运行程序。

以下行告诉 kbuild 程序 bin2hex 应在构建主机上构建。

示例

hostprogs := bin2hex

在上面的例子中,Kbuild 假设 bin2hex 是由一个名为 bin2hex.c 的 C 源代码文件创建的,该文件与 Makefile 位于同一目录中。

组合主机程序

主机程序可以基于组合对象构成。用于定义主机程序的组合对象的语法类似于用于内核对象的语法。 $(<executable>-objs) 列出了用于链接最终可执行文件的所有对象。

示例

#scripts/lxdialog/Makefile
hostprogs     := lxdialog
lxdialog-objs := checklist.o lxdialog.o

扩展名为 .o 的对象是从相应的 .c 文件编译而来。在上面的示例中,checklist.c 被编译为 checklist.o,lxdialog.c 被编译为 lxdialog.o。

最后,这两个 .o 文件链接到可执行文件 lxdialog。注意:语法 <executable>-y 不允许用于主机程序。

使用 C++ 编写主机程序

kbuild 提供了对用 C++ 编写的主机程序的支持。 引入此功能仅用于支持 kconfig,不建议用于常规用途。

示例

#scripts/kconfig/Makefile
hostprogs     := qconf
qconf-cxxobjs := qconf.o

在上面的例子中,可执行文件由 C++ 文件 qconf.cc 组成 - 由 $(qconf-cxxobjs) 标识。

如果 qconf 由 .c 和 .cc 文件混合组成,则可以使用额外的行来标识这一点。

示例

#scripts/kconfig/Makefile
hostprogs     := qconf
qconf-cxxobjs := qconf.o
qconf-objs    := check.o

使用 Rust 编写主机程序

Kbuild 提供了对用 Rust 编写的主机程序的支持。但是,由于 Rust 工具链对于内核编译不是强制性的,因此它只能用于需要 Rust 可用的场景(例如,当启用 CONFIG_RUST 时)。

示例

hostprogs     := target
target-rust   := y

Kbuild 将使用 target.rs 作为 crate 根编译 target,该文件与 Makefile 位于同一目录中。 crate 可能由多个源文件组成(请参阅 samples/rust/hostprogs)。

控制主机程序的编译器选项

编译主机程序时,可以设置特定的标志。 这些程序将始终使用 $(HOSTCC) 编译,并传递 $(KBUILD_HOSTCFLAGS) 中指定的选项。

要设置将对在该 Makefile 中创建的所有主机程序生效的标志,请使用变量 HOST_EXTRACFLAGS。

示例

#scripts/lxdialog/Makefile
HOST_EXTRACFLAGS += -I/usr/include/ncurses

要为单个文件设置特定标志,请使用以下结构

示例

#arch/ppc64/boot/Makefile
HOSTCFLAGS_piggyback.o := -DKERNELBASE=$(KERNELBASE)

也可以为链接器指定其他选项。

示例

#scripts/kconfig/Makefile
HOSTLDLIBS_qconf := -L$(QTDIR)/lib

链接 qconf 时,将传递额外的选项 -L$(QTDIR)/lib

何时实际构建主机程序

仅当主机程序被引用为先决条件时,Kbuild 才会构建它们。

这可以通过两种方式实现

  1. 在自定义规则中显式列出先决条件。

    示例

    #drivers/pci/Makefile
    hostprogs := gen-devlist
    $(obj)/devlist.h: $(src)/pci.ids $(obj)/gen-devlist
    ( cd $(obj); ./gen-devlist ) < $<
    

    在更新 $(obj)/gen-devlist 之前,不会构建目标 $(obj)/devlist.h。 请注意,自定义规则中对主机程序的引用必须以 $(obj) 为前缀。

  2. 使用 always-y

    当没有合适的自定义规则,并且主机程序应在输入 makefile 时构建时,应使用 always-y 变量。

    示例

    #scripts/lxdialog/Makefile
    hostprogs     := lxdialog
    always-y      := $(hostprogs)
    

    Kbuild 为此提供了以下简写

    hostprogs-always-y := lxdialog
    

    这将告诉 kbuild 构建 lxdialog,即使它未在任何规则中引用。

用户空间程序支持

与主机程序一样,Kbuild 还支持为目标架构(即您正在为其构建内核的架构)构建用户空间可执行文件。

语法非常相似。区别在于使用 userprogs 而不是 hostprogs

简单的用户空间程序

以下行告诉 kbuild 应为目标架构构建程序 bpf-direct。

示例

userprogs := bpf-direct

在上面的例子中,Kbuild 假设 bpf-direct 是由一个名为 bpf-direct.c 的 C 源代码文件创建的,该文件与 Makefile 位于同一目录中。

复合用户空间程序

用户空间程序可以基于组合对象构成。用于定义用户空间程序的组合对象的语法类似于用于内核对象的语法。 $(<executable>-objs) 列出了用于链接最终可执行文件的所有对象。

示例

#samples/seccomp/Makefile
userprogs      := bpf-fancy
bpf-fancy-objs := bpf-fancy.o bpf-helper.o

扩展名为 .o 的对象是从相应的 .c 文件编译而来。在上面的示例中,bpf-fancy.c 被编译为 bpf-fancy.o,bpf-helper.c 被编译为 bpf-helper.o。

最后,这两个 .o 文件链接到可执行文件 bpf-fancy。注意:语法 <executable>-y 不允许用于用户空间程序。

控制用户空间程序的编译器选项

编译用户空间程序时,可以设置特定的标志。 这些程序将始终使用 $(CC) 编译,并传递 $(KBUILD_USERCFLAGS) 中指定的选项。

要设置将对在该 Makefile 中创建的所有用户空间程序生效的标志,请使用变量 userccflags。

示例

# samples/seccomp/Makefile
userccflags += -I usr/include

要为单个文件设置特定标志,请使用以下结构

示例

bpf-helper-userccflags += -I user/include

也可以为链接器指定其他选项。

示例

# net/bpfilter/Makefile
bpfilter_umh-userldflags += -static

要指定链接到用户空间程序的库,可以使用 <executable>-userldlibsuserldlibs 语法指定链接到当前 Makefile 中创建的所有用户空间程序的库。

链接 bpfilter_umh 时,将传递额外的选项 -static。

从命令行,还将使用 USERCFLAGS 和 USERLDFLAGS

何时实际构建用户空间程序

Kbuild 仅在被告知这样做时才构建用户空间程序。 有两种方法可以做到这一点。

  1. 将其添加为另一个文件的先决条件

    示例

    #net/bpfilter/Makefile
    userprogs := bpfilter_umh
    $(obj)/bpfilter_umh_blob.o: $(obj)/bpfilter_umh
    

    在 $(obj)/bpfilter_umh_blob.o 之前构建 $(obj)/bpfilter_umh

  2. 使用 always-y

    示例

    userprogs := binderfs_example
    always-y := $(userprogs)
    

    Kbuild 为此提供了以下简写

    userprogs-always-y := binderfs_example
    

    这将告诉 Kbuild 在访问此 Makefile 时构建 binderfs_example。

Kbuild 清理基础架构

make clean 删除 obj 树中编译内核时生成的大多数文件。 这包括生成的文件,例如主机程序。 Kbuild 知道 $(hostprogs)、$(always-y)、$(always-m)、$(always-)、$(extra-y)、$(extra-) 和 $(targets) 中列出的目标。 它们在 make clean 期间全部被删除。 当执行 make clean 时,与模式 *.[oas]*.ko 匹配的文件以及 kbuild 生成的一些其他文件将在整个内核源代码树中被删除。

可以通过使用 $(clean-files) 在 kbuild makefile 中指定其他文件或目录。

示例

#lib/Makefile
clean-files := crc32table.h

执行 make clean 时,文件 crc32table.h 将被删除。 Kbuild 将假定文件与 Makefile 位于同一相对目录中。

要从 make clean 中排除某些文件或目录,请使用 $(no-clean-files) 变量。

通常,由于 obj-* := dir/,kbuild 会下降到子目录中,但在架构 makefile 中,kbuild 基础架构不足以明确这一点。

示例

#arch/x86/boot/Makefile
subdir- := compressed

上面的赋值指示 kbuild 在执行 make clean 时下降到目录 compressed/ 中。

注意 1:arch/$(SRCARCH)/Makefile 不能使用 subdir-,因为该文件包含在顶级 makefile 中。相反,arch/$(SRCARCH)/Kbuild 可以使用 subdir-

注意 2:在 make clean 期间将访问 core-y、libs-y、drivers-y 和 net-y 中列出的所有目录。

架构 Makefile

顶级 Makefile 设置环境并进行准备,然后开始下降到各个目录中。

顶级 makefile 包含通用部分,而 arch/$(SRCARCH)/Makefile 包含为所述架构设置 kbuild 所需的内容。

为此,arch/$(SRCARCH)/Makefile 设置了多个变量并定义了一些目标。

执行 kbuild 时,将执行以下步骤(大致)

  1. 内核配置 => 生成 .config

  2. 将内核版本存储在 include/linux/version.h 中

  3. 更新目标 prepare 的所有其他先决条件

    • 其他先决条件在 arch/$(SRCARCH)/Makefile 中指定

  4. 递归地下降到 init-* core* drivers-* net-* libs-* 中列出的所有目录中,并构建所有目标。

    • 上述变量的值在 arch/$(SRCARCH)/Makefile 中展开。

  5. 然后链接所有对象文件,并将生成的文件 vmlinux 位于 obj 树的根目录中。链接的第一个对象列在 scripts/head-object-list.txt 中。

  6. 最后,架构特定部分执行任何所需的后处理并构建最终引导映像。

    • 这包括构建引导记录

    • 准备 initrd 镜像等

设置变量以调整构建到架构

KBUILD_LDFLAGS

通用 $(LD) 选项

用于所有链接器调用的标志。 通常,指定模拟就足够了。

示例

#arch/s390/Makefile
KBUILD_LDFLAGS         := -m elf_s390

注意:ldflags-y 可用于进一步自定义使用的标志。 请参阅 非内置 vmlinux 目标 - extra-y

LDFLAGS_vmlinux

链接 vmlinux 时 $(LD) 的选项

LDFLAGS_vmlinux 用于指定在链接最终 vmlinux 镜像时传递给链接器的其他标志。

LDFLAGS_vmlinux 使用 LDFLAGS_$@ 支持。

示例

#arch/x86/Makefile
LDFLAGS_vmlinux := -e stext
OBJCOPYFLAGS

objcopy 标志

当使用 $(call if_changed,objcopy) 转换 .o 文件时,将使用 OBJCOPYFLAGS 中指定的标志。

$(call if_changed,objcopy) 通常用于在 vmlinux 上生成原始二进制文件。

示例

#arch/s390/Makefile
OBJCOPYFLAGS := -O binary

#arch/s390/boot/Makefile
$(obj)/image: vmlinux FORCE
        $(call if_changed,objcopy)

在此示例中,二进制文件 $(obj)/image 是 vmlinux 的二进制版本。 $(call if_changed,xxx) 的用法将在后面描述。

KBUILD_AFLAGS

汇编器标志

默认值 - 请参阅顶级 Makefile。

根据需要按架构追加或修改。

示例

#arch/sparc64/Makefile
KBUILD_AFLAGS += -m64 -mcpu=ultrasparc
KBUILD_CFLAGS

$(CC) 编译器标志

默认值 - 请参阅顶级 Makefile。

根据需要按架构追加或修改。

通常,KBUILD_CFLAGS 变量取决于配置。

示例

#arch/x86/boot/compressed/Makefile
cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small
KBUILD_CFLAGS += $(cflags-y)

许多 arch Makefile 动态运行目标 C 编译器来探测支持的选项

#arch/x86/Makefile

...
cflags-$(CONFIG_MPENTIUMII)     += $(call cc-option,\
                                            -march=pentium2,-march=i686)
...
# Disable unit-at-a-time mode ...
KBUILD_CFLAGS += $(call cc-option,-fno-unit-at-a-time)
...

第一个示例利用了配置选项在选择时展开为“y”的技巧。

KBUILD_RUSTFLAGS

$(RUSTC) 编译器标志

默认值 - 请参阅顶级 Makefile。

根据需要按架构追加或修改。

通常,KBUILD_RUSTFLAGS 变量取决于配置。

请注意,目标规范文件生成(对于 --target)在 scripts/generate_rust_target.rs 中处理。

KBUILD_AFLAGS_KERNEL

特定于内置的汇编器选项

$(KBUILD_AFLAGS_KERNEL) 包含用于编译驻留内核代码的额外 C 编译器标志。

KBUILD_AFLAGS_MODULE

特定于模块的汇编器选项

$(KBUILD_AFLAGS_MODULE) 用于添加用于汇编程序的架构特定选项。

从命令行应使用 AFLAGS_MODULE(请参阅 Kbuild)。

KBUILD_CFLAGS_KERNEL

$(CC) 特定于内置的选项

$(KBUILD_CFLAGS_KERNEL) 包含用于编译驻留内核代码的额外 C 编译器标志。

KBUILD_CFLAGS_MODULE

构建模块时 $(CC) 的选项

$(KBUILD_CFLAGS_MODULE) 用于添加用于 $(CC) 的架构特定选项。

从命令行应使用 CFLAGS_MODULE(请参阅 Kbuild)。

KBUILD_RUSTFLAGS_KERNEL

$(RUSTC) 特定于内置的选项

$(KBUILD_RUSTFLAGS_KERNEL) 包含用于编译驻留内核代码的额外 Rust 编译器标志。

KBUILD_RUSTFLAGS_MODULE

构建模块时 $(RUSTC) 的选项

$(KBUILD_RUSTFLAGS_MODULE) 用于添加用于 $(RUSTC) 的架构特定选项。

从命令行应使用 RUSTFLAGS_MODULE(请参阅 Kbuild)。

KBUILD_LDFLAGS_MODULE

链接模块时 $(LD) 的选项

$(KBUILD_LDFLAGS_MODULE) 用于添加链接模块时使用的架构特定选项。 这通常是一个链接器脚本。

从命令行应使用 LDFLAGS_MODULE(请参阅 Kbuild)。

KBUILD_LDS

带有完整路径的链接器脚本。 由顶级 Makefile 分配。

KBUILD_VMLINUX_OBJS

vmlinux 的所有对象文件。 它们以 KBUILD_VMLINUX_OBJS 中列出的相同顺序链接到 vmlinux。

scripts/head-object-list.txt 中列出的对象是例外;它们放置在其他对象之前。

KBUILD_VMLINUX_LIBS

vmlinux 的所有 .a lib 文件。 KBUILD_VMLINUX_OBJS 和 KBUILD_VMLINUX_LIBS 一起指定用于链接 vmlinux 的所有对象文件。

将先决条件添加到 archheaders

archheaders:规则用于生成可以通过 make header_install 安装到用户空间的头文件。

当在架构本身上运行时,它在 make archprepare 之前运行。

将先决条件添加到 archprepare

archprepare:规则用于列出在开始下降到子目录中之前需要构建的先决条件。

这通常用于包含汇编器常量的头文件。

示例

#arch/arm/Makefile
archprepare: maketools

在此示例中,文件目标 maketools 将在下降到子目录中之前处理。

另请参阅 XXX-TODO 章,该章描述了 kbuild 如何支持生成偏移头文件。

列出下降时要访问的目录

arch Makefile 与顶级 Makefile 协作,以定义指定如何构建 vmlinux 文件的变量。 请注意,没有相应的架构特定部分用于模块;模块构建机制都是与架构无关的。

core-y、libs-y、drivers-y

$(libs-y) 列出了 lib.a 存档可以位于的目录。

其余的列出了 built-in.a 对象文件可以位于的目录。

然后其余的按此顺序排列

$(core-y)、$(libs-y)、$(drivers-y)

顶级 Makefile 定义所有通用目录的值,而 arch/$(SRCARCH)/Makefile 仅添加架构特定目录。

示例

# arch/sparc/Makefile
core-y                 += arch/sparc/

libs-y                 += arch/sparc/prom/
libs-y                 += arch/sparc/lib/

drivers-$(CONFIG_PM) += arch/sparc/power/

架构特定引导映像

arch Makefile 指定目标,该目标获取 vmlinux 文件、对其进行压缩、将其包装在引导代码中,并将生成的文件复制到某个地方。 这包括各种安装命令。 实际目标在不同架构之间没有标准化。

通常,将任何其他处理放置在 arch/$(SRCARCH)/ 下的 boot/ 目录中。

Kbuild 没有提供任何智能方法来支持构建在 boot/ 中指定的目标。 因此,arch/$(SRCARCH)/Makefile 应手动调用 make 以在 boot/ 中构建目标。

建议的方法是在 arch/$(SRCARCH)/Makefile 中包含快捷方式,并在调用到 arch/$(SRCARCH)/boot/Makefile 中时使用完整路径。

示例

#arch/x86/Makefile
boot := arch/x86/boot
bzImage: vmlinux
        $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@

$(Q)$(MAKE) $(build)=<dir> 是在子目录中调用 make 的推荐方法。

没有用于命名架构特定目标的规则,但执行 make help 将列出所有相关目标。 为了支持这一点,必须定义 $(archhelp)。

示例

#arch/x86/Makefile
define archhelp
  echo  '* bzImage      - Compressed kernel image (arch/x86/boot/bzImage)'
endif

当执行 make 时不带参数,将构建遇到的第一个目标。 在顶级 Makefile 中,存在的第一个目标是 all:。

架构应始终默认构建可引导映像。 在 make help 中,默认目标用 * 突出显示。

将新的先决条件添加到 all: 以选择与 vmlinux 不同的默认目标。

示例

#arch/x86/Makefile
all: bzImage

当执行 make 时不带参数,将构建 bzImage。

用于构建引导映像的有用命令

Kbuild 提供了一些在构建引导映像时有用的宏。

ld

链接目标。 通常,LDFLAGS_$@ 用于为 ld 设置特定选项。

示例

#arch/x86/boot/Makefile
LDFLAGS_bootsect := -Ttext 0x0 -s --oformat binary
LDFLAGS_setup    := -Ttext 0x0 -s --oformat binary -e begtext

targets += setup setup.o bootsect bootsect.o
$(obj)/setup $(obj)/bootsect: %: %.o FORCE
        $(call if_changed,ld)

在此示例中,有两个可能的目标,需要不同的链接器选项。 链接器选项使用 LDFLAGS_$@ 语法指定 - 每个潜在目标一个。

$(targets) 分配所有潜在目标,kbuild 通过它了解目标,并将

  1. 检查命令行更改

  2. 在 make clean 期间删除目标

先决条件的 : %: %.o 部分是一个简写,它使我们不必列出 setup.o 和 bootsect.o 文件。

注意:忘记 targets := 分配是一个常见错误,导致目标文件因不明显的原因而被重新编译。

objcopy

复制二进制文件。 使用 OBJCOPYFLAGS,通常在 arch/$(SRCARCH)/Makefile 中指定。

OBJCOPYFLAGS_$@ 可用于设置其他选项。

gzip

压缩目标。 使用最大压缩来压缩目标。

示例

#arch/x86/boot/compressed/Makefile
$(obj)/vmlinux.bin.gz: $(vmlinux.bin.all-y) FORCE
        $(call if_changed,gzip)
dtc

创建适合链接到 vmlinux 中的扁平化设备树 blob 对象。 链接到 vmlinux 中的设备树 blob 放置在镜像的 init 部分中。 平台代码必须在调用 unflatten_device_tree() 之前将 blob 复制到非 init 内存中。

要使用此命令,只需将 *.dtb 添加到 obj-y 或 targets 中,或使某个其他目标依赖于 %.dtb

存在一个中心规则来从 $(src)/%.dts 创建 $(obj)/%.dtb;架构 Makefile 不需要显式写出该规则。

示例

targets += $(dtb-y)
DTC_FLAGS ?= -p 1024

预处理链接器脚本

构建 vmlinux 镜像时,使用链接器脚本 arch/$(SRCARCH)/kernel/vmlinux.lds。

该脚本是位于同一目录中的文件 vmlinux.lds.S 的预处理变体。

kbuild 知道 .lds 文件并包含规则 *lds.S -> *lds

示例

#arch/x86/kernel/Makefile
extra-y := vmlinux.lds

对 extra-y 的赋值用于告诉 kbuild 构建目标 vmlinux.lds。

对 $(CPPFLAGS_vmlinux.lds) 的赋值告诉 kbuild 在构建目标 vmlinux.lds 时使用指定的选项。

在构建 *.lds 目标时,kbuild 使用变量

KBUILD_CPPFLAGS      : Set in top-level Makefile
cppflags-y           : May be set in the kbuild makefile
CPPFLAGS_$(@F)       : Target-specific flags.
                       Note that the full filename is used in this
                       assignment.

用于 *lds 文件的 kbuild 基础架构在多个架构特定文件中使用。

通用头文件

目录 include/asm-generic 包含可在各个架构之间共享的头文件。

如何使用通用头文件的推荐方法是在 Kbuild 文件中列出该文件。

有关语法等的更多信息,请参阅 generic-y

链接后传递

如果文件 arch/xxx/Makefile.postlink 存在,则将为架构调用此 makefile,以对链接后对象(vmlinux 和 modules.ko)运行链接后传递。 还必须处理 clean 目标。

此传递在 kallsyms 生成之后运行。 如果架构需要修改符号位置,而不是操作 kallsyms,则添加另一个 postlink 目标以从 link-vmlinux.sh 调用的 .tmp_vmlinux? 目标可能更容易。

例如,powerpc 使用它来检查链接的 vmlinux 文件的重定位健全性。

导出的头文件的 Kbuild 语法

内核包含一组导出到用户空间的头文件。 许多头文件可以按原样导出,但其他头文件在准备好供用户空间使用之前需要最少的预处理。

预处理执行

  • 删除内核特定注释

  • 删除 include of compiler.h

  • 删除内核内部的所有部分(由 ifdef __KERNEL__ 保护)

导出 include/uapi/、include/generated/uapi/、arch/<arch>/include/uapi/ 和 arch/<arch>/include/generated/uapi/ 下的所有头文件。

可以在 arch/<arch>/include/uapi/asm/ 和 arch/<arch>/include/asm/ 下定义 Kbuild 文件,以列出来自 asm-generic 的 asm 文件。

有关 Kbuild 文件的语法,请参阅后续章节。

no-export-headers

no-export-headers 主要由 include/uapi/linux/Kbuild 使用,以避免在不支持特定标头的架构上导出特定标头(例如,kvm.h)。 应尽可能避免使用它。

generic-y

如果架构使用来自 include/asm-generic 的标头的逐字副本,则将其列在文件 arch/$(SRCARCH)/include/asm/Kbuild 中,如下所示

示例

#arch/x86/include/asm/Kbuild
generic-y += termios.h
generic-y += rtc.h

在构建的 prepare 阶段,将在目录中生成一个包装器包含文件

arch/$(SRCARCH)/include/generated/asm

当导出架构使用通用标头的标头时,会在目录中导出的标头集中生成类似的包装器作为一部分

usr/include/asm

这两种情况下生成的包装器都将如下所示

示例:termios.h

#include <asm-generic/termios.h>

generated-y

如果架构在通用-y 包装器旁边生成其他头文件,则 generated-y 会指定它们。

这可以防止它们被视为过时的 asm-generic 包装器并被删除。

示例

#arch/x86/include/asm/Kbuild
generated-y += syscalls_32.h

mandatory-y

mandatory-y 主要由 include/(uapi/)asm-generic/Kbuild 使用,以定义所有架构必须拥有的 ASM 标头的最小集合。

这类似于可选的 generic-y。 如果 arch/$(SRCARCH)/include/(uapi/)/asm 中缺少强制标头,Kbuild 将自动生成 asm-generic 标头的包装器。

Kbuild 变量

顶级 Makefile 导出以下变量

VERSION、PATCHLEVEL、SUBLEVEL、EXTRAVERSION

这些变量定义了当前的内核版本。一些架构的 Makefile 实际上直接使用了这些值;它们应该使用 $(KERNELRELEASE) 代替。

$(VERSION)、$(PATCHLEVEL) 和 $(SUBLEVEL) 定义了基本的三部分版本号,例如“2”、“4”和“0”。这三个值始终是数字。

$(EXTRAVERSION) 为预补丁或附加补丁定义了一个更小的子级别。它通常是一些非数字字符串,例如“-pre4”,并且通常为空。

KERNELRELEASE

$(KERNELRELEASE) 是一个单独的字符串,例如“2.4.0-pre4”,适合用于构建安装目录名或在版本字符串中显示。一些架构 Makefile 将其用于此目的。

ARCH

此变量定义了目标架构,例如“i386”、“arm”或“sparc”。一些 kbuild Makefile 测试 $(ARCH) 以确定要编译的文件。

默认情况下,顶层 Makefile 将 $(ARCH) 设置为与主机系统架构相同。对于交叉编译,用户可以在命令行上覆盖 $(ARCH) 的值。

make ARCH=m68k ...
SRCARCH

此变量指定要在 arch/ 中构建的目录。

ARCH 和 SRCARCH 可能不一定匹配。一些 arch 目录是双架构的,也就是说,单个 arch/*/ 目录支持 32 位和 64 位。

例如,您可以传入 ARCH=i386、ARCH=x86_64 或 ARCH=x86。 对于所有这些,SRCARCH=x86,因为 arch/x86/ 同时支持 i386 和 x86_64。

INSTALL_PATH

此变量定义了一个位置,供架构 Makefile 安装常驻内核镜像和 System.map 文件。将此用于特定于架构的安装目标。

INSTALL_MOD_PATH, MODLIB

$(INSTALL_MOD_PATH) 指定了 $(MODLIB) 的前缀,用于模块安装。 此变量未在 Makefile 中定义,但如果需要,用户可以传入。

$(MODLIB) 指定了模块安装的目录。 顶层 Makefile 将 $(MODLIB) 定义为 $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)。如果需要,用户可以在命令行上覆盖此值。

INSTALL_MOD_STRIP

如果指定了此变量,它将导致模块在安装后被剥离。如果 INSTALL_MOD_STRIP 为“1”,则将使用默认选项 --strip-debug。 否则,INSTALL_MOD_STRIP 值将用作 strip 命令的选项。

INSTALL_DTBS_PATH

此变量指定了构建根目录所需的重定位的前缀。 它定义了安装设备树 blob 的位置。与 INSTALL_MOD_PATH 一样,它没有在 Makefile 中定义,但可以由用户传入(如果需要)。否则,它默认为内核安装路径。

Makefile 语言

内核 Makefile 旨在与 GNU Make 一起运行。 Makefile 仅使用 GNU Make 的文档化功能,但它们确实使用了许多 GNU 扩展。

GNU Make 支持基本的列表处理功能。内核 Makefile 使用了一种新颖的列表构建和操作风格,几乎没有 if 语句。

GNU Make 有两个赋值运算符,:==:= 执行右侧的立即求值,并将实际字符串存储到左侧。 = 就像一个公式定义; 它将右侧以未求值的形式存储,然后在每次使用左侧时对该形式进行求值。

在某些情况下,= 是合适的。 但是,通常 := 是正确的选择。

致谢

待办事项

  • 生成偏移量头文件。

  • 在第 7 或第 9 章中添加更多变量?