vmlinux.o

Makefile 对应内核版本:2.6.35.13

vmlinux.o 是生成 vmlinux 的依赖之一,在链接出 vmlinux 之前会先链接出 vmlinux.o 。

vmlinux.o 定义在顶层 Makefile 中:
vmlinux.o: $(modpost-init) $(vmlinux-main) FORCE
        $(call if_changed_rule,vmlinux-modpost)


上面 $(modpost-init) 定义在顶层 Makefile 中:
modpost-init := $(filter-out init/built-in.o, $(vmlinux-init))

它只是从 $(vmlinux-init) 中去掉 init/built-in.o 文件。比如在默认的配置下,$(vmlinux-init) 指代的生成文件为:
arch/x86/kernel/head_32.o arch/x86/kernel/head32.o arch/x86/kernel/head.o arch/x86/kernel/init_task.o init/built-in.o

则 $(modpost-init) 就是:
arch/x86/kernel/head_32.o arch/x86/kernel/head32.o arch/x86/kernel/head.o arch/x86/kernel/init_task.o


在 vmlinux.o 底下的 if_changed_rule 函数和 if_changed 函数类似,它会使构建系统调用 rule_vmlinux-modpost --- 该变量也定义在顶层 Makefile 中:
define rule_vmlinux-modpost
        :
        +$(call cmd,vmlinux-modpost)
        $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $@
        $(Q)echo 'cmd_$@ := $(cmd_vmlinux-modpost)' > $(dot-target).cmd
endef

一开始就使用 $(call cmd,vmlinux-modpost) 即为 cmd_vmlinux-modpost ,它也定义在顶层 Makefile 中:
cmd_vmlinux-modpost = $(LD) $(LDFLAGS) -r -o $@                          \
         $(vmlinux-init) --start-group $(vmlinux-main) --end-group             \
         $(filter-out $(vmlinux-init) $(vmlinux-main) FORCE ,$^)

上面 $(LD) 即为 ld 链接命令。
$(LDFLAGS) 指代 -m elf_i386 (编译内核在 x86 平台,如果是交叉编译,则 -m 选项后会有相应更改)。
ld 的 --start-group 和 --end-group 之间放置目标文件列表,ld 会对列表中列出的目标文件进行重复的搜索,目的是查看各个文件之间再也没有未决的互相引用,当然这么做是相当耗时的,除非是非做不可。自然的,对于内核而言,这非做不可。
接着 $(filter-out $(vmlinux-init) $(vmlinux-main) FORCE ,$^) 是从所有的依赖中去掉伪目标 FORCE,$(vmlinux-init) 和 $(vmlinux-main) 这几个依赖后看看还有什么别的依赖。默认配置情况下没有,所以这里为空。

在链接出 vmlinux.o 目标后,接着使用 scripts/Makefile.modpost 这个文件来处理 vmlinux.o 目标。跟到  scripts/Makefile.modpost 中看到:
vmlinux.o: FORCE
        $(call cmd,kernel-mod)

所以执行的是 cmd_kernel-mod 中的语句,这些语句也在 scripts/Makefile.modpost 中:
quiet_cmd_kernel-mod = MODPOST $@
      cmd_kernel-mod = $(modpost) $@

其中  $(modpost) 的定义为:
# Step 2), invoke modpost
#  Includes step 3,4
modpost = scripts/mod/modpost                    \
 $(if $(CONFIG_MODVERSIONS),-m)                  \
 $(if $(CONFIG_MODULE_SRCVERSION_ALL),-a,)       \
 $(if $(KBUILD_EXTMOD),-i,-o) $(kernelsymfile)   \
 $(if $(KBUILD_EXTMOD),-I $(modulesymfile))      \
 $(if $(KBUILD_EXTRA_SYMBOLS), $(patsubst %, -e %,$(KBUILD_EXTRA_SYMBOLS))) \
 $(if $(KBUILD_EXTMOD),-o $(modulesymfile))      \
 $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S)      \
 $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \
 $(if $(cross_build),-c)

由上可见,这里实际使用的是  scripts/mod/ 目录下的 modpost 用来解析 vmlinux.o 对象文件,它检查目标中的 sections 是否 mis match,然后还将基本内核导出的所有符号都记录到文件 Module.symvers 中去。详情可以看 Makefile.modpost 中的注释以及参考 scripts/mod/modpost.c 中的代码。

在上面的诸多判断中,在默认的内核配置下,CONFIG_MODVERSION,CONFIG_MODULE_SRCVERSION,KBUILD_EXTMOD,KBUILD_EXTRA_SYMBOLS,CONFIG_DEBUG_SECTION_MISMATCH 以及 KBUILD_MODPOST_WARN 这些变量都为空,所以最后综合起来得到的命令为:
scripts/mod/modpost -o /home/beyes/Downloads/linux-2.6.35.13/Module.symvers -S vmlinux.o


上面调用 modpost 处理完后,再返回到主 Makefile 中,使用   $(Q)echo 'cmd_$@ := $(cmd_vmlinux-modpost)' > $(dot-target).cmd 将编译 vmlinux.o 的命令写入到.vmlinux.o.cmd 文件中保存起来,以便下次再编译内核时可以进行新旧命令的比较。
Logo

更多推荐