Linux Kernel编译流程 (二)

2023-10-27

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 .

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux Kernel编译流程 (二) 的相关文章

  • printk与内核日志打印优先级设置

    1 控制台的日志优先级 define KERN EMERG lt 0 gt 致命级 紧急事件消息 系统崩溃之前提示 表示系统不可用 define KERN ALERT lt 1 gt 警戒级 报告消息 表示必须采取措施 define KER
  • 2.6内核的通用的编译步骤

    2 6内核的通用的编译步骤 1 下载源码并解压 虽然我们可以将内核源码存放在任何自己找得到的地方 但通常还是会将内核源码下载到 usr src目录并解压 cd usr src wget ftp kernel org pub linux ke
  • chinaUnix中的linux源代码学习

    原文地址 http bbs chinaunix net thread 1930079 1 1 html 大家好 内核源码版对本版块的精华帖进行了分类汇总 所有的精华帖分为十大类 各个分类的精华帖相关信息分布在该贴2 11楼 每个分类各占1楼
  • Linux内核——cli()和sti()——标志寄存器的中断标志

    cli 和sti 有点类似于汇编指令中的CLI和STL 当某个任务在执行的过程中不想被中断 则可以在任务的开始出执行cli 在任务的结束处执行sti 恢复中断的执行 为了避免竞争条件和中断对临界代码区的干扰 在Linux 0 12内核代码中
  • Linux操作系统进程的状态和转换(五态模型)

    1 进程的状态和装换 1 1进程的三态模型 按进程在执行过程中的不同情况至少要定义三种状态 运行 running 态 进程占有处理器正在运行的状态 进程已获得CPU 其程序正在执行 在单处理机系统中 只有一个进程处于执行状态 在多处理机系统
  • 趣谈操作系统原理,存储管理之页式、段式、段页式存储

    一 概述 非连续分配管理方式允许一个程序分散地装入到不相邻的内存分区 根据分区的大小是否固定分为分页式存储管理方式和分段式存储管理方式 分页存储管理方式中 又根据运行作业时是否要把作业的所有页面都装入内存才能运行分为基本分页式存储管理方式和
  • busybox简介

    busybox BusyBox 是标准 Linux 工具的一个单个可执行实现 BusyBox 包含了一些简单的工具 例如 cat 和 echo 还包含了一些更大 更复杂的工具 例如 grep find mount 以及 telnet 有些人
  • 调度器简介,以及Linux的调度策略

    进程是操作系统虚拟出来的概念 用来组织计算机中的任务 但随着进程被赋予越来越多的任务 进程好像有了真实的生命 它从诞生就随着CPU时间执行 直到最终消失 不过 进程的生命都得到了操作系统内核的关照 就好像疲于照顾几个孩子的母亲内核必须做出决
  • linux内核开发基础(linux内核源码、树莓派源码编译、SD卡挂载)

    首先下载树莓派linux内核源码 下载网址 https github com raspberrypi linux 在树莓派使用指令 uname r查看当前树莓派的版本号 然后选择对应的linux内核版本号进行下载 将linux内核源码从共享
  • linux内核分析:进程通讯方式

    信号 一旦有信号产生 我们就有下面这几种 用户进程对信号的处理方式 1 执行默认操作 Linux 对每种信号都规定了默认操作 例如 上面列表中的 Term 就是终止进程的意思 Core 的意思是 Core Dump 也即终止进程后 通过 C
  • linux内核源码分析进程的管理与调度

    文章目录 一 进程管理 进程描写叙述符及任务结构 进程状态 进程创建 fork和vfork的差别 进程终止 二 进程调度 什么是调度 三 策略 I O消耗型和处理器消耗型的进程 进程优先级 时间片 进程抢占 调度算法 可运行队列 优先级数组
  • file_operations 结构体

    file operations 结构体中的成员函数是字符设备驱动程序设计的主体内容 这些函数实际会在应用程序进行 Linux 的 open write read close 等系统调用时最终被调用 file operations 结构体目前
  • 一文搞懂Linux内核页框回收(Page Frame Reclamation)

    页替换策略 Page Replacement Policy 每当讨论页替换策略 提及最多的就是基于LRU Least Recently Used 的算法 但严格来说这是不对的因为这些lists并不是严格按照LRU的顺序来维护的 在Linux
  • linux内核对于指令异常的处理

    1 处理流程 以arm64来介绍一下流程 如果在用户层发生指令异常时 首先进入入口el0 undef arch arm64 kernel entry s el0 undef Undefined instruction enable inte
  • kvm内存管理

    qemu kvm 进程很像一个普通的linux程序 它通过通常的malloc和mmap调用来申请内存 如果一个客户系统想使用1G物理内存 qemu kvm将会做一个malloc 1 lt lt 30 调用 在主机上申请1G的虚拟地址 然而
  • dracut 基本介绍

    dracut 基本介绍 dracut 维基 https dracut wiki kernel org index php Main Page http www 360doc com content 13 0428 09 12139495 2
  • int $0x80系统调用的idea

    1 基础知识 用户态和内核态 一般现代CPU都有几种不同的指令执行级别 Linux总共划分为4个指令执行级别 内核运行在0级别上 1 2级别默认不运行 用户程序运行在3级别上 在内核指令执行级别上 代码可以执行特权指令 访问任意的物理地址
  • Linux内核调度算法 -- CPU时间片如何分配

    内核在微观上 把CPU的运行时间分成许多分 然后安排给各个进程轮流运行 造成宏观上所有的进程仿佛同时在执行 双核CPU 实际上最多只能有两个进程在同时运行 大家在top vmstat命令里看到的正在运行的进程 并不是真的在占有着CPU哈 所
  • linux内核驱动开发笔试题

    linux内核驱动开发笔试题 一 一些常规中举的C考题 第一题 写出下述程序结果 int m 3 1 4 7 2 5 8 3 6 9 int i j k 2 for i 0 i lt 3 i printf d m k i 问题所在 本题考点
  • 树莓派内核开发准备(内核源码获取、启动过程、源码目录树)

    目录 1 交叉编译工具的安装 2 内核源码获取 3 嵌入式设备带操作系统的启动过程扫盲 4 Linux内核源码树扫盲 1 内核源码简介 2 Linux内核源代码目录树结构 tree指令查看 内核源码目录树 1 交叉编译工具的安装 参照我之前

随机推荐

  • python 均方误差_python如何实现均方误差和均方根误差?

    一 python实现均方误差 均方误差是各数据偏离真实值的距离平方和的平均数 也即误差平方和的平均数 用法 一般被用在机器学习的预测值与真实值之间的距离 均方误差对应的是最小二乘法 coding utf 8 import math def
  • python程序员 培训

    非常感谢大家这么长时间对我们的喜爱和关注 我们都知道 python是当前的大趋势 无论是就业前景 发展空间 还是薪资待遇都是当下最为火爆的行业 所以我们特意联系了央视公开课曾推荐的万门大学 向大家赠送一份人工智能的课程 这个课程我们已经了解
  • QSlider风格设置

    QT的滑动条在开发的过程中经常使用 默认的QSlider风格比较简陋 一般达不到UI设计的效果 本篇记录一个QSlider使用过程中风格的设置 1 qss常用的字段属性 1 1背景属性 属性 值 意思 background Backgrou
  • 用C语言实现经典游戏——贪吃蛇

    目录 1 游戏实现思想 1 定义蛇对象 2 食物对象 3 分数 4 初始化蛇 5 初始化食物 6 修改控制台光标位置 7 画出蛇和食物 8 蛇的移动控制 9 开始游戏 10 蛇移动 11 画墙 12 去除蛇尾 13 去除光标 14 显示分数
  • 06.数据库日常应用实例

    生活事例 01 用户注册 A select操作 搜索数据库所有用户名 B 与新注册的用户名进行比对 C 如果相同 新用户需从新改名 D 直到与数据库中的用户名不同 方能注册 02 转账 A select操作 搜索数据库所有账号 B 收款方账
  • Please enable Javascript to view this page

    今天进防火墙时出错 以下设置解决 internet选项 gt 安全 gt 受信任站点 gt 添加站点 转载于 https www cnblogs com cw828 p 10063200 html
  • 测试原则-阶段-测试用例设计-调试

    测试原则 阶段 测试用例设计 调试 测试原则和方法 测试阶段 测试策略 测试用例的设计 调试 软件度量 测试原则和方法 系统测试 成功的测试 测试原则 软件测试的方法 静态测试 动态测试 测试阶段 单元测试 集成测试 确认测试 系统测试 配
  • 【ETH链游】阿蟹Axie Infinity模拟器运行及多开

    Axie Infinity 众所周知 阿蟹 Axie Infinity 是去年最火的一款GameFi游戏 由越南团队Sky Mavis开发 短短几个月内就红遍东南亚 最大市场在菲律宾 其次是越南 马来西亚 印尼与美国等 去年6月 该游戏单日
  • 个人学习经验: C++ ifndef 作用和用法

    ifndef是if not define的简写 一般的使用场景为 1 头文件中使用 防止头文件被多重调用 2 作为测试使用 省去注释代码的麻烦 3 作为不同角色或者场景的判断使用 头件的中的 ifndef 这是一个很关键的东西 比如你有两个
  • react函数式组件传值之子传父

    在用react进行函数式编程时 父组件可以通过props向子组件传值 那么子组件怎么向父组件传值呢 首先 父组件需要向子组件传递一个函数 然后 子组件通过props获取函数并附上参数 最后 父组件通过函数拿到子组件传递的值 一 具体案例 父
  • gridcontrol选中多行数据进行复制_奇技淫巧:在 ssh 里面把服务器的文本复制到本地电脑...

    点击上方 Python编程时光 选择 加为星标 第一时间关注Python技术干货 2020年 力度最大的购书优惠 千万别错过了 使用 macOS 的同学 应该熟悉一个命令pbcopy 它可以在命令行中把一段内容写入到剪贴板 例如 echo
  • Java安装配置教程,2023年最新版,全部版本看这一篇就够了!!

    JDK新手无脑安装配置教程 JDK下载网址 ps 如果你的JDK版本在官网没有找到 可以通过第三方资源进行下载 下载安装配置教程是通用的 官方链接 gt https www oracle com java technologies java
  • 关于ViewGroup$ViewLocationHolder$mRoot的内存泄漏

    今儿遇到个场景 在Android P API 28 中 在退出了含有RecyclerView的RelativeLayout中 LeakCanary报了这么一个内存泄漏 1 定位问题 1 1 定位源码 在AndroidP中ViewGroup内
  • 初探计算机网络代理

    初探计算机网络代理 文章目录 初探计算机网络代理 什么是计算机网络代理 代理的类型有哪些 正向代理 反向代理 正向代理的实现原理是什么 普通代理 隧道代理 SOCKS 协议 反向代理的实现原理是什么 都有哪些代理产品 TL DR 这篇文章介
  • CH8-多线程

    案例8 1 龟兔赛跑 案例介绍 1 任务描述 众所周知的 龟兔赛跑 故事 兔子因为太过自信 比赛中途休息而导致乌龟赢得了比赛 本案例要求编写一个程序模拟龟兔赛跑 乌龟的速度为1米 1500毫秒 兔子的速度为5米 500毫秒 等兔子跑到第70
  • Prometheus在kubernetes集群的搭建教程

    Prometheus在kubernetes集群的搭建 一 Prometheus介绍 1 Prometheus简介 2 Prometheus介绍 3 Prometheus与市面的监控系统区别 4 Prometheus特点 5 Promethe
  • 21.QT-QTreeWidget,QTabWidget

    QTreeWidget树形列表 设置标签相关函数 void QTreeWidget setHeaderItem QTreeWidgetItem item void QTreeWidget setHeaderLabel constQStrin
  • 论文笔记:NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis

    目录 文章摘要 1 Neural Radiance Field Scene Representation 基于神经辐射场的场景表示 2 Volume Rendering with Radiance Fields 基于辐射场的体素渲染 2 1
  • 奇异值分解 (SVD)原理及python实现

    奇异值分解 Singular Value Decomposition SVD 是一种矩阵分解 Matrix Decomposition 的方法 除此之外 矩阵分解还有很多方法 例如特征分解 Eigendecomposition LU分解 L
  • Linux Kernel编译流程 (二)

    1 vmlinux 研究vmlinux文件的产生 zImage和Image产生 Linux Kernel 4 18 20 Source Insight 3 5 Ubuntu 18 04 arm linux gnueabi xxx 1 1 f