内核映像的形成——递归编译各对象
2.2.4 递归编译各对象
当prepare和scripts目标执行完后,就会去执行$(vmlinux-dirs)目标的命令:
883 $(vmlinux-dirs): prepare scripts
884 $(Q)$(MAKE) $(build)=$@
885 ifdef CONFIG_MODULES
886 $(Q)$(MAKE) $(modbuiltin)=$@
887 endif
别看这小小的5行代码,两条命令,整个Makefile最重要最核心的部分就是这里了。不过呢,只要熟悉了前面提到的KBuild体系,弄懂这里是易如反掌的。其实这里最难搞清楚的,就是$(vmlinux-dirs)变量的值,也就是形成vmlinux所需要哪些目录。下面,我们就来重点考察$(vmlinux-dirs)这个变量。它第一次被定义在655行:
655 vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) /
656 $(core-y) $(core-m) $(drivers-y) $(drivers-m) /
657 $(net-y) $(net-m) $(libs-y) $(libs-m)))
init-y、core-y 、drivers-y、net-y、libs-y这五个变量来自477到481行:
477 init-y := init/
478 drivers-y := drivers/ sound/ firmware/
479 net-y := net/
480 libs-y := lib/
481 core-y := usr/
init-m、core-m 、drivers-m 、net-m 、libs-m没有定义,为空。那么针对前面那五个,一个一个来看,首先init-y,除了477行定义,还有664行:
664 init-y := $(patsubst %/, %/built-in.o, $(init-y))
注意,664行开始的一连串定义是被包含在了ifeq ($(KBUILD_EXTMOD),)条件中,我们的make满足,所以664行定义也满足,那么init-y的值是什么呢?学知识了,讲讲patsubst函数。
模式字符串替换函数——patsubst:
$(patsubst <pattern>,<replacement>,<text>)
它的功能是查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement>中的这个“%”将是<pattern>中的那个“%”所代表的字串。(可以用“/”来转义,以“/%”来表示真实含义的“%”字符)。
所以这里,init-y的值就是init/built-in.o,这个文件是不存在,Kbuild将编译所有的$(obj-y)文件,然后,调用"$(LD) -r"将它们合并到当前目录的build-in.o文件中。稍后,该build-in.o会被顶层Makefile链接进vmlinux中。
再来看core-y,481行第一次定义为usr/,随后653行:
653 core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
也就是说最核心的目录名字符串就是core-y。完了吗?别忘了我们在535行include了一个 $(srctree)/arch/$(SRCARCH)/Makefile,找到arch/x86/Makefile,126行:
126 core-y += arch/x86/
回到顶层Makefile然后665行,
665 core-y := $(patsubst %/, %/built-in.o, $(core-y))
所以core-y最后的值是:usr/built-in.o arch/x86/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
继续走,drivers-y,478行第一次定义之后,随后在666行:
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
所以最后drivers-y的值是drivers/built-in.o sound/built-in.o firmware/built-in.o。好了,我们再来考察net-y变量,479行定义后,667行:
667 net-y := $(patsubst %/, %/built-in.o, $(net-y))
net-y的最终值是net/built-in.o。libs-y的分析方法跟他们类似,最后是lib/lib.a lib/built-in.o。综上所述,vmlinux-dirs的值再经过filter和patsubst两个函数一折腾,就成了这么一串字符串:
init usr arch/x86 kernel mm fs ipc security crypto block drivers sound firmware net lib lib
好了,vmlinux-dirs变量我们理顺了,883到887行的代码就很好理解了,参照前面讲的Kbuild体系的知识,对上面提到的14个目录进行编译与链接,生成对应的built-in.o文件。我们随便找一个目录,比如说第一个init,翻译成命令就是:
@make -f scripts/Makefile.build obj=init
首先看它的Makefile,先编译obj-y的那些对象,我们看到第5到11行:
obj-y := main.o version.o mounts.o
说明该init目录下肯定会有三个目标被编译的,他们是main.o version.o mounts.o;还有一些目标是可能会被编译的,如noinitramfs.o,取决于编译配置了CONFIG_BLK_DEV_INITRD选项;那么,肯定会被编译的东西中,version.o和mounts.o两个东西又很讨厌,前一个需要依赖一个头文件,后一个又需要另外一些对象链接而成。所以经过一系列编译和链接后会在init目录中生成built-in.o文件。这就是我们在KBuild体系中讲过的,编译本目录中所有的obj-y所包含文件,然后,调用"$(LD) -r"将它们合并到一个build-in.o文件中。
我们再看一个难一点的,看到fs文件夹。还是首先看它的Makefile,8到14行,先编译obj-y的那些对象,所以我们看到open.o read_write.o file_table.o等这些对象是必被编译的。16行以后是根据配置编译相应的对象。注意,如果对象是一个目录(以“/”结束的),比如:
72 obj-$(CONFIG_EXT4_FS) += ext4/
其实就相当于:
@make -f scripts/Makefile.build obj= ext4
那么,我们就去ext4目录中去找他的Makefile,这个文件很简单,我把全部内容先列出来:
obj-$(CONFIG_EXT4_FS) += ext4.o
ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o /
ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o /
ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o
ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
我们看到,根据CONFIG_EXT4_FS的值是y还是m,将ext4.o对象编译进内核或模块。而ext4.o对象又是如何编译得到的呢,后面ext4-y或ext4-m中的对象就告诉了你答案。这里顺便补充一个知识点,前面我们讲到了编译配置,在这儿更进一步,scripts/kconfig/mconf 这个程序读取的是每个目录中的Kconfig文件。这个文件有若干的item,每个item格式如下:
config EXT4_FS_CHEN
bool "Just For Test"
depends on EXT4_FS_XATTR
select FS_CHEN
help
hello, world!.
其中config、bool、depends、select和help是关键字,后四个关键字前必须用[tab]隔开。config后表示配置选项名称,比如我们这里,它最后的名称就是CONFIG_EXT4_FS_CHEN;bool后面是我们make menuconfig后在菜单中看到的item名称;depends表示这个item需要依赖的配置选项名;select表示,如果选择了,那么对应的配置选项CONFIG_FS_CHEN也将被设置;最后help,就是留给内核开发人员的对应配置选项注释信息。
还有一个重要的知识点,当在一个目录中编译完成后,会生成一些以.cmd结尾的隐藏文件,如.built-in.o.cmd。这个表示生成对象的具体命令。比如,init/.built-in.o.cmd的内容如下:
cmd_init/built-in.o := ld -m elf_i386 -r -o init/built-in.o init/main.o init/version.o init/mounts.o init/initramfs.o init/calibrate.o
就是指built-in.o这个对象,是通过ld命令链接nit/main.o、init/version.o、init/mounts.o、init/initramfs.o和init/calibrate.o而生成的。我也尝试着看了看.calibrate.o.cmd文件的内容,很长,说明生成.calibrate.o的命令很长,也很复杂,对编译感兴趣的朋友可以去分析一下,很有挑战性的哟。
在递归向下访问所有在下列变量中列出的目录: init-* core* drivers-* net-* libs-*,并编译生成目标文件built-in.o后,下一步就是在源代码树顶层目录中生成 vmlinux。前面“寻找第一个目标”讲了,在849行:
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
是vmlinux目标的各个依赖,再看到874行:
$(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ;
更多推荐
所有评论(0)