KBUILD系统原理分析
当执行 \时,\这两个文件将被删除。如果不使用绝对路径(路径以'/'开头)的话,Kbuild假设所要删除的文件与Makefile在同一个相对路径上。
要删除一目录:
#scripts/package/Makefile clean-dirs := $(objtree)/debian/
这就会删除目录 debian,包括其所有的子目录。如果不使用绝对路径(路径以'/'开头)的话,Kbuild假设所要删除的目录与Makefile在同一个相对路径上。
一般情况下,Kbuild会根据 \递归访问其子目录,但有的时候,Kbuild架构还不足以描述所有的情况时,还要显式的指明所要访问的子目录。例子:
#arch/i386/boot/Makefile subdir- := compressed/
上面的赋值命令告诉Kbuild,当执行\时,要递归访问目录 compressed/。 为了支持在最终编译完成启动镜像后的架构清理工作,还有一可选的目标 archclean: #arch/i386/Makefile archclean:
$(Q)$(MAKE) $(clean)=arch/i386/boot
当\执行时,make会递归访问并清理 arch/i386/boot。在 arch/i386/boot中的Makefile可以用来提示make进行下一步的递归操作。
注意1:arch/$(ARCH)/Makefile 不能使用\,因为该Makefile被包含在顶层的Makefile中,Kbuild是不会在此处进行操作的。
注意2:\会访问在 core-y,libs-y,drivers-y 和 net-y 列出的所有目录。
2.5架构Makefile
在递归访问目录之前,顶层Makefile要完成设置环境变量以及递归访问的准备工作。顶层Makefile包含的公共部分,而 arch/$(ARCH)/Makefile 包含着针对某一特定架构的配置信息。所以,要在arch/$(ARCH)/Makefile 中设置一部分变量,并定义一些目标。
Kbuild执行的几个步骤(大致):
1) 根据内核配置生成文件 .config
2) 将内核的版本号存储在 include/linux/version.h 3) 生成指向 include/asm-$(ARCH) 的符号链接
4) 更新所有编译所需的文件: 附加的文件由 arch/$(ARCH)/Makefile 指定。
5) 递归向下访问所有在下列变量中列出的目录: init-* core* drivers-* net-* libs-*,并编译生成目标文件。这些变量的值可以在 arch/$(ARCH)/Makefile 中扩充。
6) 联接所有的目标文件,在源代码树顶层目录中生成 vmlinux。最先联接是在 head-y中列出的文件,该变量由 arch/$(ARCH)/Makefile 赋值。 7) 最后完成具体架构的特殊要求,并生成最终的启动镜像。 -- 包含生成启动指令 16
KBUILD系统原理分析
-- 准备 initrd 镜像或类似文件
2.5.1 调整针对某一具体架构生成的镜像
LDFLAGS 一般是 $(LD) 选项 该选项在每次调用联接器时都会用到。 一般情况下,只用来指明模拟器。例子: #arch/s390/Makefile LDFLAGS := -m elf_s390
注意:EXTRA_LDFLAGS 和 LDFLAGS_$@ 可用来进一步自定义选项。
LDFLAGS_MODULE 联接模块时的联接器的选项
LDFLAGS_MODULE 所设置的选项将在联接器在联接模块文件 .ko 时使用。默认值为 \,指定输出文件是可重定位的。
LDFLAGS_vmlinux 联接vmlinux时的选项
LDFLAGS_vmlinux用来传递联接vmlinux时的联接器的选项。 LDFLAGS_vmlinux需 LDFLAGS_$@ 支持。例子: #arch/i386/Makefile
LDFLAGS_vmlinux := -e stext
OBJCOPYFLAGS objcopy 选项
当用 $(call if_changed,objcopy) 来转换(translate)一个.o文件时,该选项就会被使用。 $(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_chagned,xxx)的用法稍后描述。
AFLAGS $(AS) 汇编编译器选项 默认值在顶层Makefile
扩充或修改在各具体架构的Makefile,例子: #arch/sparc64/Makefile
AFLAGS += -m64 -mcpu=ultrasparc
CFLAGS $(CC) 编译器选项 默认值在顶层Makefile
17
KBUILD系统原理分析
扩充或修改在各具体架构的Makefile。 一般,CFLAGS要根据内核配置设置。例子: #arch/i386/Makefile
cflags-$(CONFIG_M386) += -march=i386 CFLAGS += $(cflags-y)
许多架构Makefile都通过调用所要使用的C编译器,动态的检查其所支持的选项: #arch/i386/Makefile ...
cflags-$(CONFIG_MPENTIUMII) += $(call cc-option,\\ -march=pentium2,-march=i686) ...
# Disable unit-at-a-time mode ...
CFLAGS += $(call cc-option,-fno-unit-at-a-time) ...
第一个例子利用了一个配置选项,当其为'y'时,扩展。 CFLAGS_KERNEL : #arch/i386/Makefile ...
cflags-$(CONFIG_MPENTIUMII) += $(call cc-option,\\ -march=pentium2,-march=i686) ...
# Disable unit-at-a-time mode ...
CFLAGS += $(call cc-option,-fno-unit-at-a-time) ...
第一个例子利用了一个配置选项,当其为'y'时,扩展。
CFLAGS_KERNEL 编译进内核时,$(CC) 所用的选项
$(CFLAGS_KERNEL) 包含了用于编译常驻内核代码的附加编译器选项。
CFLAGS_MODULE 编译成模块时,$(CC)所用的选项
$(CFLAGS_MODULE) 包含了用于编译可装载模块的附加编译器选项。
2.5.2 将所需文件加到 archprepare 中:
archprepare规则在递归访问子目录之前,列出编译目标文件所需文件。 一般情况下,这是一个包含汇编常量的头文件。(assembler constants) 例子:
#arch/arm/Makefile archprepare: maketools
此例中,目标文件 maketools 将在递归访问子目录之前编译。在TODO一章可以看到,Kbuild
18
KBUILD系统原理分析
是如何支持生成分支头文件的。(offset header files)
2.5.3 递归下向时要访问的目录列表
如何生成 vmlinux,是由架构makefile和顶层Makefile一起来定义的。注意,架构Makefile是不会定义与模块相关的内容的,所有构建模块的定义是与架构无关的。
head-y, init-y, core-y, libs-y, drivers-y, net-y $(head-y) 列出了最先被联接进 vmlinux 的目标文件。 $(libs-y) 列出了生成的所有 lib.a 所在的目录。 其余所列的目录,是 built-in.o 所在的目录。 $(init-y) 在 $(head-y) 之后所要使用的文件。 然后,剩下的步骤如下:
$(core-y),$(libs-y),$(drivers-y)和$(net-y)。
顶层makefile定义了通用的部分,arch/$(ARCH)/Makefile 添加了架构的特殊要求。 例子:
#arch/sparc64/Makefile core-y += arch/sparc64/kernel/
libs-y += arch/sparc64/prom/ arch/sparc64/lib/
drivers-$(CONFIG_OPROFILE) += arch/sparc64/oprofile/
2.5.4 具体架构的启动镜像
一具体架构Makefile的具体目的就是,将生成并压缩 vmlinux 文件,写入启动代码,并将其拷贝到正确的位置。这就包含了多种不同的安装命令。该具体目的也无法在各个平台间进行标准化。
一般,附加的处理命令入在 arch/$(ARCH)/下的boot目录。
Kbuild并没有为构造boot所指定的目标提供任何更好的方法。所以,arch/$(ARCH)/Makefile 将会调用 make 以手工构造 boot的目标文件。
比较好的方法是,在 arch/$(ARCH)/Makefile 中包含快捷方式,并在arch/$(ARCH)/boot/Makefile 中使用全部路径。例子:
#arch/i386/Makefile boot := arch/i386/boot bzImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
当在子目录中调用 make 时,推荐使用 \。 并没有对架构特殊目标的命名规则,但用命令 \可以列出所有的相关目标。 为了支持 \,$(archhelp) 必须被定义。例子: #arch/i386/Makefile define archhelp
echo '* bzImage - Image (arch/$(ARCH)/boot/bzImage)' endef
19
KBUILD系统原理分析
当make 没带参数执行时,所遇到的第一个目标将被执行。在顶层,第一个目标就是 all: 每个架构Makefile都要默认构造一可启动的镜像文件。 在 \中,默认目标就是被加亮的'*'。
添加一新的前提文件到 all:,就可以构造出一不同的vmlinux。例子: #arch/i386/Makefile all: bzImage
当 make 没有参数时,bzImage将被构造。
2.5.5 构造非Kbuild目标
extra-y
extra-y 列出了在当前目录下,所要创建的附加文件,不包含任何已包含在obj-* 中的文件。 用 extra-y 列目标,主要是两个目的: 1) 可以使Kbuild检查命令行是否发生变化
使用 $(call if_changed,xxx) 的时候
2) 让Kbuild知道哪些文件要在 \时删除 例子:
#arch/i386/kernel/Makefile extra-y := head.o init_task.o
在此例子中,extra-y用来列出所有只编译,但不联接到 built-in.o的目标文件。
2.5.6 构建启动镜像的命令
Kbuild 提供了几个用在构建启动镜像时的宏。
if_changed
if_changed 为下列命令的基础。使用方法: target: source(s) FORCE
$(call if_changed,ld/objcopy/gzip)
当执行该规则时,就检查是否有文件需要更新,或者在上次调用以后,命令行发生了改变。如果有选项发生了改变,后者会导致重新构造。只有在 $(targets)列出的的目标文件,才能使用 if_changed,否则命令行的检查会失败,并且目标总会被重建。给 $(targets)的赋值没有前缀 $(obj)/ 。 if_changed 可用来联接自定义的Kbuild命令,关于Kbuild自定义命令请看 6.7节。
注意:忘记 FORCE 是一种典型的错误。还有一种普遍的错误是,空格有的时候是有意义的;比如。下面的命令就会错误(注意在逗号后面的那个多余的空格):
target: source(s) FORCE
#WRONG!# $(call if_changed, ld/objcopy/gzip)
ld 联接目标
经常是使用LDFLAGS_$@来设置ld的特殊选项。
20