Linux4.4内核构建脚本分析(一)- vmlinux的构建

Vmlinux

Vmlinux的目标定义在顶层Makefile文件中

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

    +$(call if_changed,link-vmlinux)

If_changed是定义在scripts/Kbuild.include中的函数

# Execute command if command has changed or prerequisite(s) are updated.

if_changed = $(if $(strip $(any-prereq) $(arg-check)),                       \

  @set -e;                                                             \

  $(echo-cmd) $(cmd_$(1));                                             \

  printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)

核心的代码是 $(cmd_$(1)) , 展开之后$(cmd_link-vmlinux), 也就是调用cmd_link-vmlinux函数。cmd_link-vmlinux函数的定义在顶层Makefile

# Final link of vmlinux with optional arch pass after final link

cmd_link-vmlinux =                                                 \

  $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) ;    \

  $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)

第一行代码中$<代表第一个依赖项, 这行代码展开之后是

sh  scripts/link-vmlinux.sh $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)

其实就是调用scripts/link-vmlinux.sh这个脚本来完成vmlinux的构建工作, 这个脚本的分析就不在这里展开。 vmlinux生成时需要链接内核各个目录下built-in.o文件,所以各个目录built-in.o文件需要提前编译出来,而这些.o文件是通过一个叫$(vmlinux-dirs)定义的伪目标来驱动产生的 。

$(vmlinux-dirs)

$(vmlinux-dirs)伪目标定义在顶层Makefile中

$(vmlinux-dirs): prepare scripts

    $(Q)$(MAKE) $(build)=$@

$(vmlinux-dirs)的值展开后就是内核源文件的各个子目录

例如

init usr arch/arm/vfp arch/arm/kvm arch/arm/vdso arch/arm/kernel arch/arm/mm arch/arm/common arch/arm/probes arch/arm/net arch/arm/crypto arch/arm/firmware arch/arm/mach-sunxi kernel certs mm fs ipc security crypto block drivers sound firmware net arch/arm/lib lib virt

而构建命令$(Q)$(MAKE) $(build)=$@,其中的$(build)定义在scripts/Kbuild.include文件中。

###

# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=

# Usage:

# $(Q)$(MAKE) $(build)=dir

build := -f $(srctree)/scripts/Makefile.build obj

$@代表当前的目标项, 以init目录为例, 展开后就是

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

所以这里通过使用./scripts/Makefile.build编译脚本, 传递需要编译的目录作为参数, 来分别构建需要构建的目录下的built-in.o

Makefile.build

这个脚本的默认目标是__build

# We keep a list of all modules in $(MODVERDIR)

__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \

 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \

 $(subdir-ym) $(always)

    @:

其中依赖项$(builtin-target)的定义是

ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)

builtin-target := $(obj)/built-in.o

endif

所以只要这些$(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target))变量的值不为空的话,$(obj)/built-in.o就会成为一个需要构建的依赖项。

$(builtin-target): $(obj-y) FORCE

    $(call if_changed,link_o_target)

$(call if_changed,link_o_target)展开之后就是调用命令

# If the list of objects to link is empty, just create an empty built-in.o

cmd_link_o_target = $(if $(strip $(obj-y)),\

      $(cmd_make_builtin) $@ $(filter $(obj-y), $^) \

      $(cmd_secanalysis),\

      $(cmd_make_empty_builtin) $@)

核心代码是$(cmd_make_builtin) $@ $(filter $(obj-y), $^) ,以init目录的编译为例, 展开后是

 rm -f init/built-in.o; arm-linux-ar rcSTPD init/built-in.o init/main.o init/version.o init/mounts.o init/initramfs.o init/calibrate.o init/init_task.o

$@代表目标init/built-in.o, $(filter $(obj-y), $^)其实就是$(obj-y), Makefile.build会包含要编译目录下的Makefile文件

# The filename Kbuild has precedence over Makefile

kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))

kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)

include $(kbuild-file)

obj-y就是在init目录下的Makefile文件里面定义的

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

ifneq ($(CONFIG_BLK_DEV_INITRD),y)

obj-y                          += noinitramfs.o

else

obj-$(CONFIG_BLK_DEV_INITRD)   += initramfs.o

endif

obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o

ifneq ($(CONFIG_ARCH_INIT_TASK),y)

obj-y                          += init_task.o

endif

而各个.o文件的编译规则定义在

# Built-in and composite module parts

$(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE

    $(call cmd,force_checksrc)

    $(call if_changed_rule,cc_o_c)

$(call if_changed_rule,cc_o_c)展开后调用rule_cc_o_c

define rule_cc_o_c

  $(call echo-cmd,checksrc) $(cmd_checksrc)   \

  $(call cmd_and_fixdep,cc_o_c)   \

  $(call echo-cmd,objtool) $(cmd_objtool)   \

  $(cmd_modversions_c)   \

  $(call echo-cmd,record_mcount) $(cmd_record_mcount)

endef

核心代码$(call cmd_and_fixdep,cc_o_c), 其中cmd_and_fixdep定义是

cmd_and_fixdep =                                                             \

  $(echo-cmd) $(cmd_$(1));                                             \

  scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;\

  rm -f $(depfile);                                                    \

  mv -f $(dot-target).tmp $(dot-target).cmd;

最后展开后还是调用 cmd_cc_o_c

cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<

$(c_flags)的定义在scripts/Makefile.lib文件中

c_flags        = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE)     \

   $(__c_flags) $(modkern_cflags)                           \

   $(basename_flags) $(modname_flags)

__c_flags的相关定义在

__c_flags = $(if $(obj),$(call addtree,-I$(src)) -I$(obj)) \

  $(call flags,_c_flags)

orig_c_flags   = $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(KBUILD_SUBDIR_CCFLAGS) \

                 $(ccflags-y) $(CFLAGS_$(basetarget).o)

_c_flags       = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags))

在编译的目录下面Makefile文件也可以修改$(ccflags-y) 调整编译参数

ccflags-y := -fno-function-sections -fno-data-sections

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值