vmlinux
vmlinux
文件的产生, zImage
和Image
产生首先当我们执行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)/$@
autoksyms_recursive
我们在顶层Makefile
中找到autoksyms_recursive
, 会发现如下代码片段, autoksyms_recursive
也依赖 $(vmlinux-deps)
autoksyms_recursive: $(vmlinux-deps)
@echo "eric_debug:.......vmlinux-deps=$(vmlinux-deps)"
$(vmlinux-deps)
我们在顶层Makefile
中可以看见$(vmlinux-deps)
依赖 $(vmlinux-dirs)
, 而$(vmlinux-dirs)
依赖 prepare
和 scripts
, 所以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)
从上面的分析看,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)
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-generic
,asm-generic
主要是移除一些时间戳旧一点的文件,读者可以自己分析下就好.
include/config/auto.conf.cmd,include/generated/autoconf.h
include/config/tristate.conf,include/config/auto.conf
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__)
$(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
举个栗子:
怎样产生drivers
目录下的build-in.a
,并且怎样产生drivers/usb
目录下的build-in.a
Let’s get started , please follow me.
当执行命令行$(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
是被include
到Makefile.build
中的,而此刻的Kbuild-file
正是(.Drivers/Makefile
)
Ok, 我们假设./Drivers/Makefile中只有上面四个项并且都配置过了, 那么最后obj-y := usb/ i2c/ spi/ pci/
$(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)
中的值是怎么产生的呢?,
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
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/
$(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)
中的值是怎么产生的呢?
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
$(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生产过程讲完了.
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}
冬天已经来了,春天还会远吗? 当然不会
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 .