[笔记] linux 4.19 版本 Kbuild 编译流程解析

目录

  • 写在前面与一些说明
    • linux 编译工程框架 Kbuild
    • Top-Makefile 文件
  • linux 编译命令
    • make help
    • distclean 目标
    • defconfig 目标
      • build 变量与 $(build)=dir 赋值
      • 使用 obj 变量实现包含目标模块下的 makefile
      • defconfig 规则展开
      • defconfig 的生成命令解析
    • make 默认目标生成 image.gz 镜像与 .dtb 设备树文件
      • vmlinux 编译生成流程
      • 子模块目录的指定(阅读 Kbuild 框架可知)
      • 编译目标文件的指定(阅读 Kbuild 框架可知)
      • 目标文件是编译进内核还是编译为 .ko 模块(阅读 Kbuild 框架可知)

写在前面与一些说明

之前一直在搞 RTOS 与 ARM 底层,对 linux 用的不多,现在是时候学习 linux 下的驱动框架与内核了,感谢网上那么多的学习资料(正点原子、知乎、CSDN...

linux 编译工程框架 Kbuild

linux 使用的编译工程框架为 Kbuild, 如果对 Kbuild 一无所知,在阅读本篇文章之前,建议阅读一遍 linux 内核工程中的下列文件(路径为 ./Documentation/kbuild/):
[笔记] linux 4.19 版本 Kbuild 编译流程解析_第1张图片

Top-Makefile 文件

top Makefile 为 liunx 内核根目录下的 Makefile 文件:
[笔记] linux 4.19 版本 Kbuild 编译流程解析_第2张图片

linux 编译命令

搭建交叉编译环境就不说了,直接在 SOC 原厂提供的 linux 工程根目录下运行如下命令:

# 清除工程
make  CROSS_COMPILE=aarch64-none-linux-gnu- ARCH=arm64 distclean

# defconfig 为 linux 特有的工程配置文件,控制哪些模块需要编译(编译进镜像或编译成 .ko)
# 并会在工程根目录下生成 .config 文件
make  CROSS_COMPILE=aarch64-none-linux-gnu- ARCH=arm64 defconfig

# 编译生成 linux 镜像, 在工程根目录下会生成 vmlinux 可执行文件
# 在 arch/(ARCH)/boot/ 目录下会生成 image.gz, 可由 uboot 下载启动
make  CROSS_COMPILE=aarch64-none-linux-gnu- ARCH=arm64 -j4

make help

help 命令可以帮助我们查看基本的编译目标与命令,如果对 makefile 工程不熟悉,建议先运行此命令,熟悉主要的编译目标与作用:

make help

[笔记] linux 4.19 版本 Kbuild 编译流程解析_第3张图片

distclean 目标

作用为清除工程,包括所有中间文件,临时文件,目标文件,可执行文件:

make  CROSS_COMPILE=aarch64-none-linux-gnu- ARCH=arm64 distclean

[笔记] linux 4.19 版本 Kbuild 编译流程解析_第4张图片

defconfig 目标

defconfig 文件为 linux 特有的工程配置文件,控制哪些模块需要编译(编译进镜像或编译成 .ko),并会在工程根目录下生成 .config 文件。
defconfig 文件在 arch/(ARCH)/config/ 目录下:
[笔记] linux 4.19 版本 Kbuild 编译流程解析_第5张图片
执行该命令时,对应 Top-Makefile 的如下规则:

# 该规则会根据 defconfig 文件,在工程根目录下生成 .config 文件
%config: scripts_basic outputmakefile FORCE
	$(Q)$(MAKE) $(build)=scripts/kconfig $@

[笔记] linux 4.19 版本 Kbuild 编译流程解析_第6张图片

build 变量与 $(build)=dir 赋值

build 变量为 Kbuild 框架里的一个非常重要的变量,用于调用 Kbuild 编译框架,并指定编译模块的路径.
该变量定义在 scripts/Kbuild.include 文件中:
[笔记] linux 4.19 版本 Kbuild 编译流程解析_第7张图片

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

对 build 变量解析如下:

  1. 首先使用 -f 选项指定 make 要运行的 makefile 文件为 $(srctree)/scripts/Makefile.build(一般为 ./scripts/makefile.build). 这个 makefile 是 Kbuild 框架的总入口文件,所有模块的编译都需要通过这个 makefile 文件进行编译。
  2. build 变量 包含一个 obj 变量, obj 变量用来指示目标模块的路径,并实现包含目标模块下的makefile。

使用 obj 变量实现包含目标模块下的 makefile

在此只做简述,后续会详解 linux 的 Kbuild 编译框架。
对应的代码在 makefile.build 文件中:
[笔记] linux 4.19 版本 Kbuild 编译流程解析_第8张图片
通过如下代码实现包含目标模块下的 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)

如果目标模块下存在 Kbuild 文件,那么会使用 Kbuild 作为要执行的 makefile。

defconfig 规则展开

根据上述信息,可展开 defconfig 目标对应的规则:

# 该规则会根据 defconfig 文件,在工程根目录下生成 .config 文件
defconfig: scripts_basic outputmakefile FORCE
	make -f ./scripts/Makefile.build obj=scripts/kconfig defconfig

其中,依赖的 scripts_basicoutputmakefile 对应其他的目标规则,但这两个依赖主要用于生成辅助工具,暂不细究。

defconfig 的生成命令解析

命令展开为:

make -f ./scripts/Makefile.build obj=scripts/kconfig defconfig

即执行 scripts/kconfig/Makefile 文件,并生成 defconfig 目标,对应的命令如下:
[笔记] linux 4.19 版本 Kbuild 编译流程解析_第9张图片

defconfig: $(obj)/conf
ifeq ($(KBUILD_DEFCONFIG),)
	$< $(silent) --defconfig $(Kconfig)
else
ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)),)
	@$(kecho) "*** Default configuration is based on '$(KBUILD_DEFCONFIG)'"
	$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig)
else
	@$(kecho) "*** Default configuration is based on target '$(KBUILD_DEFCONFIG)'"
	$(Q)$(MAKE) -f $(srctree)/Makefile $(KBUILD_DEFCONFIG)
endif
endif

展开为 $(obj)/conf --defconfig=arch/$(arm64)/configs/defconfig Kconfig ,该命令的作用就是通过 defconfig 文件,生成 .config 文件。

make 默认目标生成 image.gz 镜像与 .dtb 设备树文件

当 make 不指定目标时,会使用 makefile 的第一个目标 _all 作为默认目标:
[笔记] linux 4.19 版本 Kbuild 编译流程解析_第10张图片
_all 目标会被会被覆盖:
[笔记] linux 4.19 版本 Kbuild 编译流程解析_第11张图片
此时默认编译目标为 all, all 目标对应的规则为:
[笔记] linux 4.19 版本 Kbuild 编译流程解析_第12张图片
虽然指定了:

# 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

但是在 include arch/$(SRCARCH)/Makefile 文件中,会被覆盖为:

all:	Image.gz $(KBUILD_DTBS)

[笔记] linux 4.19 版本 Kbuild 编译流程解析_第13张图片
通过如上的目标规则转换,make 默认目标的编译会生成 image.gz, vmlinux, *.dtb 文件。

主要分析 vmlinux 编译生成流程。

vmlinux 编译生成流程

这部分需要通读一遍 Top-Makefile, 以下依次列出 vmlinux 的目标规则转换路径,并说明:

  1. vmlinux 目标主要依赖 vmlinux-dep 目标:
vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(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
	+$(call if_changed,link-vmlinux)
  1. vmlinux-dep 变量包含所有的目标文件与目标模块子目录:
# 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)

vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN) $(KBUILD_VMLINUX_LIBS)
  1. vmlinux-dep 目标依赖 vmlinux-dirs 目标:
# The actual objects are generated when descending,
# make sure no implicit rule kicks in
$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;
  1. vmlinux-dirs 变量是一个包含目录各个目录的变量,用于对 build 变量赋值:
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)))
  1. vmlinux-dirs 目标实现对所有子模块的递归编译:
# 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)=$@ need-builtin=1

最终,通过第5步的 $(Q)$(MAKE) $(build)=$@ need-builtin=1, 会调用 Kbuild 编译框架,对目标模块进行编译,而在 Makefile.build 文件中,同样会处理子目录的递归编译。此处暂不细究,需要通读 Kbuild 框架相关的 Makefile.

子模块目录的指定(阅读 Kbuild 框架可知)

通过变量 init-y/m, core-y/m, drivers-y/m,net-y/m, libs-y/m, virt-y 来实现需要编译的子目录

编译目标文件的指定(阅读 Kbuild 框架可知)

通过变量 obj-y/m(以及其他变量),可以控制需要编译生成哪些目标文件

目标文件是编译进内核还是编译为 .ko 模块(阅读 Kbuild 框架可知)

变量obj-y 所指定的目标文件会编译进内核;
变量 obj-m 所指定的目标文件会编译为模块。

你可能感兴趣的:(linux,笔记)