Linux Kbuild文档 3

3. Kbuild Makefile

Linux内核源代码是通过Makefile组织编译的,Linux2.6内核Makefile的许多特性和2.4内核差别很大,在内核目录的documention/kbuild/makefiles.txt中有详细的说明。

3.1 Makefile的组织结构

Linux内核的Makefile分为5个部分,如表6所示:

表6 Makefile的5个部分

Makefile

顶层Makefile

.config

内核配置文件

arch/$(ARCH)/Makefile

具体架构的Makefile

scripts/Makefile.*

通用的规则等。面向所有的Kbuild Makefiles。

kbuild Makefiles

内核源代码中大约有500个这样的文件

顶层Makefile阅读的.config文件,而该文件是由内核配置程序生成的。

顶层Makefile负责制作:vmlinux(内核文件)与模块(任何模块文件)。制作的过程主要是

通过递归向下访问子目录的形式完成。并根据内核配置文件确定访问哪些子目录。顶层Makefile要原封不动的包含一具体架构的Makefile,其名字类似于arch/$(ARCH)/Makefile。该架构Makefile向顶层Makefile提供其架构的特别信息。

每一个子目录都有一个Kbuild Makefile文件,用来执行从其上层目录传递下来的命令。

Kbuild Makefile从.config文件中提取信息,生成Kbuild完成内核编译所需的文件列表。

scripts/Makefile.*包含了所有的定义、规则等信息。这些文件被用来编译基于kbuild

Makefile的内核。

3.2 Makefile语言

内核的Makefile使用的是GNU Make。该Makefile只使用GNU Make已注明的功能,并使用了许多GNU 的扩展功能。

GNU Make支持基本的显示处理过程的函数。内核Makefile 使用了一种类似小说的方式,显示"if"语句的构造、处理过程。

GNU Make 有2个赋值操作符,":="和"="。":=",将对右边的表达式求值,并将所求的值赋给左边。"="更像是一个公式定义,只是将右边的值简单的赋值给左边,当左边的表达式被使用时,才求值。

有时使用"="是正确的。但是,一般情况下,推荐使用":="。

3.3 Kbuild 变量

顶层Makefile输出以下变量:


(1)VERSION、PATCHLEVEL、SUBLEVEL和EXTRAVERSION

这些变量定义了当前内核的版本号。只有很少一部分Makefile会直接用到这些变量;可使用 $(KERNELRELEASE)代替。$(VERSION),$(PATCHLEVEL),和$(SUBLEVEL)定义了最初使用的三个数字的版本号,比如"2""4"和"0"。这三个值一般是数字。$(EXTRAVERSION) 为了补丁定义了更小的版本号。一般是非数字的字符串,比如"-pre4" ,或就空着。


(2)KERNELRELEASE

$(KERNELRELEASE) 是一个字符串,类似"2.4.0-pre4",用于安装目录的命名或显示当前的版本号。一部分架构Makefile使用该变量。


(3)ARCH

该变量定义了目标架构,比如"i386","arm" 或"sparc"。有些Kbuild Makefile根据 $(ARCH) 决定编译哪些文件。

默认情况下,顶层Makefile将其设置为本机架构。如果是跨平台编译,用户可以用下面的命令覆盖该值:

make ARCH=m68k ...


(4)INSTALL_PATH

该变量为架构Makefile定义了安装内核镜像与 System.map 文件的目录。主要用来指明架构特殊的安装路径。


(5)INSTALL_MOD_PATH和MODLIB

$(INSTALL_MOD_PATH) 为了安装模块,给 $(MODLIB) 声明了前缀。该变量不能在Makefile中定义,但可以由用户传给Makefile。

$(MODLIB) 具体的模块安装的路径。顶层Makefile将$(MODLIB)定义为

$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)。用户可以通过命令行参数的形式将其覆盖。


(6)INSTALL_MOD_STRIP

如果该变量有定义,模块在安装之前,会被剥出符号表。如果INSTALL_MOD_STRIP 为 "1",就使用默认选项 --strip-debug。否则,INSTALL_MOD_STRIP 将作为命令 strip的选项使用。

3.4 Kbuild Makefile的定义

大部分内核中的Makefile都是使用Kbuild组织结构的Kbuild Makefile。这章介绍了

Kbuild Makefile的语法。

Kbuild文件倾向于"Makefile"这个名字,"Kbuild"也是可以用的。但如果"Makefile"

"Kbuild"同时出现的话,使用的将会是"Kbuild"文件。


3.4.1 目标定义

目标定义是Kbuild Makefile的主要部分,也是核心部分。主要是定义了要编

译的文件,所有的选项,以及到哪些子目录去执行递归操作。

最简单的Kbuild makefile 只包含一行,如:

该例子告诉Kbuild在这目录里,有一个名为foo.o的目标文件。foo.o将从foo.c或foo.S文件编译得到。

如果foo.o要编译成一模块,那就要用obj-m了。所采用的形式如下:

$(CONFIG_FOO)可以为y(编译进内核) 或m(编译成模块)。如果CONFIG_FOO不是y和m,那么该文件就不会被编译联接了。


3.4.2 编译进内核 - obj-y

Kbuild Makefile 规定所有编译进内核的目标文件都存在$(obj-y)列表中。而这些列表依赖内核的配置。

Kbuild编译所有的$(obj-y)文件。然后,调用"$(LD) -r"将它们合并到一个build-in.o文件中。稍后,该build-in.o会被其父Makefile联接进vmlinux中。

$(obj-y)中的文件是有顺序的。列表中有重复项是可以的:当第一个文件被联接到built-in.o中后,其余文件就被忽略了。联接也是有顺序的,那是因为有些函数(module_init()/__initcall)将会在启动时按照他们出现的顺序进行调用。所以,记住改变联接的顺序可能改变你SCSI控制器的检测顺序,从而导致你的硬盘数据损害。

举例说明obj-y:


3.4.3 编译可装载模块 - obj-m

$(obj-m) 列举出了哪些文件要编译成可装载模块。

一个模块可以由一个文件或多个文件编译而成。如果是一个源文件,Kbuild Makefile只需简单的将其加到$(obj-m)中去就可以了。例如:

注意:此例中 $(CONFIG_ISDN_PPP_BSDCOMP) 的值为'm'

如果内核模块是由多个源文件编译而成,那你就要采用上面那个例子一样的方法去声明你所要编译的模块。Kbuild需要知道你所编译的模块是基于哪些文件,所以你需要通过变量$(<module_name>-objs)来告诉它。例如:

在这个例子中,模块名将是isdn.o,Kbuild将编译在$(isdn-objs)中列出的所有文件,然后使用"$(LD) -r"生成isdn.o。

Kbuild能够识别用于组成目标文件的后缀-objs和后缀-y。这就让Kbuild Makefile可以通过使用 CONFIG_ 符号来判断该对象是否是用来组合对象的。例如:

在这个例子中,如果 $(CONFIG_EXT2_FS_XATTR) 是 'y',xattr.o将是复合对象ext2.o的一部分。

注意:当然,当你要将其编译进内核时,上面的语法同样适用。所以,如果你的CONFIG_EXT2_FS=y,那Kbuild会按你所期望的那样,生成ext2.o文件,然后将其联接到built-in.o中。


3.4.4目标库文件 - lib-y

在obj-*中所列文件是用来编译模块或者是联接到特定目录中的built-in.o。同样,也可以列出一些将被包含在lib.a库中的文件。在 lib-y 中所列出的文件用来组成该目录下的一个库文件。在obj-y与lib-y中同时列出的文件,因为都是可以访问的,所以该文件是不会被包含在库文件中的。同样的情况,lib-m中的文件就要包含在lib.a库文件中。

注意,一个Kbuild makefile可以同时列出要编译进内核的文件与要编译成库的文件。所以,在一个目录里可以同时存在built-in.o与lib.a两个文件。例如:

这将由 checksum.o 和delay.o 两个文件创建一个库文件 lib.a。为了让Kbuild真正认识到这里要有一个库文件lib.a要创建,其所在的目录要加到libs-y列表中。


3.4.5递归向下访问目录

一个Makefile只对编译所在目录的对象负责。在子目录中的文件的编译要由其所在的子目录的Makefile来管理。只要你让Kbuild知道它应该递归操作,那么该系统就会在其子目录中自动的调用 make 递归操作。

这就是obj-y和obj-m的作用。比如ext2被放的一个单独的目录下,在fs目录下的Makefile会告诉Kbuild使用下面的赋值进行向下递归操作。

如果 CONFIG_EXT2_FS 被设置为 'y'(编译进内核)或是'm'(编译成模块),相应的 obj- 变量就会被设置,并且Kbuild就会递归向下访问 ext2 目录。Kbuild只是用这些信息来决定它是否需要访问该目录,而具体怎么编译由该目录中的Makefile来决定。将 CONFIG_ 变量设置成目录名是一个好的编程习惯。这让Kbuild在完全忽略那些相应的CONFIG_ 值不是'y'和'm'的目录。


3.4.6编辑标志

编辑标记包括:EXTRA_CFLAGS,、EXTRA_AFLAGS、EXTRA_LDFLAGS、EXTRA_ARFLAG。所有的EXTRA_变量只在所定义的Kbuild Makefile中起作用。EXTRA_变量可以在Kbuild Makefile中所有命令中使用。

$(EXTRA_CFLAGS) 是用 $(CC) 编译C源文件时的选项。例如:

该变量是必须的,因为顶层Makefile拥有变量 $(CFLAGS) 并用来作为整个源代码树的编译选项。

$(EXTRA_AFLAGS) 也是一个针对每个目录的选项,只不过它是用来编译汇编源代码的。例如:

$(EXTRA_LDFLAGS)和$(EXTRA_ARFLAGS)分别与$(LD)和$(AR)类似,只不过它们是针对每个目录的。例如:

CFLAGS_$@, AFLSGA_$@

CFLAGS_$@ 和 AFLAGS_$@ 只能在当前Kbuild Makefile中的命令中使用。

$(CFLAGS_$@) 是 $(CC) 针对每个文件的选项。$@ 表明了具体操作的文件。例如:

以上三行分别设置了aha152x.o,gdth.o 和 seagate.o的编辑选项。

$(AFLAGS_$@) 也类似,只不是是针对汇编语言的。例如:


3.4.7依赖跟踪

Kbuild 跟踪在以下方面依赖:

1) 所有要参与编译的文件(所有的.c 和.h文件)

2) 在参与编译文件中所要使用的 CONFIG_ 选项

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

因此,如果你改变了 $(CC) 的选项,所有受影响的文件都要重新编译。


3.4.8特殊规则

特殊规则就是那Kbuild架构不能提供所要求的支持时,所使用的规则。一个典型的例子就是在构建过程中生成的头文件。另一个例子就是那些需要采用特殊规则来准备启动镜像。

特殊规则的写法与普通Make规则一样。Kbuild并不在Makefile所在的目录执行,所以所有的特殊规则都要提供参与编译的文件和目标文件的相对路径。

在定义特殊规则时,要使用以下两个变量:$(src)和$(obj)。$(src) 表明Makefile所在目录的相对路径。经常在定位源代码树中的文件时,使用该变量。$(obj) 表明目标文件所要存储目录的相对路径。经常在定位所生成的文件时,使用该变量。例如:

这就是一个特殊规则,遵守着make所要求的普通语法。

你可能感兴趣的:(Linux Kbuild文档 3)