2.2.4 递归编译各对象

preparescripts目标执行完后,就会去执行$(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-ycore-y drivers-ynet-ylibs-y这五个变量来自477481行:

477 init-y              := init/

478 drivers-y  := drivers/ sound/ firmware/

479 net-y        := net/

480 libs-y              := lib/

481 core-y             := usr/

 

init-mcore-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-y481行第一次定义为usr/,随后653行:

653 core-y             += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

也就是说最核心的目录名字符串就是core-y。完了吗?别忘了我们在535include了一个 $(srctree)/arch/$(SRCARCH)/Makefile,找到arch/x86/Makefile126行:

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-y478行第一次定义之后,随后在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.olibs-y的分析方法跟他们类似最后是lib/lib.a lib/built-in.o。综上所述,vmlinux-dirs的值再经过filterpatsubst两个函数一折腾,就成了这么一串字符串:

init usr arch/x86 kernel mm fs ipc security crypto block drivers sound firmware net lib lib

好了,vmlinux-dirs变量我们理顺了,883887行的代码就很好理解了,参照前面讲的Kbuild体系的知识,对上面提到的14个目录进行编译与链接,生成对应的built-in.o文件。我们随便找一个目录,比如说第一个init,翻译成命令就是:

@make -f scripts/Makefile.build obj=init

 

首先看它的Makefile,先编译obj-y的那些对象,我们看到第511行:

obj-y                          := main.o version.o mounts.o

说明该init目录下肯定会有三个目标被编译的,他们是main.o version.o mounts.o;还有一些目标是可能会被编译的,如noinitramfs.o,取决于编译配置了CONFIG_BLK_DEV_INITRD选项;那么,肯定会被编译的东西中,version.omounts.o两个东西又很讨厌,前一个需要依赖一个头文件,后一个又需要另外一些对象链接而成。所以经过一系列编译和链接后会在init目录中生成built-in.o文件。这就是我们在KBuild体系中讲过的,编译本目录中所有的obj-y所包含文件,然后,调用"$(LD) -r"将它们合并到一个build-in.o文件中。

 

我们再看一个难一点的,看到fs文件夹。还是首先看它的Makefile814行,先编译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-yext4-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!.

 

其中configbooldependsselecthelp是关键字,后四个关键字前必须用[tab]隔开。config后表示配置选项名称,比如我们这里,它最后的名称就是CONFIG_EXT4_FS_CHENbool后面是我们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.oinit/version.oinit/mounts.oinit/initramfs.oinit/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) ;

 

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐