目录:
1.顶层makefile简介
2.压缩内核镜像bzimage构建
2.1 bzimage由来
2.2 setup.bin构建
2.3 vmlinux.bin构建
摘要:
本文假定读者具有的前提:粗略的makefile知识,编译x86平台linux内核,内核版本为2.6.35.13-正在执行如下动作make menuconfig,make bzimage, make modules , make modules_install。
1 顶层makefile简介
怎么配置内核?
先定义config-targets := 0,在这个逻辑中:
$(srctree)/Makefile:
427:ifeq ($(KBUILD_EXTMOD),) // KBUILD_EXTMOD变量用于选定module编译到指定目录,默认目录为代码树的module目录 ifneq ($(filter config %config,$(MAKECMDGOALS)),) // MAKECMDGOALS为执行make时命令行中的make的参数。 config-targets := 1 ifneq ($(filter-out config %config,$(MAKECMDGOALS)),) mixed-targets := 1 endif endif 434:endif |
判断此刻不是编译module,如果有config对象,则config-targets赋值为1;若还有其他对象,则为mixed-targets,mixed-targets指同时包括config对象和镜像对象(注:行号,对应的版本2.6.35.13版本的makefile,若是其他版本可以忽略,由于添加注释缘故,行号会有些出入,所以行号本身并不重要,但余念想彼时大学园中,孤诣coding,凡事必究其原因,大耗精力,若今日有此类读者,故添之,亦不必强迫纠结无行号之痒)。
顶层makefile的主要逻辑:
$(srctree)/Makefile:
436:ifeq ($(mixed-targets),1) //编译混合对象 444:else 445: ifeq ($(config-targets),1) include $(srctree)/arch/$(SRCARCH)/Makefile // $(srctree)变量即为当前代码树的目录 //编译配置对象 464: else include $(srctree)/arch/$(SRCARCH)/Makefile // 编译vmlinux // 编译modules 1376: endif 1377:endif |
怎么得到特定体系结构?
$(srctree)/Makefile:
162: SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/ -e s/s390x/s390/ -e s/parisc64/parisc/ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ -e s/sh[234].*/sh/ ) 189: ARCH ?= $(SUBARCH) |
// 若ARCH没有定义,则将SUBARCH的值赋给ARCH,若需要交叉编译其他平台的内核,则需要执行:
make ARCH=arm menuconfig,make ARCH=arm bzimage, make ARCH=arm modules,再下载到开发板上,当然这个不代表具体命令,毕竟我没有做过这方面的开发。
2 bzimage构建
2.1 bzimage由来
在顶层makefile搜索一下bziamge没有发现bzimage对象,bzimage对象与体系结构相关,故在体系结构的makefile里面构建。$(srctree)/arch/x86/makefile搜索bzimage:
$(srctree)/arch/x86/Makefile:
156:bzImage: vmlinux ifeq ($(CONFIG_X86_DECODER_SELFTEST),y) $(Q)$(MAKE) $(build)=arch/x86/tools posttest endif $(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE) $(Q)mkdir -p $(objtree)/arch/$(UTS_MACHINE)/boot 162: $(Q)ln -fsn ../../x86/boot/bzImage $(objtree)/arch/$(UTS_MACHINE)/boot/$@ |
注释:
1. 变量boot,其值arch/x86/boot。顶层makefile定义export KBUILD_IMAGE ?= vmlinux,后在体系结构的makefile更改:
$(srctree)/arch/x86/Makefile:
154:KBUILD_IMAGE := $(boot)/bzImage
2. 变量Q-make程序默认会将执行的动作打印出来,但是若makefile里面命令前加了“@”符号,则不会打印,变量Q即控制是否需要quiet编译(不打印make执行的命令);变量Q定义:
$(srctree)/Makefile:
287:ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
290:else
quiet=quiet_
Q = @
293:endif
3. 变量build-linux内核将一些通用的规则,写成统一的脚本,就想函数一样便于调用。
$(srctree)/scripts/kbuild.include:
###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
150:build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj // if为makefile函数,这行逻辑用c语言会这样写:
if ( $(KBUILD_SRC) )
$(srctree)/
即若$(KBUILD_SRC)有定义,定义build := -f $(srctree)/scripts/Makefile.build obj= ,否则定义build := -f scripts/Makefile.build obj=。
4. $(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE),可以表示如下信息: make -f scripts/Makefile.build obj=arch/x86/boot arch/x86/boot/bzimage 。 -f参数指定使用的makefile为scripts/Makefile.build。这条规则是一条普遍的规则,使用比较多,在此讲一下其逻辑:
$(srctree)/scripts/Makefile.build:
5:src := $(obj)
42:kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) // makefile内建函数-filter函数过滤掉文件,留下目录。makefile内建函数一般是这种调用模式:$(函数名 参数1,参数2)
43:kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) // makefile内建函数-wildcard函数判断文件是否存在 makefile内建函数-if函数,条件判断。$(if 条件判断,条件成立执行语句,条件不成立执行语句)
44:include $(kbuild-file) // 上面的逻辑即包括boot目录下的makefile,若有kbuild,则包括kbuild。即include arch/x86/boot/Makefile(该目录下只有Makefile)
arch/x86/boot/Makefile:
77: quiet_cmd_image = BUILD $@ 78: cmd_image = $(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin $(ROOT_DEV) > $@
81: $(obj)/bzImage: $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/tools/build FORCE |
当依赖更新时,重新调用cmd_image命令。这个逻辑如下:
# Execute command if command has changed or prerequisite(s) are updated.
#
if_changed = $(if $(strip $(any-prereq) $(arg-check)),
@set -e;
$(echo-cmd) $(cmd_$(1));
echo ‘cmd_$@ := $(make-cmd)’ > $(dot-target).cmd)
bzimage压缩内核由arch/x86/boot/setup.bin和arch/x86/boot/vmlinux.bin,通过arch/x86/boot/tools/build组建。跟踪这两个目标即知道内核是怎么构建的。
2.2 vmlinux
$(srctree)/Makefile:
# vmlinux image – including updated kernel symbols vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE ifdef CONFIG_HEADERS_CHECK $(Q)$(MAKE) -f $(srctree)/Makefile headers_check endif ifdef CONFIG_SAMPLES $(Q)$(MAKE) $(build)=samples endif ifdef CONFIG_BUILD_DOCSRC $(Q)$(MAKE) $(build)=Documentation endif $(call vmlinux-modpost) $(call if_changed_rule,vmlinux__) $(Q)rm -f .old_version |
script/Kbuild.include:
if_changed_rule = $(if $(strip $(any-prereq) $(arg-check) ),
@set -e;
$(rule_$(1)))
其实调用rule_vmlinux__命令:
$(srctree)/Makefile:
define rule_vmlinux__ : $(if $(CONFIG_KALLSYMS),,+$(call cmd,vmlinux_version)) $(call cmd,vmlinux__) $(Q)echo ‘cmd_$@ := $(cmd_vmlinux__)’ > $(@D)/.$(@F).cmd $(Q)$(if $($(quiet)cmd_sysmap), echo ‘ $($(quiet)cmd_sysmap) System.map’ &&) $(cmd_sysmap) $@ System.map; if [ $$? -ne 0 ]; then rm -f $@; /bin/false; fi; $(verify_kallsyms) endef |
实质调用cmd命令:
cmd = @$(echo-cmd) $(cmd_$(1))
即调用cmd_vmlinux__
$(srctree)/Makefile:
cmd_vmlinux__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) -o $@ -T $(vmlinux-lds) $(vmlinux-init) –start-group $(vmlinux-main) –end-group $(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o FORCE ,$^) |
这里可以看出代码树的vmlinux的组成是$(vmlinux-init),$(vmlinux-main)以及其他。
vmlinux-init := $(head-y) $(init-y)
head-y := arch/x86/kernel/head_$(BITS).o
head-y += arch/x86/kernel/head$(BITS).o
head-y += arch/x86/kernel/head.o
head-y += arch/x86/kernel/init_task.o
2.3 arch/x86/boot/setup.bin
$(srctree)/arch/x86/boot/Makefile:
$(obj)/setup.bin: $(obj)/setup.elf FORCE $(call if_changed,objcopy) $(obj)/setup.elf: $(src)/setup.ld $(SETUP_OBJS) FORCE $(call if_changed,ld) |
$(SETUP_OBJS)这些目标即开机初始化目标,在$(srctree)/arch/x86/boot/Makefile定义。
setup-y += a20.o bioscall.o cmdline.o copy.o cpu.o cpucheck.o edd.o
setup-y += header.o main.o mca.o memory.o pm.o pmjump.o
setup-y += printf.o regs.o string.o tty.o video.o video-mode.o
setup-y += version.o
setup-$(CONFIG_X86_APM_BOOT) += apm.o
setup-y += video-vga.o
2.4 arch/x86/boot/vmlinux.bin
$(srctree)/arch/x86/boot/Makefile:
$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE $(call if_changed,objcopy) $(obj)/compressed/vmlinux: FORCE $(Q)$(MAKE) $(build)=$(obj)/compressed $@ |
$(srctree)/arch/x86/boot/compressed/Makefile:
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o $(obj)/piggy.o FORCE $(call if_changed,ld) @: $(obj)/vmlinux.bin: vmlinux FORCE $(call if_changed,objcopy) //objcopy顶层目录生成的vmlinux为当前目录vmlinux.bin
vmlinux.bin.all-y := $(obj)/vmlinux.bin suffix-$(CONFIG_KERNEL_GZIP) := gz quiet_cmd_mkpiggy = MKPIGGY $@ targets += piggy.S |
查看mkpiggy.c源码:
printf(“input_data:n”);
printf(“.incbin “%s”n”, argv[1]);
printf(“input_data_end:n”);
这几行,意思是将vmlinux.bin.gzip压缩的二进制文件包含进来。
ld和gzip命令调用,scripts/makefile.lib:
quiet_cmd_ld = LD $@ cmd_ld = $(LD) $(LDFLAGS) $(ldflags-y) $(LDFLAGS_$(@F)) $(filter-out FORCE,$^) -o $@
quiet_cmd_gzip = GZIP $@ |