Linux Kernel编译流程 (二)

1 vmlinux

  • 研究vmlinux文件的产生, zImageImage产生
  • Linux Kernel 4.18.20
  • Source Insight 3.5
  • Ubuntu 18.04
  • arm-linux-gnueabi-xxx

1.1 find all target

首先当我们执行sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-命令后, 默认的target是_all,所以我们首先在顶层Makefile中找到_all, 发现如下代码片段, all依赖vmlinux, 而vmlinux 依赖autoksyms_recursive$(vmlinux-deps),
但是此时的all真的是默认的target吗?

_all: all
all: vmlinux

#SRCARCH := arm
SRCARCH 	:= $(ARCH)	
include arch/arm/Makefile
cmd_link-vmlinux =                                                 \
	$(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) ;    \
	$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE
	@echo "eric_debug:......$@ start"
	+$(call if_changed,link-vmlinux)
	@echo "eric_debug:......$@ end" 
$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;

PHONY += $(vmlinux-dirs)
$(vmlinux-dirs): prepare scripts
	$(Q)$(MAKE) $(build)=$@ need-builtin=1

显然不是,在arch/arm/Makefile, 也有如下代码段,其中all依赖 zImage, 而zImage依赖vmlinux, 所以最终形成依赖
zImage->vmlinux, 所以make整个all是指zImage dtbs

boot := arch/arm/boot
KBUILD_IMAGE := $(boot)/zImage
#all : zImage dtbs
all: $(notdir $(KBUILD_IMAGE)) $(KBUILD_DTBS)
BOOT_TARGETS	= zImage Image xipImage bootpImage uImage
$(BOOT_TARGETS): vmlinux
	$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

1.2 find autoksyms_recursive

我们在顶层Makefile中找到autoksyms_recursive , 会发现如下代码片段, autoksyms_recursive 也依赖 $(vmlinux-deps)

autoksyms_recursive: $(vmlinux-deps)
	@echo "eric_debug:.......vmlinux-deps=$(vmlinux-deps)"

1.3 find $(vmlinux-deps)

我们在顶层Makefile中可以看见$(vmlinux-deps)依赖 $(vmlinux-dirs), 而$(vmlinux-dirs)依赖 preparescripts, 所以make先去执行prepare和scripts.

$(sort $(vmlinux-deps)): $(vmlinux-dirs);
$(vmlinux-dirs): prepare scripts
	$(Q)$(MAKE) $(build)=$@ need-builtin=1

同样在顶层目录中可以发现对vmlinux-deps赋值的代码段,vmlinux-deps的内容如下,

#eric_debug:.......vmlinux-deps=arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o  init/built-in.a  usr/built-in.a  arch/arm/vfp/built-in.a  arch/arm/vdso/built-in.a  arch/arm/kernel/built-in.a  arch/arm/mm/built-in.a  arch/arm/common/built-in.a  arch/arm/probes/built-in.a  arch/arm/net/built-in.a  arch/arm/crypto/built-in.a  arch/arm/firmware/built-in.a  arch/arm/mach-vexpress/built-in.a  arch/arm/plat-versatile/built-in.a  kernel/built-in.a  certs/built-in.a  mm/built-in.a  fs/built-in.a  ipc/built-in.a  security/built-in.a  crypto/built-in.a  block/built-in.a  arch/arm/lib/built-in.a  lib/built-in.a  drivers/built-in.a  sound/built-in.a  firmware/built-in.a  ubuntu/built-in.a  arch/arm/oprofile/built-in.a  net/built-in.a  virt/built-in.a  arch/arm/lib/lib.a  lib/lib.a
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN) $(KBUILD_VMLINUX_LIBS)

其中,vmlinux-deps所需要的内容是由下面的语句展开后得到的,其中include arch/arm/Makefile会添加core-y的内容。

init-y		:= init/
drivers-y	:= drivers/ sound/ firmware/ ubuntu/
net-y		:= net/
libs-y		:= lib/
core-y		:= usr/
virt-y		:= virt/

#include arch/arm/Makefile,
include arch/$(SRCARCH)/Makefile

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

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

vmlinux-alldirs	:= $(sort $(vmlinux-dirs) $(patsubst %/,%,$(filter %/, \
		     $(init-) $(core-) $(drivers-) $(net-) $(libs-) $(virt-))))

init-y		:= $(patsubst %/, %/built-in.a, $(init-y))
core-y		:= $(patsubst %/, %/built-in.a, $(core-y))
drivers-y	:= $(patsubst %/, %/built-in.a, $(drivers-y))
net-y		:= $(patsubst %/, %/built-in.a, $(net-y))
libs-y1		:= $(patsubst %/, %/lib.a, $(libs-y))
libs-y2		:= $(patsubst %/, %/built-in.a, $(filter-out %.a, $(libs-y)))
virt-y		:= $(patsubst %/, %/built-in.a, $(virt-y))

# Externally visible symbols (used by link-vmlinux.sh)
export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y2) $(drivers-y) $(net-y) $(virt-y)
export KBUILD_VMLINUX_LIBS := $(libs-y1)
export KBUILD_LDS          := arch/$(SRCARCH)/kernel/vmlinux.lds
export LDFLAGS_vmlinux
# used by scripts/package/Makefile
export KBUILD_ALLDIRS := $(sort $(filter-out arch/%,$(vmlinux-alldirs)) arch Documentation include samples scripts tools)

1.4 find prepare

从上面的分析看,make 首先会去执行prepare

prepare3: include/config/kernel.release
# prepare2 creates a makefile if using a separate output directory
prepare2: prepare3 outputmakefile asm-generic
prepare1: prepare2 $(version_h) $(autoksyms_h) include/generated/utsrelease.h \
                   include/config/auto.conf
	$(cmd_crmodverdir)
archprepare: archheaders archscripts prepare1 scripts_basic

prepare0: archprepare gcc-plugins
	@echo "eric_debug.....start:$@"
	$(Q)$(MAKE) $(build)=.
	@echo "eric_debug.....end:$@"

# All the preparing..
prepare: prepare0 prepare-objtool

我们直接找到prepare最后的依赖kernel.release, 会发现kernel.release依赖include/config/auto.conf

define filechk_kernel.release
	echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))"
endef
include/config/kernel.release: include/config/auto.conf FORCE
	$(call filechk,kernel.release)

1.4.1 find auto.conf

KCONFIG_CONFIG = .config

#include/config/auto.conf.cmd为空
$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ; 

include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
	@echo "eric_debug:.......$@ start, KCONFIG_CONFIG=$(KCONFIG_CONFIG)"
	$(Q)$(MAKE) -f $(srctree)/Makefile syncconfig
	@echo "eric_debug:.......$@ end, KCONFIG_CONFIG=$(KCONFIG_CONFIG)"

首先$(KCONFIG_CONFIG) include/config/auto.conf.cmd由于没有依赖,所以make直接执行命令行

#$(Q)$(MAKE) -f ./Makefile syncconfig,顶层目录下没有syncconfig,会调用scripts/kconfig/Makefile
$(Q)$(MAKE) -f $(srctree)/Makefile syncconfig

我们分析下,为什么会用scripts/kconfig/Makefile? 首先有个困惑,使用make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-命令行后,config-targets :=0,是不应该进入 %config 片段,但是自己没有想清楚原来调用的是$(Q)$(MAKE) -f ./Makefile syncconfig, 相当于make syncconfig, 又重新在顶层Makefile文件中配置config-targets :=1,这样就会进入到顶层Makefile中的%config执行, 从而进入到Makefile.build, 其中obj=scripts/kconfig, 通过语句include $(kbuild-file) 包含scripts/kconfig/Makefile,并执行其中的syncconfig

#Top Makefile
%config: scripts_basic outputmakefile FORCE	
	@echo "eric_debug.........start:$(config-targets):$@"
	#Kbuild.include (scripts)--->build := -f $(srctree)/scripts/Makefile.build obj
	#$(Q)(MAKE) -f $(srctree)/scripts/Makefile.build obj=syncconfig
	$(Q)$(MAKE) $(build)=scripts/kconfig $@
	@echo "eric_debug.........end $@"
#scripts/kconfig/Makefile
syncconfig: $(obj)/conf
	@echo "eric_debug----------------start:$@:$(silent):$(Kconfig) "
	$(Q)mkdir -p include/config include/generated
	#eric_debug----------------start:syncconfig::Kconfig
	$< $(silent) --$@ $(Kconfig)

Ok, 由于conf在生成.config文件中已经产生,所以make直接执行命令行$< $(silent) --$@ $(Kconfig),展开后得到 scripts/kconfig/conf --syncconfig Kconfig, 进入conf.c文件中查看,
NOTE: getopt_long method

while ((opt = getopt_long(ac, av, "s", long_opts, NULL)) != -1) {
	input_mode = (enum input_mode)opt;
	switch (opt) {
	case defconfig:
		defconfig_file = optarg;
		break;
	}
}
switch (input_mode) {
case syncconfig:
	conf_read(NULL);		//读取.config文件
	break;
}
switch (input_mode) {
case syncconfig:
	do {
		conf_cnt = 0;
		check_conf(&rootmenu);
	} while (conf_cnt && (input_mode != listnewconfig && input_mode != olddefconfig));
	break;
}

if (sync_kconfig) {
	if (conf_get_changed() && conf_write(NULL)) {...}	//如果.config文件改变,就写到.config
	//conf_write_autoconf()产生下面几个文件: 
	//include/config/auto.conf.cmd,include/generated/autoconf.h 
	//include/config/tristate.conf,include/config/auto.conf
	if (conf_write_autoconf()) {...}
} 

当conf执行完后会产生下面四个文件,然后返回到prepare2, 接下来看prepare2的一个依赖asm-genericasm-generic主要是移除一些时间戳旧一点的文件,读者可以自己分析下就好.

include/config/auto.conf.cmd,include/generated/autoconf.h 
include/config/tristate.conf,include/config/auto.conf

1.4.2 返回prepare0

当make执行完返回到prepare0,就会执行命令行$(Q)$(MAKE) $(build)=., 展开后得到$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.build obj=.,其中target是__build, obj没有任何内容,但是会编译asm-offsets.c. 这是为什么?
请再次仔细查看Makefile.build的代码, 最终会包含顶层目录中的Kbuild文件, 可以看出__build会依赖$(always),而include $(kbuild-file), 会将顶层Kubild包含进来. 我们可以看到在Kbuild文件中, always 包含 include/generated/asm-offsets.h, asm-offsets.h依赖 arch/$(SRCARCH)/kernel/asm-offsets.s , 而asm-offsets.s文件又依赖asm-offsets.c文件,其通过cc_s_c命令生成,所以会编译asm-offset.c

# src := .
src := $(obj)
PHONY := __build
__build:
always :=
targets :=
#kbuild-dir := ./.
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
#kbuild-file := ././Kbuild(./Kbuild)
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
#include $(./Kbuild)
include $(kbuild-file)

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

顶层Kbuild中的内容如下,

offsets-file := include/generated/asm-offsets.h

#always += include/generated/asm-offsets.h
always  += $(offsets-file)

# We use internal kbuild rules to avoid the "is up to date" message from make
arch/$(SRCARCH)/kernel/asm-offsets.s: arch/$(SRCARCH)/kernel/asm-offsets.c \
                                      $(obj)/$(timeconst-file) $(obj)/$(bounds-file) FORCE
	$(call if_changed_dep,cc_s_c)
	
#./include/generated/asm-offsets.h: arch/arm/kernel/asm-offset.s FORCE
$(obj)/$(offsets-file): arch/$(SRCARCH)/kernel/asm-offsets.s FORCE
	$(call filechk,offsets,__ASM_OFFSETS_H__)

1.7 Go back to $(vmlinux-dirs)

当prepare 和 scripts都执行完了, make就开始执行命令行了$(Q)$(MAKE) $(build)=$@ need-builtin=1,产生vmlinux-deps对应的目录下的built-in.a,它是通过从vmlinux-dirs取得对应的目录, 然后执行命令行产生的。

$(sort $(vmlinux-deps)): $(vmlinux-dirs);
$(vmlinux-dirs): prepare scripts
	$(Q)$(MAKE) $(build)=$@ need-builtin=1

$(u-boot-dirs)中的内容如下:

 eric_debug:.......vmlinux-dirs=init usr arch/arm/vfp 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-vexpress arch/arm/plat-versatile kernel certs mm fs ipc security crypto block drivers sound firmware ubuntu arch/arm/oprofile net arch/arm/lib lib virt

1.7.1 举个栗子:怎样产生drivers目录下的build-in.a,并且怎样产生drivers/usb目录下的build-in.a

Let’s get started , please follow me.

1.7.1.1 进入Makefile.build – obj=drivers

当执行命令行$(Q)$(MAKE) $(build)=$@ need-builtin=1,展开后得到如下代码段,默认target是all, 但是在Makefile.build中默认target是__build.

$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.build obj=drivers need-builtin=1

进入Makefile.build中,展开并执行下面的语句,最后得到关系 __build: drivers/built-in.a,需要产生built-in.a,而它又依赖$(real-obj-y).

#src := drivers
src := $(obj)
PHONY := __build
__build:

#auto.conf包含drivers/Makefile和drives/usb/Makefile中obj-$(CONFIG_XXX)的条件选项变量CONFIG_XXX
-include include/config/auto.conf
include scripts/Makefile.lib

kbuild-dir := ./drivers
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
#kbuild-file := ./drivers/Makefile
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
#include $(./drivers/Makefile)
#将drivers/Makefile对应的obj-y,即*.o 文件包含进来
include $(kbuild-file)

#strip将首尾部空格去掉,且中间多个空格合并为一个,str:=-a--b--c--; $(strip $(str)), 结果str:=a-b-c
#builtin-target := drivers/built-in.a
ifneq ($(strip $(real-obj-y) $(need-builtin)),)
builtin-target := $(obj)/built-in.a
endif

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

$(sort $(subdir-obj-y)): $(subdir-ym) ;

quiet_cmd_ar_builtin = AR      $@
      cmd_ar_builtin = rm -f $@; \
                     $(AR) rcSTP$(KBUILD_ARFLAGS) $@ $(filter $(real-obj-y), $^)                     
$(builtin-target): $(real-obj-y) FORCE
	$(call if_changed,ar_builtin)

$(subdir-ym):
	$(Q)$(MAKE) $(build)=$@ need-builtin=$(if $(findstring $@,$(subdir-obj-y)),1)

我们查看下此时./drivers/Makefile里面的obj-y的部分值, 从下面可以看到,当我们配置了USB相关选项后,CONFIG_XXX=y

obj-$(CONFIG_USB)		+= usb/
obj-y				+= i2c/
obj-$(CONFIG_SPI)		+= spi/
obj-y				+= pci/

我们可以发现Makefile.lib和kbuild-file是被includeMakefile.build中的,而此刻的Kbuild-file正是(.Drivers/Makefile)
Ok, 我们假设./Drivers/Makefile中只有上面四个项并且都配置过了, 那么最后obj-y := usb/ i2c/ spi/ pci/

1.7.1.1.2 Find $(real-obj-y)

接下来我们分析下**$(real-obj-y)是怎么产生的呢?**首先我们可以在Makefile.lib中发现如下的定义,展开后得到如下的代码段, 其中obj-y 是从 .Drivers/Makefile中来的, obj-y := usb/ i2c/ spi/ pci/

#__subdir-y	:= usb
__subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
#subdir-y := usb i2c spi pci
subdir-y	+= $(__subdir-y)
#obj-y := usb/built-in.a i2c/built-in.a spi/built-in.a pci/built-in.a
obj-y		:= $(patsubst %/, %/built-in.a, $(obj-y))


#此刻./Drivers/Makefile没有obj-m, 所以subdir-m 为空
#subdir-ym := i2c pci spi usb
subdir-ym	:= $(sort $(subdir-y) $(subdir-m))

#subdir-obj-y := usb/built-in.a i2c/built-in.a spi/built-in.a pci/built-in.a
subdir-obj-y := $(filter %/built-in.a, $(obj-y))

#real-obj-y := usb/built-in.a i2c/built-in.a spi/built-in.a pci/built-in.a
real-obj-y := $(foreach m, $(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m)))

#这个地方一定需要, 才能找到正确的路径
#subdir-obj-y := drives/usb/built-in.a drives/i2c/built-in.a drives/spi/built-in.a drives/pci/built-in.a
subdir-obj-y	:= $(addprefix $(obj)/,$(subdir-obj-y))
#real-obj-y := drives/usb/built-in.a drives/i2c/built-in.a drives/spi/built-in.a drives/pci/built-in.a
real-obj-y	:= $(addprefix $(obj)/,$(real-obj-y))
#subdir-ym := drives/i2c drives/pci drives/spi drives/usb
subdir-ym	:= $(addprefix $(obj)/,$(subdir-ym))

从上面代码段, 我们可以知道real-obj-y := usb/built-in.a i2c/built-in.a spi/built-in.a pci/built-in.a, 即找到$(real-obj-y) 的值. $(real-obj-y)中的值是怎么产生的呢?,

1.7.1.1.3 Find (xxx)/built-in.a, xxx={i2c pci spi usb}

当我们再次阅读Makefile.build发现如下代码段

#$(subdir-ym) = drives/i2c drives/pci drives/spi drives/usb
#subdir-obj-y := drives/usb/built-in.a drives/i2c/built-in.a drives/spi/built-in.a drives/pci/built-in.a
$(subdir-ym):
	$(Q)$(MAKE) $(build)=$@ need-builtin=$(if $(findstring $@,$(subdir-obj-y)),1)
  • $@ = drives/usb时,展开$(Q)$(MAKE) $(build)=$@ need-builtin=$(if $(findstring $@,$(subdir-obj-y)),1)语句,得到代码段 $(Q)$(MAKE)-f $(srctree)/scripts/Makefile.build obj=drivers/usb need-builtin=1
1.7.1.1.4 进入Makefile.build, obj=drivers/usb

当make执行$(Q)$(MAKE)-f $(srctree)/scripts/Makefile.build obj=drivers/usb need-builtin=1时, 又会再一次执行下面的语句.此时的kbuild-file为./drivers/usb/Makefile

src := $(obj)
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
#include $(./drivers/usb/Makefile)
include $(kbuild-file)

#builtin-target := drivers/usb/built-in.a
ifneq ($(strip $(real-obj-y) $(need-builtin)),)
builtin-target := $(obj)/built-in.a
endif

$(sort $(subdir-obj-y)): $(subdir-ym) ;

quiet_cmd_ar_builtin = AR      $@
      cmd_ar_builtin = rm -f $@; \
                     $(AR) rcSTP$(KBUILD_ARFLAGS) $@ $(filter $(real-obj-y), $^)  
                                        
$(builtin-target): $(real-obj-y) FORCE
	$(call if_changed,ar_builtin)

$(subdir-ym):
	$(Q)$(MAKE) $(build)=$@ need-builtin=$(if $(findstring $@,$(subdir-obj-y)),1)	

部分./drivers/usb/Makefile内容如下

obj-$(CONFIG_USB_PCI)		+= host/
obj-$(CONFIG_USB_EHCI_HCD)	+= host/
obj-$(CONFIG_USB_OHCI_HCD)	+= host/
obj-$(CONFIG_USB_XHCI_HCD)	+= host/
obj-$(CONFIG_USB_SERIAL)	+= serial/
1.7.1.1.5 find $(real-obj-y)

假设 ./drivers/usb/Makefile 中的内容只有上面的内容,那么obj-y := host/ serial/, 展开Makefile.lib得到下面的代码段

__subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y	+= $(__subdir-y)
#obj-y := host/built-in.a serial/built-in.a
obj-y		:= $(patsubst %/, %/built-in.a, $(obj-y)
#此刻./Drivers/Makefile没有obj-m, 所以subdir-m 为空
#subdir-ym := host serial
subdir-ym	:= $(sort $(subdir-y) $(subdir-m))

#subdir-obj-y := host/built-in.a serial/built-in.a 
subdir-obj-y := $(filter %/built-in.a, $(obj-y))

#real-obj-y := host/built-in.a serial/built-in.a
real-obj-y := $(foreach m, $(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m)))

#这个地方一定需要, 才能找到正确的路径, 
#subdir-obj-y := drives/usb/host/built-in.a drives/usb/serial/built-in.a
subdir-obj-y	:= $(addprefix $(obj)/,$(subdir-obj-y))
#real-obj-y := drives/usb/host/built-in.a drives/usb/serial/built-in.a
real-obj-y	:= $(addprefix $(obj)/,$(real-obj-y))
#subdir-ym := drives/usb/host drives/usb/serial
subdir-ym	:= $(addprefix $(obj)/,$(subdir-ym))

从上面代码段中,我们可以知道,real-obj-y := drives/usb/host/built-in.a drives/usb/serial/built-in.a 即找到$(real-obj-y) 的值. $(real-obj-y)中的值是怎么产生的呢?

1.7.1.1.6 进入Makefile.build, obj=drivers/usb/host

当我们再次阅读Makefile.build发现如下代码段

#$(subdir-ym) = drives/usb/host drives/usb/serial
#subdir-obj-y := drives/usb/host/built-in.a drives/usb/serial/built-in.a
$(subdir-ym):
	$(Q)$(MAKE) $(build)=$@ need-builtin=$(if $(findstring $@,$(subdir-obj-y)),1)
  • $@ = drives/usb/host时,展开$(Q)$(MAKE) $(build)=$@ need-builtin=$(if $(findstring $@,$(subdir-obj-y)),1)语句,得到代码段 $(Q)$(MAKE)-f $(srctree)/scripts/Makefile.build obj=drives/usb/host need-builtin=1
1.7.1.1.7 find $(real-obj-y)
src := $(obj)
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
#include $(./drivers/usb/host/Makefile)
include $(kbuild-file)

#builtin-target := drivers/usb/host/built-in.a
ifneq ($(strip $(real-obj-y) $(need-builtin)),)
builtin-target := $(obj)/built-in.a
endif

$(sort $(subdir-obj-y)): $(subdir-ym) ;

quiet_cmd_ar_builtin = AR      $@
      cmd_ar_builtin = rm -f $@; \
                     $(AR) rcSTP$(KBUILD_ARFLAGS) $@ $(filter $(real-obj-y), $^)  
                                        
$(builtin-target): $(real-obj-y) FORCE
	$(call if_changed,ar_builtin)

$(subdir-ym):
	$(Q)$(MAKE) $(build)=$@ need-builtin=$(if $(findstring $@,$(subdir-obj-y)),1)	

部分./drivers/usb/host/Makefile内容如下

obj-$(CONFIG_USB_OHCI_HCD)	+= ohci-hcd.o
obj-$(CONFIG_USB_OHCI_HCD_PCI)	+= ohci-pci.o
obj-$(CONFIG_USB_UHCI_HCD)	+= uhci-hcd.o
obj-$(CONFIG_USB_EHCI_HCD)	+= ehci-hcd.o

这时, 我们来分析下$(real-obj-y),
假设 ./drivers/usb/host/Makefile 中的内容只有上面的内容,那么obj-y := ohci-hcd.o uhci-hcd.o ehci-hcd.o ohci-pci.o, 展开Makefile.lib得到下面的代码段

#__subdir-y 为空
__subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
#subdir-y 为空
subdir-y	+= $(__subdir-y)
#obj-y为空
obj-y		:= $(patsubst %/, %/built-in.a, $(obj-y)#subdir-ym 为空
subdir-ym	:= $(sort $(subdir-y) $(subdir-m))

#subdir-obj-y 为空 
subdir-obj-y := $(filter %/built-in.a, $(obj-y))

#real-obj-y :=  ohci-hcd.o uhci-hcd.o ehci-hcd.o ohci-pci.o
real-obj-y := $(foreach m, $(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m)))

#这个地方一定需要, 才能找到正确的路径, 
#subdir-obj-y 为空
subdir-obj-y	:= $(addprefix $(obj)/,$(subdir-obj-y))
#real-obj-y := drives/usb/host/ohci-hcd.o drives/usb/host/uhci-hcd.o drives/usb/host/ehci-hcd.o drives/usb/host/ohci-pci.o 
real-obj-y	:= $(addprefix $(obj)/,$(real-obj-y))
#subdir-ym 为空
subdir-ym	:= $(addprefix $(obj)/,$(subdir-ym))

所以最后builtin-target := drivers/usb/host/built-in.a; real-obj-y := drives/usb/host/ohci-hcd.o drives/usb/host/uhci-hcd.o drives/usb/host/ehci-hcd.o drives/usb/host/ohci-pci.o

quiet_cmd_ar_builtin = AR      $@
      cmd_ar_builtin = rm -f $@; \
                     $(AR) rcSTP$(KBUILD_ARFLAGS) $@ $(filter $(real-obj-y), $^)
                     
$(builtin-target): $(real-obj-y) FORCE
	$(call if_changed,ar_builtin)

调用cmd_ar_builtin压缩为drivers/usb/host/built-in.a, 然后回退到1.7.1.1.6 进入Makefile.build, obj=drivers/usb/serial;
执行完后,回退到 1.7.1.1.3 生成 (xxx)/built-in.a, xxx={i2c pci spi usb}, 然后回退到1.7.1.1, 生成drivers/built-in.a.

Note, 而*.o是由如下命令产生

$(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE
	echo "eric_debug.....start: $@"
	$(call cmd,force_checksrc)
	$(call cmd,force_check_kmsg)
	$(call if_changed_rule,cc_o_c)
	echo "eric_debug.....end: $@"

好了,drivers/和drivers/usb目录下的build-in.a生产过程讲完了.

1.8 vmlinux产生

当最后$(vmlinux-dirs)所有的目录下built-in.a产生完后,返回到vmlinux,执行cmd_link-vmlinux命令通过scripts/link-vmlinux.sh*.a链接起来,形成vmlinux. 其中*.lds是由*.lds.S形成的.

#include arch/arm/Makefile,
include arch/$(SRCARCH)/Makefile

#KBUILD_LDS := arch/arm/kernel/vmlinux.lds
EXPORT KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds

cmd_link-vmlinux =                                                 \
	$(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) ;    \
	$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
	
vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE
	@echo "eric_debug:......$@ start"
	+$(call if_changed,link-vmlinux)
	@echo "eric_debug:......$@ end"

其中vmlinux.sh部分内容如下,

local lds="./arch/arm/kernel/vmlinux.lds"
objects="--whole-archive			\
	
		built-in.a				\
		--no-whole-archive			\
		--start-group				\
		${KBUILD_VMLINUX_LIBS}			\
		--end-group				\
		${1}"

${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2}	\
	-T ${lds} ${objects}

冬天已经来了,春天还会远吗? 当然不会

1.9 Image and zImage产生

在顶层Makefile中,有语句include arch/$(SRCARCH)/Makefile, 展开后得到 include arch/arm/Makefile, 其内容如下,

machine-$(CONFIG_ARCH_VEXPRESS)		+= vexpress
MACHINE  := arch/arm/mach-$(word 1,$(machine-y))/
boot := arch/arm/boot
BOOT_TARGETS	= zImage Image xipImage bootpImage uImage

$(BOOT_TARGETS): vmlinux
	$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

我们可以看见Image和zImage都依赖vmlinux, 展开$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@, 得到如下语句,$(Q)(MAKE) -f $(srctree)/scripts/Makefile.build obj=arch/arm/boot MACHINE=arch/arm/mach-vexpress arch/arm/boot/$@
其中$@ 每次会取{zImage Image xipImage bootpImage uImage} 其中的一个。 进入Makefile.build, 展开后得到如下代码段

PHONY := __build
__build:

#auto.conf包含drivers/Makefile和drives/usb/Makefile中obj-$(CONFIG_XXX)的条件选项变量CONFIG_XXX
-include include/config/auto.conf
include scripts/Makefile.lib

#kbuild-dir := ./arch/arm/boot 
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
#kbuild-file := ./arch/arm/boot/Makefile
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
#include $(./arch/arm/boot/Makefile)
include $(kbuild-file)

我看看arch/arm/boot/Makefile,展开后得到如下代码段, 并得到如下依赖关系uImage --> zImage --> $(obj)/compressed/vmlinux-->Image-->vmlinux

#obj := arch/arm/boot 
$(obj)/uImage:	$(obj)/zImage FORCE
	@$(check_for_multiple_loadaddr)
	$(call if_changed,uimage)
	
$(obj)/zImage:	$(obj)/compressed/vmlinux FORCE
	$(call if_changed,objcopy)

$(obj)/compressed/vmlinux: $(obj)/Image FORCE
	$(Q)$(MAKE) $(build)=$(obj)/compressed $@

$(obj)/Image: vmlinux FORCE
	$(call if_changed,objcopy)
	
$(obj)/bootpImage: $(obj)/bootp/bootp FORCE
	$(call if_changed,objcopy)

好了,整个Kernel编译流程基本讲解完了,Thanks .

你可能感兴趣的:(linux内核)