Chinaunix首页 | 论坛 | 博客
  • 博客访问: 410109
  • 博文数量: 62
  • 博客积分: 1483
  • 博客等级: 上尉
  • 技术积分: 779
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-24 12:25
文章分类

全部博文(62)

文章存档

2012年(2)

2011年(6)

2010年(6)

2009年(48)

我的朋友

分类: LINUX

2009-09-22 20:04:50

调试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
阅读(3926) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~