调试kernel-2.6.13的makefile (分析makefile是如何编译内核和模块的)
打开主makefile,往下看。
ifdef M
ifeq ("$(origin M)", "command line")
KBUILD_EXTMOD := $(M) 把$(M)赋给KBUILD_EXTMOD
endif
endif
这个是用来构建模块的,如$(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules
一会儿会分析到。
继续往下看
# KBUILD_SRC is set on invocation of make in OBJ directory
ifeq ($(KBUILD_SRC),)这条判断在makefile被第一次make时是成功的。(这个量在目标目录被make调用时设置)
ifdef O
ifeq ("$(origin O)", "command line")
KBUILD_OUTPUT := $(O)
endif
endif
上面的作用就是可以在命令行上确定make的输出目录。不感兴趣。
然后就找到了第一个依赖关系。:-)
.PHONY: _all
_all:
也就是说_all这个伪目标是make的总目标
endif # ifeq ($(KBUILD_SRC),)
然后就结束了
ifeq ($(skip-makefile),)
第一次被make调用时也ok。
ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif
就是说有两个主方向,当我们不是编译扩展的模块的时候,all就是主目标。否则就是modules。
到了这里。
ifeq ($(dot-config),1)
通过了,所以
-include .config.cmd
include .config
我们的.config被包含进来了。
all: vmlinux
到这里,我们的终极目标更明确了,但是有这么一句话it is usually overriden in the arch makefile
后面会分析到。其实这个可以去掉的。
然后是
include $(srctree)/arch/$(ARCH)/Makefile
下潜到这里
include $(srctree)/arch/$(ARCH)/Makefile
毫无疑问,$(ARCH)是arm了。于是到了相应平台的makefile
ifeq ($(CONFIG_XIP_KERNEL),y)
all: xipImage
else
all: zImage
endif
而配置文件里有
# CONFIG_XIP_KERNEL is not set
也就是all: zImage这样了。前面不是有一个all了吗?没错,这个覆盖了前面的那个。
查找zImage
bzImage: zImage
zImage Image xipImage bootpImage aesopk: vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
如果make bzImage的话,这里也是总入口。可见zImage仍然需要vmlinux的。
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
在他上面加上如下语句
@echo $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
@echo `pwd`
@sleep 3000
可以发现对应的是:
@make -f scripts/Makefile.build obj=arch/arm/boot MACHINE=arch/arm/mach-s3c2410/ arch/arm/boot/zImage
/home/lzd/Desktop/kernel-2.6.13
先往后看
lzd002:
ok到了这里,就是生成zimage的神秘花园了,^_^
ifneq ($(MACHINE),) 当然通过了,所以下面的文件被包含了进来。
include $(srctree)/$(MACHINE)/Makefile.boot
endif
下潜到arch/arm/mach-s3c2410/Makefile.boot,这个文件不是自动创建的,也就说要钉死的。他只有两句,定义了下面两个变量
zreladdr-y := 0x30008000 顾名思义,zimage的重载物理地址
params_phys-y := 0x30000100 参数的物理地址
显然上面两个参数都不是给内核用的,一个是给解压缩程序用的,另一个给uboot用。
继续
# Note: the following conditions must always be true:
# ZRELADDR == virt_to_phys(TEXTADDR) 这个是线性地址对应的物理地址
# PARAMS_PHYS must be within 4MB of ZRELADDR 这个要看度春雷的书了
# INITRD_PHYS must be in RAM
ZRELADDR := $(zreladdr-y)
PARAMS_PHYS := $(params_phys-y)
INITRD_PHYS := $(initrd_phys-y) 没有用到initrd文件?
好了,上面这些变量被初始话了。少不了export ZRELADDR INITRD_PHYS PARAMS_PHYS
ifeq ($(CONFIG_XIP_KERNEL),y) 显然要到else了
else
$(obj)/xipImage: FORCE
@echo 'Kernel not configured for XIP (CONFIG_XIP_KERNEL!=y)'
@false
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
@echo ' Kernel: $@ is ready'
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
@echo ' Kernel: $@ is ready'
endif
好了,这些就是用来生成 image 和 zimage 的地方了。可见此时的scripts/Makefile.build 文件的功能很简单
只是简单的包含/arch/arm/boot/makefile就ok了,而这里也是make的出口,当make执行到这里的时候,总目标已经将近完成了
先生成image,用了cmd_objcopy,但是我找了好久都没有找到
但是通过下面可以得知许多的命令,来自生成的文件。
cmd_files := $(wildcard .*.cmd $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd))
ifneq ($(cmd_files),)
$(cmd_files): ; # Do not try to update included dependency files
include $(cmd_files)
endif
如果这么作的话
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
echo $(cmd_objcopy)
sleep 40000
可以得到
/usr/local/arm/3.4.1/bin/arm-linux-objcopy -O binary -R .note -R .comment -S vmlinux arch/arm/boot/Image
而这个内容在/arch/arm/boot/.image.cmd里也是这个样子。
不管了,就是这么生成的拉,呵呵,希望明白人告诉我为什么?^_^
而$(obj)/compressed/vmlinux文件的生成,又要用到make.build了,如下
@make -f scripts/Makefile.build obj=arch/arm/boot/compressed arch/arm/boot/compressed/vmlinux
摘取出/arch/arm/boot/compress/makefile的有用部分。
ifeq ($(CONFIG_ZBOOT_ROM),y)
ZTEXTADDR := $(CONFIG_ZBOOT_ROM_TEXT)
ZBSSADDR := $(CONFIG_ZBOOT_ROM_BSS)
else 这里有效
ZTEXTADDR := 0
ZBSSADDR := ALIGN(4)
endif
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o \
$(addprefix $(obj)/, $(OBJS)) FORCE
$(call if_changed,ld)
@:
$(obj)/piggy.gz: $(obj)/../Image FORCE
$(call if_changed,gzip)这里把未压缩的image内核压缩,image是ld链接的,他是elf格式的。
$(obj)/piggy.o: $(obj)/piggy.gz FORCE 当压缩之后,才编译piggy.o,这里你会感到很奇怪。
piggy.S文件只有己行,怎么产生的piggy.o却有1.5m呢?看看piggy.S就知道了
.section .piggydata,#alloc
.globl input_data
input_data:
.incbin "arch/arm/boot/compressed/piggy.gz" 这里,他把piggy.gz就是那个压缩的内核给包含进来了。^_^
.globl input_data_end
input_data_end:
下面的vmlinux.lds跟arm/kernel/vmlinux.lds不同, 注意区别,那个是用来链接内核的,而这里的只是给解压缩代码用的。
$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile .config
@sed "$(SEDFLAGS)" < $< > $@
$(obj)/misc.o: $(obj)/misc.c include/asm/arch/uncompress.h lib/inflate.c
到这里,压缩的带着解压缩头 的vmlinux elf文件已经产生了。
然后
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy) 经过objcopy,把elf转换成bin文件。任务完成了。^_^
@echo ' Kernel: $@ is ready'
到了这里,make的终极任务就完成了:-)
当然,含有个构建modules的任务,这个是make的附加的任务,如下
# Modules
ifdef CONFIG_MODULES
# By default, build modules as well
all: modules 如果你把这句去掉的话,make就不会作modules了
# Build modules
.PHONY: modules
modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux)
@echo ' Building modules, stage 2.';
$(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost
在我的机器上,可以得到上面的执行语句如下:呵呵,还是个绝对路径。
@make -rR -f /home/lzd/Desktop/kernel-2.6.13/scripts/Makefile.modpost
对于make这样的行为,不太理解,不过实践是检验真理的标准,我作了下面的测试makefile,证明了确实是这样
a:=aaa
b:=bbb
.PHONY:all
all:
.PHONY:aaa
.PHONY:bbb
all:aaa
aaa:
@echo $(a)
all:bbb
bbb:
@echo $(b)
结果是aaa,bbb都得到了打印。
下面是对于-rR的解释
-r, --no-builtin-rules Disable the built-in implicit rules.
-R, --no-builtin-variables Disable the built-in variable settings.
查了下书,意思分别是禁止make使用隐含规则 和 作用在变量上的隐含规则
在主makefile 中:
KBUILD_BUILTIN := 1
ifeq ($(MAKECMDGOALS),modules)
KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)
endif
ifeq ($(MAKECMDGOALS),)
KBUILD_MODULES := 1
endif
ifeq ($(KBUILD_EXTMOD),) 这里有个构建模块的地儿
.PHONY: modules
modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux)
@echo ' Building modules, stage 2.';
$(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost
else # KBUILD_EXTMOD 这里也有个构建模块的地儿
modules: $(module-dirs)
@echo ' Building modules, stage 2.';
$(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost
有这么几种构建模块的方式
1
#make (直接敲make)
此时$(MAKECMDGOALS)变量为空(有一个make的环境变量叫“MAKECMDGOALS”,这个变量中会存放你所指定的终极目标的列表,
如果在命令行上,你没有指定目标,那么,这个变量是空值。)。而
KBUILD_BUILTIN := 1 这个变量的默认数值为1
从而
modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux)
这个依赖关系中有vmlinux。也就是说没有vmlinux是不行的。
2 #make modules
此时$(MAKECMDGOALS)变量 为 modules,进而下面的语句判断成功。
ifeq ($(MAKECMDGOALS),modules)
KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1) 这里会把KBUILD_BUILTIN改成空
endif
此时的依赖关系不再需要vmlinux
3
#make -C $(KERNEL_PATH) M=$(MODULE_PATH) modules
当我们在外面的目录构建module时
那么 KBUILD_EXTMOD就会被赋值为M,从而执行else分支的
$(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost
不管是那种情况,继续,从下面的依赖开始分析
modules: $(module-dirs)
module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD))
.PHONY: $(module-dirs) modules
$(module-dirs): crmodverdir $(objtree)/Module.symvers
$(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@)
上面翻译为 make -f scripts/Makefile.build obj=/home/lzd/module_driver/led_drv
显然,/home/lzd/module_driver/led_drv/makefile要被第2次使用,来生成.o文件
/home/lzd/Desktop/kernel-2.6.13
CC [M] /home/lzd/module_driver/led_drv/qq2440_leds.o
就是上面的语句。
# When compiling out-of-tree modules, put MODVERDIR in the module
# tree rather than in the kernel tree. The kernel tree might
# even be read-only.
export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions
.PHONY: crmodverdir
crmodverdir:
$(Q)mkdir -p $(MODVERDIR) 如果是外建modules的化,就在你的外建目录里建立一个.tmp_versions目录
lzd@lzd-laptop:~/module_driver/led_drv$ ls -a
. Makefile~ .qq2440_leds.ko.cmd qq2440_leds.o
.. qq2440_leds.c qq2440_leds.mod.c .qq2440_leds.o.cmd
leds_test qq2440_leds.c~ qq2440_leds.mod.o .tmp_versions 就是这个了
Makefile qq2440_leds.ko .qq2440_leds.mod.o.cmd
继续
.PHONY: $(objtree)/Module.symvers
$(objtree)/Module.symvers:
@test -e $(objtree)/Module.symvers || ( \
echo; \
echo " WARNING: Symbol version dump $(objtree)/Module.symvers"; \
echo " is missing; modules will have no dependencies and modversions."; \
echo )
这里是在检查有没有 kernel-2.6.13/Module.symvers这个文件,他是新建立的,内容是空的,不知道什么时候建立的?
modules: $(module-dirs)
@echo ' Building modules, stage 2.';
$(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost
终于到了这里,这个是真正终极的家伙:-)
打开script/makefile.modpost文件
.PHONY: _modpost
_modpost: __modpost
__modules := $(sort $(shell grep -h '\.ko' /dev/null $(wildcard $(MODVERDIR)/*.mod)))
modules := $(patsubst %.o,%.ko, $(wildcard $(__modules:.ko=.o)))
_modpost: $(modules)
$(modules): %.ko :%.o %.mod.o FORCE
$(call if_changed,ld_ko_o)
quiet_cmd_ld_ko_o = LD [M] $@
cmd_ld_ko_o = $(LD) $(LDFLAGS) $(LDFLAGS_MODULE) -o $@ $(filter-out FORCE,$^)
最终这里调用cmd_ld_ko_o命令生成了你想要的.ko文件。
当然这里分析的是最简单的情况。(完)
lzd002:end
回到主makefile中往下看:
ifeq ($(KBUILD_EXTMOD),)
如果不想编译外部的模块,那就是编译内部的意思,呵呵。这个测试是成功的。
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
$(call if_changed_rule,vmlinux__)
来了,呵呵,这个才是硬道理。
lzd001:
先看后面,在回来看
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) FORCE ,$^)
对最后一行的解释:剔除前三个,只剩下 kallsyms.o 这就是用来链接 vmlinux 的命令
$(LDFLAGS)是空的。
LDFLAGS_vmlinux :=-p --no-undefined -X
vmlinux-lds := arch/$(ARCH)/kernel/vmlinux.lds
-p --no-pipeline-knowledge Stop the linker knowing about the pipeline length
--no-undefined Do not allow unresolved references in object files
-X, --discard-locals Discard temporary local symbols (default)
-T $(vmlinux-lds) 这个是主要的链接脚本,很重要。
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_EXTRA_PASS is not set
ifdef CONFIG_KALLSYMS_EXTRA_PASS
last_kallsyms := 3
else
last_kallsyms := 2
endif
kallsyms.o := .tmp_kallsyms$(last_kallsyms).o
也就说 $(kallsyms.o) = .tmp_kallsyms2.o
而
.tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE
$(call if_changed_dep,as_o_S)
cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $<
a_flags = -Wp,-MD,$(depfile) $(AFLAGS) $(AFLAGS_KERNEL) \
$(NOSTDINC_FLAGS) $(CPPFLAGS) \
$(modkern_aflags) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o)
对下面的两个伪目标没有兴趣,不过还是写出来。
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
.PHONY: scripts
scripts: scripts_basic include/config/MARKER
$(Q)$(MAKE) $(build)=$(@)
下面的参数被传递给 scripts/Makefile.build
@make -f scripts/Makefile.build obj=scripts/basic
@make -f scripts/Makefile.build obj=scripts
不知道他作了什么。
lzd001:end
在前面加上
@echo $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
sleep 3000
得到了一堆依赖文件
1 arch/arm/kernel/vmlinux.lds
2 arch/arm/kernel/head.o
3 arch/arm/kernel/init_task.o
4 init/built-in.o
5 usr/built-in.o
6 arch/arm/kernel/built-in.o
7 arch/arm/mm/built-in.o
8 arch/arm/common/built-in.o
9 arch/arm/mach-s3c2410/built-in.o
10 arch/arm/nwfpe/built-in.o
11 kernel/built-in.o
12 mm/built-in.o
13 fs/built-in.o
14 ipc/built-in.o
15 security/built-in.o
16 crypto/built-in.o
17 lib/lib.a
18 arch/arm/lib/lib.a
19 lib/built-in.o
20 arch/arm/lib/built-in.o
21 drivers/built-in.o
22 sound/built-in.o
23 net/built-in.o
24 .tmp_kallsyms2.o
$(call if_changed_rule,vmlinux__)???
逐个去找:-)
1.arch/arm/kernel/vmlinux.lds
好了,我们的第一个具体任务出来了arch/arm/kernel/vmlinux.lds
到目录里没有找到,但是有个vmlinux.lds.S,可见,他是自动生成的,而且很可能就是通过vmlinux.lds.S生成的。
搜索vmlinux-lds有
vmlinux-lds := arch/$(ARCH)/kernel/vmlinux.lds
sort排序时会去掉相同的单词。
$(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ;
就是下面的那行。
.PHONY: $(vmlinux-dirs)
$(vmlinux-dirs): prepare-all scripts
$(Q)$(MAKE) $(build)=$@
prepare-all scripts更新后,才会调用这个命令,第一次时是
在找
prepare-all: prepare0 prepare
prepare0: prepare1 include/linux/version.h include/asm include/config/MARKER
ifneq ($(KBUILD_MODULES),)
$(Q)rm -rf $(MODVERDIR)
$(Q)mkdir -p $(MODVERDIR)
echo $(Q)rm -rf $(MODVERDIR) $(Q)mkdir -p $(MODVERDIR)
sleep 8000
endif
结果是@rm -rf .tmp_versions @mkdir -p .tmp_versions,就是重建.tmp_versions这个临时目录。
而
include/asm:
@echo ' SYMLINK $@ -> include/asm-$(ARCH)'
$(Q)if [ ! -d include ]; then mkdir -p include; fi;
@ln -fsn asm-$(ARCH) $@
sleep 3000 就是创建那个关键的符号连接asm,但是要想停在这里,得先把asm给删除掉。
# Split autoconf.h into include/linux/config/*
include/config/MARKER: include/linux/autoconf.h
@echo ' SPLIT include/linux/autoconf.h -> include/config/*'
@scripts/basic/split-include include/linux/autoconf.h include/config 这个程序作了什么不清楚
@touch $@ 显然是更新include/config/MARKER的访问时间,这个有什么意义呢????
这样prepare0就完成了。
prepare1: prepare2 outputmakefile
prepare2:
echo $(KBUILD_SRC) 可见这里是空。也就说prepare2,在这里没作什么。:-)
sleep 4000
ifneq ($(KBUILD_SRC),)
@echo ' Using $(srctree) as source for kernel'
$(Q)if [ -h $(srctree)/include/asm -o -f $(srctree)/.config ]; then \
echo " $(srctree) is not clean, please run 'make mrproper'";\
echo " in the '$(srctree)' directory.";\
/bin/false; \
fi;
$(Q)if [ ! -d include2 ]; then mkdir -p include2; fi;
$(Q)ln -fsn $(srctree)/include/asm-$(ARCH) include2/asm
endif
.PHONY: outputmakefile
# outputmakefile generate a Makefile to be placed in output directory, if
# using a seperate output directory. This allows convinient use
# of make in output directory
outputmakefile:
$(Q)if test ! $(srctree) -ef $(objtree); then \
$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) \
> $(objtree)/Makefile; \
echo ' GEN $(objtree)/Makefile'; \
sleep 8000 ; \ 加上后,没能刹住车,可见,他也没作什么,好喜欢呵呵。
fi
.PHONY: prepare-all prepare prepare0 prepare1 prepare2
找不到prepare这个伪目标,呵呵,原来他在/arch/arm/makefile 里,如下:
prepare: maketools include/asm-arm/.arch
.PHONY: maketools FORCE
maketools: include/asm-arm/constants.h include/linux/version.h FORCE
$(Q)$(MAKE) $(build)=arch/arm/tools include/asm-arm/mach-types.h
上面的翻译是make -f scripts/Makefile.build obj=arch/arm/tools include/asm-arm/mach-types.h
include/asm-$(ARCH)/constants.h: arch/$(ARCH)/kernel/asm-offsets.s
$(call filechk,gen-asm-offsets) 调用filechk,参数为gen-asm-offsets,filechk是个命令包,如下:
define filechk
@set -e; \
echo ' CHK $@'; \
mkdir -p $(dir $@); \
$(filechk_$(1)) < $< > $@.tmp; \
if [ -r $@ ] && cmp -s $@ $@.tmp; then \
rm -f $@.tmp; \
else \
echo ' UPD $@'; \
mv -f $@.tmp $@; \
fi
endef
他会调用下面的命令包
define filechk_gen-asm-offsets
(set -e; \
echo "#ifndef __ASM_OFFSETS_H__"; \
echo "#define __ASM_OFFSETS_H__"; \
echo "/*"; \
echo " * DO NOT MODIFY."; \
echo " *"; \
echo " * This file was generated by arch/$(ARCH)/Makefile"; \
echo " *"; \
echo " */"; \
echo ""; \
sed -ne "/^->/{s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; s:->::; p;}"; \
echo ""; \
echo "#endif" )
endef
结果是通过arch/arm/kernel/asm-offsets.s产生include/asm-arm/constants.h文件。
.PHONY: scripts
scripts: scripts_basic include/config/MARKER
scripts_basic: include/linux/autoconf.h
好可怕的树形结构阿呵呵。
vmlinux-dirs是什么呢?下面是他的值。
init usr arch/arm/kernel arch/arm/mm arch/arm/common arch/arm/mach-s3c2410 arch/arm/nwfpe kernel mm fs ipc security crypto drivers sound net lib arch/arm/lib
在搜索vmlinux-dirs
.PHONY: $(vmlinux-dirs)
$(vmlinux-dirs): prepare-all scripts
$(Q)$(MAKE) $(build)=$@
prepare-all scripts更新后,才会调用这个命令,第一次时是
make -f scripts/Makefile.build obj=init 然后依次
usr arch/arm/kernel arch/arm/mm arch/arm/common arch/arm/mach-s3c2410 arch/arm/nwfpe kernel
mm fs ipc security crypto drivers sound net lib arch/arm/lib
好进去看看(make -f scripts/Makefile.build obj=init)。
到了script/Makefile.build下面
src := $(obj) 即src=init
.PHONY: __build
__build:
这个文件的目标
-include .config包含进来
include $(if $(wildcard $(obj)/Kbuild), $(obj)/Kbuild, $(obj)/Makefile)
如果$(obj)/Kbuild存在,就返回它,不存在就返回$(obj)/Makefile,这里他把init/makefile包含了进来
在潜水到/init/makefile
obj-y := main.o version.o mounts.o initramfs.o
obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o
mounts-y := do_mounts.o
mounts-$(CONFIG_DEVFS_FS) += do_mounts_devfs.o
mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o
mounts-$(CONFIG_BLK_DEV_INITRD) += do_mounts_initrd.o
mounts-$(CONFIG_BLK_DEV_MD) += do_mounts_md.o
...
上面是/init/makefile的主要部分。
又要潜水了 :-(
include scripts/Makefile.lib
这个似乎是个处理各种-y,-m符号的地儿。回去
ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif
上面的作用是,只要发现$(obj-y) $(obj-m) $(obj-n) $(obj-) $(lib-target)这些变量有数值,就要创建一个
相应的$(obj)/built-in.o文件。(strip函数是用来去掉开头和结尾的空格的)一般的目录下都会有的。
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m)) \
$(subdir-ym) $(always)
@:
冒号(:)命令是bash的内建命令,通常把它看作true命令,如下
lzd@lzd-laptop:~/Desktop/kernel-2.6.13$ help :&&type :
:: :
No effect; the command does nothing. A zero exit code is returned.
: is a shell builtin
这样调试
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m)) \
$(subdir-ym) $(always)
if test $(obj) = 'init'; then echo you_want_to_know; \
sleep 4000;fi
@:
我得到了这些信息
__build: init/built-in.o
往后找 builtin-target,如下:
$(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)
先往后看,一会在回来看,就像make那样 ^_^
就是用下面的命令来链接init/built-in.o
cmd_link_o_target = $(if $(strip $(obj-y)),\
$(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^),\ ld_flags是空的。
rm -f $@; $(AR) rcs $@)
这里 $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) 得到了执行,结果是把所有的.o文件链接到了一起,成为built-in.o文件。
这样调试
$(builtin-target): $(obj-y) FORCE
if test $(obj) = 'init'; then echo $(obj-y); \
sleep 4000;fi
$(call if_changed,link_o_target)
得到结果如下:
init/main.o init/version.o init/mounts.o init/initramfs.o init/calibrate.o
多此一举了,在init目录下的makefile里已经有定义了。
终于见到希望了,第一个被CC的应该是init/main.c了 ^_^
下面是规则。
%.o: %.c FORCE
$(call cmd,force_checksrc)
$(call if_changed_rule,cc_o_c)
cmd定义在主makefile中
cmd = @$(if $($(quiet)cmd_$(1)),echo ' $($(quiet)cmd_$(1))' &&) $(cmd_$(1))
就是如果存在这个变量 cmd_$(1),就执行他 cmd_$(1),也就是cmd_force_checksrc这个命令。没什么意思。
在主函数里找到了 if_changed_rule 的定义。
# Usage: $(call if_changed_rule,foo)
# will check if $(cmd_foo) changed, or any of the prequisites changed,
# and if so will execute $(rule_foo)
if_changed_rule = $(if $(strip $? \
$(filter-out $(cmd_$(1)),$(cmd_$(@F)))\
$(filter-out $(cmd_$(@F)),$(cmd_$(1)))),\
$(Q)$(rule_$(1)))
按照他的解释应该是执行 rule_cc_o_c。
这是个命令包,如下:
define rule_cc_o_c
$(if $($(quiet)cmd_checksrc),echo ' $($(quiet)cmd_checksrc)';) \
$(cmd_checksrc) \
$(if $($(quiet)cmd_cc_o_c),echo ' $(subst ','\'',$($(quiet)cmd_cc_o_c))';) \
$(cmd_cc_o_c); \
$(cmd_modversions) 无定义 \
scripts/basic/fixdep $(depfile) $@ '$(subst ','\'',$(cmd_cc_o_c))' > $(@D)/.$(@F).tmp; \
rm -f $(depfile); \
mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd
endef
查找 $(cmd_cc_o_c)
ifndef CONFIG_MODVERSIONS
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
而
# CONFIG_MODVERSIONS is not set
所以就是上面的这个分支。make最终用$(CC) $(c_flags) -c -o $@ $<这条命令来编译的main.c文件。
.PHONY: $(subdir-ym)
$(subdir-ym):
$(Q)$(MAKE) $(build)=$@
subdir-ym 这个变量是在makefile.lib里定义的,他根据obj-y里的带有/后缀的文件生成。过程如下
__subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y))) 寻找*/类型的文件,然后把*/变成*(即把subdir/ 换成 subdir)
subdir-y += $(__subdir-y) 追加进来
__subdir-m := $(patsubst %/,%,$(filter %/, $(obj-m))) 同样道理
subdir-m += $(__subdir-m)
subdir-ym := $(sort $(subdir-y) $(subdir-m)) 排序
subdir-ym := $(addprefix $(obj)/,$(subdir-ym)) 加上前缀,得到绝对路径,(如fs/subdir),
2.
查找 vmlinux-init,有vmlinux-init := $(head-y) $(init-y)
同样的依赖关系(与1)
$(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ;
根据上面的内容,可以推断出,他作了什么。
让我们看看递归所有目录,生成build_in.o是怎么实现的?
加上下面的代码。
.PHONY: $(subdir-ym)
$(subdir-ym):
echo $(Q)$(MAKE) $(build)=$@;
sleep 1
$(Q)$(MAKE) $(build)=$@
可见:
echo @make -f scripts/Makefile.build obj=kernel/power;
@make -f scripts/Makefile.build obj=kernel/power
sleep 1
echo @make -f scripts/Makefile.build obj=fs/autofs;
@make -f scripts/Makefile.build obj=fs/autofs
sleep 1
echo @make -f scripts/Makefile.build obj=fs/autofs4;
@make -f scripts/Makefile.build obj=fs/autofs4
sleep 1
echo @make -f scripts/Makefile.build obj=fs/cramfs;
@make -f scripts/Makefile.build obj=fs/cramfs
sleep 1
echo @make -f scripts/Makefile.build obj=fs/devfs;
@make -f scripts/Makefile.build obj=fs/devfs
sleep 1
echo @make -f scripts/Makefile.build obj=fs/devpts;
@make -f scripts/Makefile.build obj=fs/devpts
可以理解,相应的子目录里的build_in.o是怎么生成的,但是根目录下的build_in.o是怎么生成的呢?
如下调试
$(builtin-target): $(obj-y) FORCE
sleep 1
echo $(obj-y)
$(call if_changed,link_o_target)
从下面的结果分析。
echo fs/vfat/vfat.o
fs/vfat/vfat.o
sleep 1
echo fs/yaffs2/yaffs.o
fs/yaffs2/yaffs.o
sleep 1
echo fs/open.o fs/read_write.o fs/file_table.o fs/buffer.o fs/bio.o fs/super.o fs/block_dev.o fs/char_dev.o fs/stat.o fs/exec.o fs/pipe.o fs/namei.o fs/fcntl.o fs/ioctl.o fs/readdir.o fs/select.o fs/fifo.o fs/locks.o fs/dcache.o fs/inode.o fs/attr.o fs/bad_inode.o fs/file.o fs/filesystems.o fs/namespace.o fs/aio.o fs/seq_file.o fs/xattr.o fs/libfs.o fs/fs-writeback.o fs/mpage.o fs/direct-io.o fs/ioprio.o fs/inotify.o fs/eventpoll.o fs/binfmt_aout.o fs/binfmt_script.o fs/binfmt_elf.o fs/mbcache.o fs/nfs_common/built-in.o fs/dnotify.o fs/proc/built-in.o fs/partitions/built-in.o fs/sysfs/built-in.o fs/devpts/built-in.o fs/ext2/built-in.o fs/cramfs/built-in.o fs/ramfs/built-in.o fs/fat/built-in.o fs/msdos/built-in.o fs/vfat/built-in.o fs/devfs/built-in.o fs/nfs/built-in.o fs/lockd/built-in.o fs/nls/built-in.o fs/autofs/built-in.o fs/autofs4/built-in.o fs/yaffs2/built-in.o
他是把所有本目录下的 (*.o文件) + (子目录下的built-in.o文件)的集合,下面是(子目录下的built-in.o文件)的实现,在makefile.lib中。
obj-y := $(patsubst %/, %/built-in.o, $(obj-y))
即把 obj-y 中所有的目录,都换成/fs/yaffs2/built-in.o这样的形式。可见makefile.lib的重要。
回到lzd001
回到lzd002
总结:
主makefile首先包含了平台下的makefile,从而由/arch/arm/makefile确定了最终的目标,script/makefile.build文件是个执行者,
活都是他干的,他首先包含要处理目标的 makefile,比如 /fs/makefile,然后包含makefile.lib,由makefile.lib对上面的/fs/makefile
进行分析,得到递归的目录,然后不折不扣的递归执行,知道完成目标。
zimage vmlinux(压缩的)也是通过script/makefile.build来完成的,但是makefile.lib的作用不再重要。重要的是传递给
script/makefile.build的obj参数下的makefile。比如arch/arm/boot/makefile arch/arm/boot/compress/makefile
由他们来完成最后目标(zimage vmlinux)的生成。
而modules的生成则是同样的道理,由script/makefile.modpost来完成任务。
Image :1.5M 节省了一般的nor或者nand空间。
bzImgae 3.2M
阅读(3906) | 评论(0) | 转发(0) |