Linux make 过程分析

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16

如果make不带任何参数,匹配默认目标_all

# If building an external module we do not care about the all: rule
# but instead _all depend on modules
PHONY += all
ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif

KBUILD_EXTMODE取决于make参数中带不带M参数,不带的话为空。所以_all依赖all,all又依赖vmlinux

# The all: target is the default when no target is given on the
# command line.
# This allow a user to issue only 'make' to build a kernel including modules
# Defaults to vmlinux, but the arch makefile usually adds further targets
all: vmlinux
vmlinux: scripts/link-vmlinux.sh vmlinux_prereq $(vmlinux-deps) FORCE
	+$(call if_changed,link-vmlinux)

scripts/link-vmlinux.sh是一个脚本
vmlinux_prereq是一个phony目标,其receipe如下:

vmlinux_prereq: $(vmlinux-deps) FORCE
ifdef CONFIG_HEADERS_CHECK
	$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
ifdef CONFIG_GDB_SCRIPTS
	$(Q)ln -fsn $(abspath $(srctree)/scripts/gdb/vmlinux-gdb.py)
endif
ifdef CONFIG_TRIM_UNUSED_KSYMS
	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/adjust_autoksyms.sh \
	  "$(MAKE) -f $(srctree)/Makefile vmlinux"
endif

vmlinux-deps是一个变量,其定义为

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

其中各项的定义为:

init-y		:= $(patsubst %/, %/built-in.o, $(init-y))
core-y		:= $(patsubst %/, %/built-in.o, $(core-y))
drivers-y	:= $(patsubst %/, %/built-in.o, $(drivers-y))
net-y		:= $(patsubst %/, %/built-in.o, $(net-y))
libs-y1		:= $(patsubst %/, %/lib.a, $(libs-y))
libs-y2		:= $(filter-out %.a, $(patsubst %/, %/built-in.o, $(libs-y)))
virt-y		:= $(patsubst %/, %/built-in.o, $(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)

而init-y等的定义为

ifeq ($(KBUILD_EXTMOD),)
# Additional helpers built in scripts/
# Carefully list dependencies so we do not try to build scripts twice
# in parallel
PHONY += scripts
scripts: scripts_basic include/config/auto.conf include/config/tristate.conf \
	 asm-generic gcc-plugins
	$(Q)$(MAKE) $(build)=$(@)

# Objects we will link into vmlinux / subdirs we need to visit
init-y		:= init/
drivers-y	:= drivers/ sound/ firmware/
net-y		:= net/
libs-y		:= lib/
core-y		:= usr/
virt-y		:= virt/
endif # KBUILD_EXTMOD

综上,vmlinux-deps = arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o init/built-in.o usr/built-in.o arch/arm/vfp/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o arch/arm/common/built-in.o arch/arm/probes/built-in.o arch/arm/net/built-in.o arch/arm/crypto/built-in.o arch/arm/firmware/built-in.o arch/arm/mach-vexpress/built-in.o arch/arm/mach-zynq/built-in.o arch/arm/plat-versatile/built-in.o kernel/built-in.o certs/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 arch/arm/lib/built-in.o lib/built-in.o drivers/built-in.o sound/built-in.o firmware/built-in.o net/built-in.o virt/built-in.o arch/arm/lib/lib.a lib/lib.a

也就是说,vmlinux依赖这一堆.o和.a,以及scripts/link-vmlinux.sh和链接脚本文件vmlinux.lds

在依赖更新完后,vmlinux目标会执行

	+$(call if_changed,link-vmlinux)

if_changed 是定义在 scripts/Kbuild.include 的一个函数,接受一个参数,其作用是检查调用此函数的receipe的所有依赖,如果某个依赖比目标新 或依赖文件不存在,或构建命令改变了,则执行$(cmd_$(1))。在这里就是执行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)

展开后可知,vmlinux目标实际执行的命令为

/bin/bash scripts/link-vmlinux.sh arm-linux-gnueabihf-ld -EL -p --no-undefined -X --pic-veneer --build-id

scripts/link-vmlinux.sh 是用来链接linux的脚本

build-in.o

对于 vmlinux-deps,有规则:

# The actual objects are generated when descending,
# make sure no implicit rule kicks in
$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;

而 vmlinux-dirs 的定义为:

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)))

此处的init-y等是在init-y := $(patsubst %/, %/built-in.o, $(init-y))之前的值

vmlinux-dirs 保存着vmlinux源码文件的目录,其值如下:

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

vmlinux-dirs的receipe如下:

# Handle descending into subdirectories listed in $(vmlinux-dirs)
# Preset locale variables to speed up the build process. Limit locale
# tweaks to this spot to avoid wrong language settings when running
# make menuconfig etc.
# Error messages still appears in the original language

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

prepare是一个phony, 用来创建输出目录,构建主机工具,自动文件生成等
scripts是一个phony,用来编译fixdep, 生成内核配置、三态配置、通用头文件、编译scripts下所有主机工具

$(Q)$(MAKE) $(build)=$@展开为@ make -f ./scripts/Makefile.build obj=$@
这又涉及到scripts/Makefile.build中__build目标
由于KBUILD_BUILTIN=1,KBUILD_MODULES 为空,因此__build 目标为

__build: $(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)
	@:

builtin-target定义在这里:

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

以init为例,scripts/Makefile.build会include init/Makefile, 而在其中,定义了obj-y
所以builtin-target = init/built-in.o

$builtin-target的recepie如下:

#
# Rule to compile a set of .o files into one .o file
#
ifdef builtin-target

ifdef CONFIG_THIN_ARCHIVES
  cmd_make_builtin = rm -f $@; $(AR) rcSTP$(KBUILD_ARFLAGS)
  cmd_make_empty_builtin = rm -f $@; $(AR) rcSTP$(KBUILD_ARFLAGS)
  quiet_cmd_link_o_target = AR      $@
else
  cmd_make_builtin = $(LD) $(ld_flags) -r -o
  cmd_make_empty_builtin = rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS)
  quiet_cmd_link_o_target = LD      $@
endif

# 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) $@)

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

targets += $(builtin-target)
endif # builtin-target

$(builtin-target) 目标依赖 $(obj-y), 调用$(call if_changed,link_o_target),执行cmd_link_o_target ,把目录下的所有.o链接到一个大的built-in.o中

TBD: 这些.o是怎么来的? 貌似跟scripts/Makefile.lib有关,另外这里似乎实现的是递归编译

# Descending
# ---------------------------------------------------------------------------

PHONY += $(subdir-ym)
$(subdir-ym):
	$(Q)$(MAKE) $(build)=$@
posted @ 2026-05-08 13:45  p_wild  阅读(10)  评论(0)    收藏  举报