1. Kbuild简介
- Kernel build,用来编译 Linux 内核
- 基于 GNU make 设计,对 Makefile 进行扩充
- 菜单式配置:Kconfig
- 预定义目标和变量:xx_defconfig、menuconfig、obj-y
- 跨平台工具、递归式 Makefile
- Linux 模块化设计、高度可以裁剪
- 模块机制
- Kbuild 子系统
2. Kbuild工作流程
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_defconfig
make menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- uImage LOADADDR=0x60003000
2.1 编译三步骤
- 配置阶段:编译平台、目标、配置文件
- 编译阶段:解析 Makefile、建立目标依赖关系、按照依赖关系依次生成各个目标及目标依赖
- 安装阶段:
- 桌面 PC:内核镜像安装、模块安装、头文件安装
- 嵌入式:根文件系统、Flash镜像制作等
2.2 Makefile 中的预定义
- 预定义目标:
- xxx_defconfig 、menuconfig 、gconfig
- vmlinux、bzImage、zImage
- modules、install、modules_install
- clean、mrproper、distclean
- 预定义变量:
- ARCH、CROSS_COMPILE
- obj-m、obj-y、xxx-objs
2.3 Config symbols
obj-$(CONFIG_HELLO) | |
---|---|
config symbol | <== .config |
配置变量 | config entry <== Kconfig 文件 <== menuconfig |
CONFIG_XXX | config entry <== xxx_defconfig |
3. Kbuild 编译系统构成
- Kbuild 本质
一个可扩展、可配置的 Makefile 框架
递归式 Makefile、菜单式配置 - 构成:
- Makefile:顶层目录下的Makefile
- .config:内核的配置文件
- arch/$(ARCH)/Makefile:跟平台架构相关的Makefile
- scripts/Makefile.*:通用编译规则
- Kbuild Makefile:分布在各个子目录下
- Kconfig:配置菜单,定义每个config symbol的属性(类型、描述、依赖等)
4. Kconfig 简介
4.1 Kconfig作用
- 用来生成配置菜单,配置各种 config symbol,生成对应的配置变量:CONFIG_XXX
- 每个目录下都有一个 Kconfig 文件
- 各个 Kconfig 文件通过 source 命令构建多级菜单
- 解析工具:scripts/kconfig/*conf
4.2 实验:内核模块添加配置菜单
4.3 Kconfig 语法
- config:用来定义菜单选项
- menuconfig
- choice / endchooice
- comment
- if / endif
- source:生成一个树型菜单
- (后面几节内容讲解)
5. Kconfig 菜单条目
- 菜单示例
config HELLOtristate "A hello module test"helpa simple kernel module test
- 实验:
在内核目录下新建一个test目录,用来实验 Kconfig。
6. 依赖关系:depends on
- 内核中的依赖关系示例
config STACKPROTECTORbool "Stack Protector buffer overflow detection"depends on $(cc-option,-fstack-protector)config CC_HAS_ASM_GOTOdef_bool $(success,$(srctree)/scripts/gcc-goto.sh $(CC))config USB_DISKdepends on TEST && m
7. 反向依赖:select / imply
7.1 select
如果 TEST 被选中的话,RTC 默认也会被 “强制性”选中
具体效果可以实验下。
7.2 弱反向依赖:imply
具体效果可以实验下。
8. 内核配置中的反向依赖
示例 1:一个子系统绑定(依赖)几个驱动,当用户选择这个子系统中,这几个关联的驱动都会自动选中。
A_init()
{
if (IS_REACHABLE(CONFIG_C))
C_register(&a);
…
}
示例 2:
# Generic IOMAP is used to ...
config HAVE_GENERIC_IOMAPconfig GENERIC_IOMAPdepends on HAVE_GENERIC_IOMAP && FOOlib/Makefile :obj-$(CONFIG_GENERIC_IOMAP) += iomap.o# For each architecture using the generic IOMAP functionality we would see:config X86select ...select HAVE_GENERIC_IOMAPselect ...
9. Kconfig 菜单:menuconfig
等价于如下的去掉 depens on 使用if方式:
如果是如下的方式,会破坏 A 和 A0、A1 的依赖的关系,同理 B 和 B0、B1
(A 和 A0、A1 必须紧跟着,才能保持依赖关系,同理 B 和 B0、B1)
10. Kconfig 互斥选择:choice / endchoice
11. Kbuild 子菜单
- 生成子菜单的两种方法:
11.1 方法一
- 通过依赖关系生成菜单
- 若菜单条目依赖前项,则其为该选项的子菜单
config Abool "A configuration"
config Bbool "B configuration"depends on A
11.2 方法二
- 子菜单:menu / endmenu
- 所有的菜单条目都在 menu 和 endmenu 之间的块中
- 子菜单会继承父菜单的依赖关系
menu "test menu"
config xxx_1
…
config xxx_2
endmenu
12. 更多编译目标
make config
make nconfig:基于文本的菜单配置
make menuconfig:依赖 ncurses 图形库
# apt-get install libncurses5-dev
make xconfig:基于窗口的配置菜单,依赖Qt库
# add-apt-repository ppa:rock-core/qt4
# apt-get install libqt4-dev
make gconfig:基于GTK的菜单配置
# apt-get install gtk+-2.0 glib-2.0 libglade2-dev
.config:
make clean
make mrproper
make distclean
13. 文件 .config
13.1 简介
- .config 文件是如何生成的?
- .config 文件里都是什么?
- .config 文件有什么用?如何参与编译工作?
- 参考:scripts/kconfig/mconf.c、conf.c
13.2 .config 的生成
13.2.1 .config 生成的第一阶段
- make vexpress_defconfig ==> .config
- make menuconfig ==> .config
13.2.2 .config 生成的第二阶段
- .config ==> syncconfig ==> Makefile
include/config/auto.conf:用来配置Makefile
include/generated/autoconf.h:供C程序引用
include/config/*.h:空头文件,用于构建依赖关系
# Makefile:
KCONFIG_CONFIG ?= .config
cmd_syncconfig = $(MAKE) -f $(srctree)/Makefile syncconfig
PHONY += include/config/auto.conf
%/config/auto.conf %/generated/autoconf.h : $(KCONFIG_CONFIG)+$(call cmd,syncconfig)# include/config/auto.conf:
deps_config := kernel/trace/Kconfig certs/Kconfig fs/udf/Kconfig … include/config/auto.conf: \$(deps_config)# scripts/Kbuild.include:
cmd = @set -e; $(echo-cmd) $(cmd_$(1))
13.3 .config 如何参与编程
- .config ==> syncconfig ==> include/config/auto.conf
- .config ==> syncconfig ==> include/config/tristate.conf
在 Makefile 中引用 auto.conf 定义的配置变量(config symbols)。示例:
# 顶层 Makefile:
need-config := 1
ifdef need-config
include include/config/auto.conf
endif# include include/config/auto.conf:
CONFIG_USB=y# drivers/usb/Makefile:
obj-$(CONFIG_USB) += core/
13.4 .config 如何被 C 语言引用
- .config ==> syncconfig ==> include/generated/autoconf.h
- 配置变量(config symbols) –> C 语言的宏定义
在 C 程序中引用 autoconf.h 定义的宏:
// include/generated/autoconf.h:
#define CONFIG_USB_MON 1
// include/linux/usb.h:
#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
struct mon_bus *mon_bus; /* non-null when associated */
int monitored; /* non-zero when monitored */
#endif
gcc –Iinclude include/generated/autoconf.h –c hello.c
14. Kbuild Makefile 工作流程
14.1 Linux内核镜像的流程
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j4
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- uImage LOADADDR=ox60003000
14.2 Kbuild Makefile 的构成
- 顶层Makefile:主要用来调用相应规则的Makefile
- .config:用户配置的各种选项
- arch/$(ARCH)/Makefile:跟平台相关的Makefile
- 各个目录下的Makefile:负责编译各个模块
- scripts/Makefile.* :定义各种通用规则
14.3 scripts/Makefile.* :各类规则文件
scripts/Makefile.build:通用规则,用来编译built-in.a、lib.a
scripts/Makefile.lib:负责分析obj-y、obj-y和子目录中的subdir-y等
scripts/Makefile.include:一些通用定义,被Makefile.* 包含使用
scripts/Makefile.host:编译各种主机工具
scripts/Makefile.headerinst:头文件安装规则
scripts/Makefile.modinst:模块installguize
scripts/Makefile.modpost:模块编译,由.o和.mod生成module.ko
scripts/Makefile.modsign:模块签名
scripts/Makefile.clean:clean规则,make clean时调用
14.4 Kbuild Makefile 预定义目标和变量
- obj-m:将当前文件编译为独立的模块
- obj-y:将当前文件编译进内核
- xxx-objs:一个模块依赖的多个源文件
- bzImage:
- menuconfig:
- CONFIG_xxx:
- include include/config/auto.conf
- include/config/auto.conf.cmd
14.5 Kbuild Makefile 工作流程
- 根据 ARCH 变量,首先 include arch/$(ARCH)/Makefile
- 读取 .config 文件:读取用户的各种配置变量
- 解析预定义目标、目标,构建依赖关系
- 编译各个模块或组件(使用 scripts/Makefile.*)
- 将每个目录下的源文件编译为对应的 .o 目标文件
- 将 .o 目标文件归档为 built-in.a
- 将所有对象链接成 vmlinux
- 编译模块…
15. vmlinux 编译过程分析
15.1 内核镜像编译流程
15.2 默认目标的依赖:vmlinux
# Top Makefile:
# That's our default target when none is given on the command line
PHONY := __all
__all: all
all: vmlinux
vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps)+$(call if_changed,link-vmlinux)
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_OBJS) $(KBUILD_VMLINUX_LIBS)export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
KBUILD_VMLINUX_OBJS := $(head-y) $(patsubst %/,%/built-in.a, $(core-y))
KBUILD_VMLINUX_OBJS += $(addsuffix built-in.a, $(filter %/, $(libs-y)))
KBUILD_VMLINUX_OBJS += $(patsubst %/,%/built-in.a, $(drivers-y))
15.2.1 KBUILD_VMLINUX_OBJS 变量
# arch/arm/kernel/Makefile:
head-y := arch/arm/kernel/head$(MMUEXT).o
core-y += arch/arm/
core-y += $(machdirs) $(platdirs)
libs-y := arch/arm/lib/ $(libs-y)# Makefile:
core-y := init/ usr/
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/
libs-y := lib/
drivers-y := drivers/ sound/
drivers-y += net/ virt/
展开后:
KBUILD_VMLINUX_OBJS := arch/arm/kernel/head.o arch/arm/built-in.a init/built-in.a usr/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 lib/built-in.a arch/arm/lib/built-in.a drivers/built-in.a sound/built-in.a net/built-in.a virt/ built-in.a
15.2.2 KBUILD_VMLINUX_LIBS 变量
KBUILD_VMLINUX_LIBS := $(patsubst %/, %/lib.a, $(libs-y))
libs-y := lib/
libs-y := arch/arm/lib/ $(libs-y)
展开后:
KBUILD_VMLINUX_LIBS := lib/lib.a arch/arm/lib/lib.a
15.2.3 autoksyms_recursive
autoksyms_recursive: descend modules.order$(Q)$(CONFIG_SHELL) $(srctree)/scripts/adjust_autoksyms.sh \"$(MAKE) -f $(srctree)/Makefile vmlinux"
15.3 生成 vmlinux 的规则
# Makefile:
vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps)+$(call if_changed,link-vmlinux)# scripts/Kbuild.include:
if_changed = $(if $(newer-prereqs)$(cmd-check), \$(cmd); \printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
cmd = @set -e; $(echo-cmd) $(cmd_$(1))
# Makefile:
cmd_link-vmlinux = \$(CONFIG_SHELL) $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)";$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
# SHELL used by kbuild
CONFIG_SHELL := sh
LDFLAGS_vmlinux = --build-id=sha1 --orphan-handling=warn
展开后:
cmd_link-vmlinux = sh scripts/link-vmlinux.sh "ld.lld""--build-id=sha1 --orphan-handling=warn";true
- scripts/link-vmlinux.sh 脚本
- 链接 $(KBUILD_VMLINUX_OBJS) 中的所有 built-in.a
- 链接 $(KBUILD_VMLINUX_LIBS)
- 符号处理:生成 system.map、include/generated/autoksyms.h 等文件
modpost_link()
{local objectsobjects="--whole-archive \${KBUILD_VMLINUX_OBJS} \--no-whole-archive \--start-group \${KBUILD_VMLINUX_LIBS} \--end-group"${LD} ${KBUILD_LDFLAGS} -r -o ${1} ${objects}
}
16. built-in.a 生成分析
默认目标的依赖分析:
Makefile:
vmlinux-dirs := ( p a t s u b s t (patsubst %/,%, (patsubst(filter %/,
$(core-y) $(core-m) $(drivers-y) $(drivers-m)
$(libs-y) $(libs-m)))
build-dirs:= $(vmlinux-dirs)
展开后:
build-dirs:= init lib drivers net sound certs crypto ipc kernel mm …
PHONY += descend $(build-dirs)
$(build-dirs): prepare
( Q ) (Q) (Q)(MAKE) ( b u i l d ) = (build)= (build)=@
single-build=$(if $(filter-out $@/, $(filter $@/%, $(KBUILD_SINGLE_TARGETS))),1)
need-builtin=1 need-modorder=1
KBUILD_SINGLE_TARGETS := $(addprefix $(extmod-prefix), $(single-no-ko))
extmod-prefix = $(if ( K B U I L D E X T M O D ) , (KBUILD_EXTMOD), (KBUILDEXTMOD),(KBUILD_EXTMOD)/)
single-no-ko := $(sort $(patsubst %.ko, %.mod, $(MAKECMDGOALS)))
Kbuild.include:
build := -f $(srctree)/scripts/Makefile.build obj
●示例:编译sound目录
__build展开后:
__build: sound/built-in.a
●sound/built-in.a:
– 单文件模块:obj-y=hello.o
– 复合模块:
obj-y=hello.o hello-y= a.o b.o c.o
obj-y=hello.o hello-objs= a.o b.o c.o
– 子目录:obj-y=subdir
scripts/Makefile.build:
$(obj)/built-in.a: $(real-obj-y) FORCE
$(call if_changed,ar_builtin)
cmd_ar_builtin = rm -f $@; $(AR) cDPrST $@ $(real-prereqs)
real-prereqs = $(filter-out $(PHONY), $^)
real-obj-y := $(foreach m, $(obj-y), $(if $(strip ( ( ((m:.o=-objs)) ( ( ((m:.o=-y)) ( ( ((m:.o=-))), ( ( ((m:.o=-objs)) ( ( ((m:.o=-y)),$(m)))
real-obj-y := $(addprefix ( o b j ) / , (obj)/, (obj)/,(real-obj-y))
17.单个目标文件生成分析
scripts/Makefile.build:
$(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE
$(call if_changed_rule,cc_o_c)
$(call cmd,force_checksrc)
define rule_cc_o_c
$(call cmd_and_fixdep,cc_o_c)
$(call cmd,gen_ksymdeps)
$(call cmd,checksrc)
$(call cmd,checkdoc)
$(call cmd,objtool)
$(call cmd,modversions_c)
$(call cmd,record_mcount)
endef
scripts/Kbuild.include:
if_changed_rule = $(if ( n e w e r − p r e r e q s ) (newer-prereqs) (newer−prereqs)(cmd-check),KaTeX parse error: Expected group after '_' at position 6: (rule_̲(1)),@😃
cmd = @set -e; $(echo-cmd) KaTeX parse error: Expected group after '_' at position 5: (cmd_̲(1))
cmd_and_fixdep =
$(cmd);
scripts/basic/fixdep $(depfile) @ ′ @ ' @′(make-cmd)’ > $(dot-target).cmd;
rm -f $(depfile)
quiet_cmd_cc_o_c = CC $(quiet_modtag) $@
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
cmd_force_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $<
Makefile:
CHECK = sparse
CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__
-Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF)
命令展开后:
%.o : %.c
gcc –c –o $@ %.c
sparse -D__linux__ -Dlinux -D__STDC__ …
●总结:
.c ==> .o ==> 递归生成built-in.a ==> vmlinux
18.zImage生成分析
18.1 内核镜像生成过程
…
CC sound/xx.o
CC xxxx/xxx.o
…
LD vmlinux
SORTTAB vmlinux
SYSMAP System.map
OBJCOPY arch/arm/boot/Image
Kernel: arch/arm/boot/Image is ready
GZIP arch/arm/boot/compressed/piggy_data
LD arch/arm/boot/compressed/vmlinux
OBJCOPY arch/arm/boot/zImage
Kernel: arch/arm/boot/zImage is ready
UIMAGE arch/arm/boot/uImage
18.2 Image镜像生成分析
arch/arm/Makefile:
KBUILD_IMAGE := $(boot)/zImage
all: $(notdir $(KBUILD_IMAGE))
zImage: Image
BOOT_TARGETS = zImage Image uImage
$(BOOT_TARGETS): vmlinux
( Q ) (Q) (Q)(MAKE) ( b u i l d ) = (build)= (build)=(boot) MACHINE=$(MACHINE) ( b o o t ) / (boot)/ (boot)/@
@$(kecho) ’ Kernel: ( b o o t ) / (boot)/ (boot)/@ is ready’
其中:
build := -f ( s r c t r e e ) / s c r i p t s / M a k e f i l e . b u i l d o b j b o o t : = a r c h / a r m / b o o t m a c h i n e − y 为 空 , M A C H I N E : = a r c h / a r m / m a c h − (srctree)/scripts/Makefile.build obj boot := arch/arm/boot machine-y为空,MACHINE := arch/arm/mach- (srctree)/scripts/Makefile.buildobjboot:=arch/arm/bootmachine−y为空,MACHINE:=arch/arm/mach−(word 1,$(machine-y))/
所以 MACHINE :=
boot := arch/arm/boot
zImage: vmlinux
make -f scripts/Makefile.build obj=arch/arm/boot arch/arm/boot/zImage
Image: vmlinux
make -f scripts/Makefile.build obj=arch/arm/boot arch/arm/boot/Image
arch/arm/Makefile:
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
( Q ) (Q) (Q)(MAKE) ( b u i l d ) = (build)= (build)=(obj)/compressed $@
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
展开后:
arch/arm/boot/Image: vmlinux
arm-linux-gnueabi-objcopy -O binary -R .comment -S vmlinux Image
arch/arm/boot/compressed/vmlinux:arch/arm/boot/Image
make -f scripts/Makefile.build obj=arch/arm/boot/compressed vmlinux
arch/arm/boot/zImage: arch/arm/boot/compressed/vmlinux
arm-linux-gnueabi-objcopy -O binary -R .comment -S vmlinux zImage
●分析一下其中的Image的生成:
arch/arm/boot/Image: vmlinux
arm-linux-gnueabi-objcopy -O binary -R .comment -S vmlinux Image
参数说明:
-O:生成一个二进制文件
-R:从一个目标文件中删除指定的section
-S:–strip-all,全方位压缩vmlinux文件
18.3 piggy.o生成分析
$(obj)/piggy.o: $(obj)/piggy_data
$(obj)/piggy_data: $(obj)/…/Image FORCE
( c a l l i f c h a n g e d , (call if_changed, (callifchanged,(compress-y))
.config:
compress-$(CONFIG_KERNEL_GZIP) = gzip
Makefile.lib:
cmd_gzip = cat $(real-prereqs) | $(KGZIP) -n -f -9 > $@
KGZIP = gzip
18.4 arch/arm/boot/compressed/vmlinux生成分析
arch/arm/boot/compressed/vmlinux: arch/arm/boot/Image
make -f scripts/Makefile.build obj=arch/arm/boot/compressed vmlinux
arch/arm/boot/compressed/Makefile:
$(obj)/vmlinux: $(obj)/vmlinux.lds ( o b j ) / (obj)/ (obj)/(HEAD) $(obj)/piggy.o
$(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3)
$(bswapsdi2) $(efi-obj-y) FORCE
( c a l l i f c h a n g e d , l d ) @ (call if_changed,ld) @ (callifchanged,ld)@(check_for_bad_syms)
其中:
HEAD = head.o
OBJS += misc.o decompress.o
cmd_ld = $(LD) $(ld_flags) $(real-prereqs) -o $@
LD = $(CROSS_COMPILE)ld
ld_flags = $(KBUILD_LDFLAGS) $(ldflags-y) KaTeX parse error: Expected group after '_' at position 9: (LDFLAGS_̲(@F))
ldflags-y += $(EXTRA_LDFLAGS),其中EXTRA_LDFLAGS为空
real-prereqs = $(filter-out $(PHONY), $^)
展开后:
arch/arm/boot/compressed/vmlinux: vmlinux.lds head.o misc.o decompress.o piggy.o
arm-linux-gnueabi-ld -EL vmlinux.lds compressed/head.o compressed/piggy.o
18.5 zImage镜像生成分析
arch/arm/boot/zImage: arch/arm/boot/compressed/vmlinux
arm-linux-gnueabi-objcopy -O binary -R .comment -S vmlinux zImage
参数说明:
-O:生成一个二进制文件
-R:从一个目标文件中删除指定的section
-S:–strip-all,全方位压缩vmlinux文件
19.uImage镜像生成分析
19.1 uImage镜像生成分析
mkimage –A arm -O linux –T kernel –C none –a 0x60003000 –e 0x60003000 -d zImage uImage
mkimage参数说明:
-A:指定CPU架构类型
-O:指定操作系统类型
-T:指定image类型
-C:采用的压缩方式:none、gzip、bzip2等
-a:内核加载地址
-e:内核镜像入口地址
19.2 uImage启动过程
-a ==> set load address to ‘addr’ (hex) —— 镜像的加载地址
-e ==> set entry point to ‘ep’ (hex) —— 镜像的运行地址
首先要知道mkimage做出来的文件,会有64字节的描述信息在文件头部。而Uboot会将镜像文件加载到 -a指定的地址,并跳到 -e地址运行。
这里有几种情况:
1、uImage下载到内存的地址,和 -a指定的地址相同,uboot不会对镜像做内存搬移。
2、uImage下载到内存的地址,和-a指定的地址不同,uboot会将下载地址的uImage镜像文件(去掉64字节头部信息)搬移到-a指定的地址。
3、-a和 -e参数地址相同时,理论上,需要uImage的下载地址和 -a地址不同,镜像做过搬移后,到 -e地址运行是正常的。
4、-a和 -e参数地址不同,固定为 -a + 0x40= -e,镜像不会搬移(头部64字节信息不会去掉),到 -e地址运行才会正常。
20.内核模块编译分析
20.1 内核模块编译信息
CC [M] drivers/char/hello.o
MODPOST Module.symvers
CC [M] drivers/char/hello.mod.o
LD [M] drivers/char/hello.ko
20.2 内核模块编译步骤
1.步骤一:
将每个源文件编译为对应的 .o 目标文件
将单个或多个 .o 目标文件链接成模块文件module.o
生成对应的module.mod文件
生成modules.order文件,里面保存所有的ko文件信息
●流程示例:
add.o / sub.o / … ==> hello.o ==> hello.mod ==> modules.order
或者hello.o ==> hello.mod ==> modules.order
2.步骤二:
从modules.order文件中查找所有的ko文件
使用modpost,为每个ko模块创建module.mod.c文件
创建Module.symvers文件,保存导出的符号(EXPORT_SYMBOL)及CRC值
将module.o和module.mod.o链接成module.ko
●流程示例:
modules.order ==> hello.mod.c ==> Module.symvers
hello.o + hello.mod.o ==> hello.ko
3.步骤三:
生成和内核模块相关的信息:版本魔幻数
License、versions、alias
20.3 此前实验代码示例
20.4 模块编译对应的Makefile
●modules目标对应的规则:
Makefile:
modules: $(if $(KBUILD_BUILTIN),vmlinux) modules_check modules_prepare
( Q ) (Q) (Q)(MAKE) -f $(srctree)/scripts/Makefile.modpost
scripts/Makefile.modpost:
PHONY := __modpost
__modpost: $(output-symdump)
( Q ) (Q) (Q)(MAKE) -f $(srctree)/scripts/Makefile.modfinal
scripts/Makefile.modfinal:
PHONY := __modfinal
__modfinal: $(modules)
find all .ko modules listed in modules.order
modules := $(sort $(shell cat $(MODORDER)))
cat modules.order
drivers/char/hello.ko
scripts/Makefile.modfinal:
( m o d u l e s ) : + (modules): %.ko : %.o %.mod.o scripts/module.lds FORCE + (modules):+(call if_changed,ld_ko_o)
cmd_ld_ko_o =
$(LD) -r $(KBUILD_LDFLAGS) $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE)
-T scripts/module.lds -o $@ $(filter %.o, $^);
$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
21.modules_install过程分析
模块安装信息:
●模块安装对应的Makefile
Makefile:
modules_install: emodinst _emodinst_post
emodinst:
$(Q)mkdir -p ( M O D L I B ) / (MODLIB)/ (MODLIB)/(install-dir)
( Q ) (Q) (Q)(MAKE) -f $(srctree)/scripts/Makefile.modinst
_emodinst_post: emodinst
$(call cmd,depmod)
scripts/Makefile.modinst:
PHONY := __modinst
__modinst: $(modules)
modules := $(sort $(shell cat $(if ( K B U I L D E X T M O D ) , (KBUILD_EXTMOD), (KBUILDEXTMOD),(KBUILD_EXTMOD)/)modules.order))
$(modules):
( c a l l c m d , m o d u l e s i n s t a l l , (call cmd,modules_install, (callcmd,modulesinstall,(MODLIB)/$(modinst_dir))
modinst_dir = $(if ( K B U I L D E X T M O D ) , (KBUILD_EXTMOD), (KBUILDEXTMOD),(ext-mod-dir),kernel/$(@D))
cmd_modules_install =
mkdir -p $(2) ;
cp $@ $(2) ;
$(mod_strip_cmd) ( 2 ) / (2)/ (2)/(notdir $@) ;
$(mod_sign_cmd) ( 2 ) / (2)/ (2)/(notdir $@) ( p a t s u b s t (patsubst %,|| true, (patsubst(KBUILD_EXTMOD)) ;
$(mod_compress_cmd) ( 2 ) / (2)/ (2)/(notdir $@)
22.headers_install过程分析
●目标header对应的规则
Makefile:
headers: $(version_h) scripts_unifdef uapi-asm-generic archheaders archscripts
$(if $(wildcard ( s r c t r e e ) / a r c h / (srctree)/arch/ (srctree)/arch/(SRCARCH)/include/uapi/asm/Kbuild),
$(error Headers not exportable for the $(SRCARCH) architecture))
( Q ) (Q) (Q)(MAKE) $(hdr-inst)=include/uapi
( Q ) (Q) (Q)(MAKE) ( h d r − i n s t ) = a r c h / (hdr-inst)=arch/ (hdr−inst)=arch/(SRCARCH)/include/uapi
其中:
hdr-inst := -f $(srctree)/scripts/Makefile.headersinst obj
简化一下
headers: $(version_h) scripts_unifdef uapi-asm-generic archheaders archscripts
make -f scripts/Makefile.headersinst obj=include/uapi
make -f scripts/Makefile.headersinst obj=arch/arm/include/uapi
scripts/Makefile.headersinst:
PHONY := __headers
__headers: $(all-headers)
$(call cmd,remove)
all-headers := $(src-headers) $(gen-headers)
src-headers := $(if $(src-subdirs), $(shell cd $(src) && find $(src-subdirs) -name ‘*.h’))
其中:
src = include/uapi
src-headers = include/uapi/$(src-subdirs)/.h
src-headers := $(filter-out $(no-export-headers), $(src-headers))
src-headers := include/uapi/asm-generic/.h include/uapi/linux/.hinclude/uapi/sound/.h …
●src-headers对应的规则:
$(src-headers): $(dst)/%.h: $(src)/%.h $(srctree)/scripts/headers_install.sh FORCE
$(call if_changed,install)
其中:
src = include/uapi
dst := usr/include
cmd_install = $(CONFIG_SHELL) $(srctree)/scripts/headers_install.sh $< $@
● gen-headers对应的规则:
scripts/Makefile.headersinst:
gen := ( o b j t r e e ) / (objtree)/ (objtree)/(subst include/,include/generated/,$(obj))
gen-headers := $(if $(gen-subdirs), $(shell cd $(gen) && find $(gen-subdirs) -name ‘*.h’))
$(gen-headers): $(dst)/%.h: $(gen)/%.h $(srctree)/scripts/headers_install.sh FORCE
$(call if_changed,install)
其中:
gen = include/generated/uapi
dst := usr/include
cmd_install = $(CONFIG_SHELL) $(srctree)/scripts/headers_install.sh $< $@
23.内核中的空头文件探秘
●前面内容:
.config生成的三个主要文件:
– include/config/auto.conf:用来配置Makefile
– include/generated/autoconf.h:在C程序中引用
– include/config/.h:
●Kbuild Makefile
跟踪三种依赖关系:(修改下面的任意一项,编译都能够跟踪到修改的依赖项)
– 编译所需要的所有源文件:.c
– 源文件*.c中包含的各种头文件:*.h
– 所有程序中使用的配置选项:CONFIG_XX(内核会生成对应的空头文件)
#include <xx.h>
#ifdef CONFIG_SMP
__boot_cpu_id = cpu;
#endif
23.1 示例:头文件依赖
23.1.1 方式一
没有添加对 pi.h 文件的依赖,修改PI从3.14到3.1415926时,a.out没有对pi.h产生依赖关系,所以:
在Makefile中添加对pi.h的依赖:
23.1.2 方式二
●gcc的-MD编译选项
生成文件关联的信息,输出的信息导入到hello_pi.d的文件里面。
●在Makefile中包含hello_pi.d文件
23.2 内核中头文件依赖
23.2.1 内核文件编译示例
scripts/Makefile.lib:
c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE)
-include $(srctree)/include/linux/compiler_types.h
$(__c_flags) $(modkern_cflags)
$(basename_flags) $(modname_flags)
.header_wuxiang.o.cmd:
source_drivers/char/header_wuxiang.o := drivers/char/header_wuxiang.c
deps_drivers/char/header_wuxiang.o :=
$(wildcard include/config/smp.h)
$(wildcard include/config/wuxiang.h)
include/linux/kconfig.h
$(wildcard include/config/cc/version/text.h)
…
drivers/char/pi.h \
drivers/char/header_wuxiang.o: $(deps_drivers/char/header_wuxiang.o)
$(deps_drivers/char/header_wuxiang.o):
Makefile.build:
$(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_dep) FORCE
$(call if_changed_rule,cc_o_c)
-include ( f o r e a c h f , (foreach f, (foreachf,(existing-targets),$(dir ( f ) ) . (f)). (f)).(notdir $(f)).cmd)
existing-targets := $(wildcard $(sort $(targets)))
targets += $(targets-for-builtin) $(targets-for-modules)
Kbuild.include:
Execute the command and also postprocess generated .d dependencies file.
if_changed_dep = $(if ( n e w e r − p r e r e q s ) (newer-prereqs) (newer−prereqs)(cmd-check),$(cmd_and_fixdep),@😃
cmd_and_fixdep =
$(cmd);
scripts/basic/fixdep $(depfile) @ ′ @ ' @′(make-cmd)’ > $(dot-target).cmd;
rm -f $(depfile)
depfile = $(subst ( c o m m a ) , , (comma),_, (comma),,(dot-target).d) # depfile保存gcc -MD生成的依赖文件
Name of target with a ‘.’ as filename prefix. foo/bar.o => foo/.bar.o
dot-target = $(dir @ ) . @). @).(notdir $@)