vmlinux是如何炼成的--kernel makefile

引子

kernel的makefile包含的内容还真是多,我就是想看看要是我自己添加一个目录编译到内核里,要怎么做。

就是这么个不起眼的实验,引发了一堆的故事。


最简单的例子

添加 一个目录,叫test, 添加了test.c 和 Makefile。

文件内容很简单,如下。

cat Makefile

#
# Makefile for the linux kernel makefile experiment.
#


obj-y := test.o

cat test.c

#include

int test_global = 0;


然后在主 Makefile中 添加

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


最后 make test

make[1]: Nothing to be done for `all'.
  HOSTCC  arch/x86/tools/relocs
  CHK     include/linux/version.h
  CHK     include/generated/utsrelease.h
  CC      kernel/bounds.s
  GEN     include/generated/bounds.h
  CC      arch/x86/kernel/asm-offsets.s
  GEN     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  CC      test/test.o
  LD      test/built-in.o

恩,不错,可以了。


vmlinux是如何炼成的

人总是不知足的,我又开始好奇,这个build的过程究竟是怎么个回事。


好吧,我们知道make后,最终的结果叫vmlinux,那我们就找找这个神奇的东西是怎么

产生的吧。 


vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)


$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;


vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE

ifdef CONFIG_HEADERS_CHECK
    $(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
ifdef CONFIG_SAMPLES
    $(Q)$(MAKE) $(build)=samples
endif
ifdef CONFIG_BUILD_DOCSRC
    $(Q)$(MAKE) $(build)=Documentation
endif
    +$(call if_changed,link-vmlinux)


vmlinx 基于上面三个目标, 而vmlinux-deps又基于 $(vmlinux-dirs)。 恩,好复杂。

那来看看vmlinux-dirs都包含什么吧。


在主Makefile中看到下面的内容。

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

vmlinux-dirs    := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
             $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
             $(net-y) $(net-m) $(libs-y) $(libs-m)))

$(vmlinux-dirs): prepare scripts
    $(Q)$(MAKE) $(build)=$@

恩, 我们把vmlinux-dirs的东东打印出来看看。

vmlinux-dris: init usr arch/x86 kernel mm fs ipc security crypto block test drivers sound firmware arch/x86/pci arch/x86/power arch/x86/video arch/x86/oprofile net lib arch/x86/lib


这样,你是不是明白点了呢。 这些都是kernel源代码中子目录。也就是kernel将要挨个的

进入每个子目录,编译~。


那最后这个vmlinux是怎么生成的呢? 怎么样将每个目录下生成的模块链接成一个vmlinux的文件的呢?

看到上面vmlinux目标中,最后一个命令:


+$(call if_changed,link-vmlinux)


哦,原来是调用了cmd_link-vmlinux。这个命令就定义在主Makefile中。

      cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)

打出来看看,长这样。

      /bin/bash $<  ld -m elf_i386 --emit-relocs --build-id

$< 表示第一个以来目标,那么在vmlinux目标中,第一个目标是 scripts/link-vmlinux.sh, 展开后就成为。

/bin/bash scripts/link-vmlinux.sh ld -m elf_i386 --emit-relocs --build-id


额,原来是又调用了一个脚本。。。 好吧, 再进去看看。  发现这么个东东

info LD vmlinux
vmlinux_link "${kallsymso}" vmlinux


vmlinux_link()
{
    local lds="${objtree}/${KBUILD_LDS}"

    if [ "${SRCARCH}" != "um" ]; then
        ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2}                  \
            -T ${lds} ${KBUILD_VMLINUX_INIT}                     \
            --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}
    else
        ${CC} ${CFLAGS_vmlinux} -o ${2}                              \
            -Wl,-T,${lds} ${KBUILD_VMLINUX_INIT}                 \
            -Wl,--start-group                                    \
                 ${KBUILD_VMLINUX_MAIN}                      \
            -Wl,--end-group                                      \
            -lutil ${1}
        rm -f linux
    fi
}

好吧,原来是调用了这个函数。。。 打出来看看吧。

ld -m elf_i386 --emit-relocs --build-id -o vmlinux -T arch/x86/kernel/head_32.o arch/x86/kernel/head32.o arch/x86/kernel/head.o init/built-in.o --start-group 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 test/built-in.o lib/lib.a arch/x86/lib/lib.a lib/built-in.o arch/x86/lib/built-in.o drivers/built-in.o sound/built-in.o firmware/built-in.o arch/x86/pci/built-in.o arch/x86/power/built-in.o arch/x86/video/built-in.o net/built-in.o --end-group .tmp_kallsyms2.o

恩,原来真相是这样的。 最后把这么多东西链接起来成为vmlinux。 看到我们添加的test目录了么,它也生成了一个built-in.o,最后链接到了vmlinux中。


$ nm vmlinux | grep test_global
c198d284 B test_global

啊哦,还真有这个symbol!


神秘的built-in.o


在最后的链接过程中,我们可以看到,几乎所有的依赖条件中,都会生成一个built-in.o的文件。 那这个文件,是怎么生成的呢?

$(vmlinux-dirs): prepare scripts
    $(Q)$(MAKE) $(build)=$@


从这个规则中可以看到,vmlinux-dir目标是通过下面的make来生成的。展开一下看看。 这个build在scripts/Kbuild.include中。

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

对应到test目录 那就是

make -f scripts/Makefile.build obj=test


这么看来,就要进到scripts/Makefile.build文件了。

PHONY := __build
__build:


__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
     $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
     $(subdir-ym) $(always)
    @:

__build是这个makefile的第一个目标,也是默认的目标。 这里面藏着一个builtin-target,看着很像,再搜搜。


ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif


恩 原来这个就是这么多叫built-in.o的原因。但是要生成buit-in.o,必须要以上的这些变量不能全部为空。


那再来看看编译这个built-in.o的规则是什么

quiet_cmd_link_o_target = LD      $@
# If the list of objects to link is empty, just create an empty built-in.o
cmd_link_o_target = $(if $(strip $(obj-y)),\
              $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \
              $(cmd_secanalysis),\
              rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@)

$(builtin-target): $(obj-y) FORCE
    $(call if_changed,link_o_target)

targets += $(builtin-target)

恩,基本明白了,就是当obj-y的内容不为空,那么就用ld来链接成一个built-in.o。

但是我试了一下,如果没有obj-y那么,也会生成一个built-in.o,但是用的是别的命令。

如在i386下,用的是

rm -f test/built-in.o; ar rcsD test/built-in.o


不知道这个是什么高级玩意。


好了,到此为止,基本上一个最上层的框架有了一个概念。 那就先休息一下~










你可能感兴趣的:(Linux,kernel,makefile)