分析Makefile:找到第一个目标文件和链接文件。
Linux内核Makefile文件分类
对于Makefile 的文档在Documentation\kbuild
下的 makefiles.txt 对内核的makefile 讲的很透彻。
每个子目录下都会有个 Makefile 文件
obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o
.ko
模块文件。分析一个 Makefile 时,从它的命令开始分析。编译内核时是直接make
或 make uImage
从顶层Makefile 一直往下走时会涉及到所有的东西。
make uImage
时这个目标 uImage 不在顶层的Makefile 中,在arch/arm/Makefile
中定义了这个目标。我们是在顶层目录make uImage
的,则可知顶层 Makefile 会包含arch/arm/Makefile
。
在顶层目录的 Makefile 中搜索 “include”:
# srctree源码树,ARCH是arm include $(srctree)/arch/$(ARCH)/Makefile
.config
最终会生成include/linux/autoconf.h
头文件给源码用,另一个是include/config/auto.config
文件# Read in config -include include/config/auto.conf
从make uImage
命令往下分析。
1.目标 uImage 定义在arch/arm/Makefile
中,找到uImage 目标所在行,查看它相关的依赖。
# Convert bzImage to zImage bzImage: zImage BOOT_TARGETS = zImage Image xipImage bootpImage uImage $(BOOT_TARGETS): vmlinux $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
可见 uImage 依赖于 vmlinux.uImage
是一个头部+真正的内核。所以制作这个uImage 时需要编译出真正的内核。这
个真正的内核显然就是 vmlinux 。vmlinux 的依赖在顶层目录的 Makefile 中。:
all: vmlinux
在顶层目录直接输入 make ,默认就是执行第一个目标,"all"就是第一个目标。这个目标也
是依赖于 vmlinux 。即都是要先生成 vmlinux .
2.vmlinux的依赖:
顶层Makefile中
# Include targets which we want to # execute if the rest of the kernel build went well. vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) 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 ifdef CONFIG_GDB_SCRIPTS $(Q)ln -fsn `cd $(srctree) && /bin/pwd`/scripts/gdb/vmlinux-gdb.py endif +$(call if_changed,link-vmlinux)
scripts/link-vmlinux.sh
:shell脚本$(vmlinux-deps)
:vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
其中
$(KBUILD_LDS)
:链接脚本$(KBUILD_VMLINUX_INIT)
:一些初始化代码$(KBUILD_VMLINUX_MAIN)
:一些主要代码3.分别分析这些变量:在顶层Makefile中
# Externally visible symbols (used by link-vmlinux.sh) export KBUILD_VMLINUX_INIT := $(head-y) $(init-y) export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y) export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
$(head-y)
:在顶层目录的Makefile 中没有定义,则会在架构目录下(arch/arm/Makefile
)的Makefile 中。MMUEXT等于空,相当于head-y=head.o
#Default value head-y := arch/arm/kernel/head$(MMUEXT).o
init-y
在顶层 Makefile 中。init-y := init/ init-y := $(patsubst %/, %/built-in.o, $(init-y))
%/
代表的是init/
目录下的所有文件。%/built-in.o
相当于在 init/
下的文件全部编译成 built-in.o
。这个函数的意思是:init-y := $(patsubst %/, %/built-in.o, $(init-y)) = $(patsubst %/, %/built-in.o, init/) = init/built-in.o
即 init-y 等于 init 目录下所有涉及的那些文件,这些文件会被编译成一个built-in.o
。,也可以使用obj=$(dir:%.c=%.o)
效果一样的vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
core-y :核心
core-y := usr/ core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ # core-y = usr/built-in.o core-y := $(patsubst %/, %/built-in.o, $(core-y))
意思是最后
core-y = usr/built-in.o += kernel/built-in.o += mm/built-in.o += fs/built-in.o += ipc/built-in.o += security/built-in.o += crypto/built-in.o += block/built-in.o
就是将这些目录(usr、kernel、mm、fs、ipc、security、crypto、block)下涉及的文件分别
编译成built-in.o 不是所有文件,而是涉及到的文件。
libs-y := lib/ $(net-y) $(net-m) $(libs-y) $(libs-m))) libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y)) libs-y := $(libs-y1) $(libs-y2)
最后
libs-y = lib/lib.a += lib/built-in.o
drivers-y := drivers/ sound/ firmware/ $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
意思是最后 drivers-y = drivers/built-in.o
(将 drivers 目录下所有涉及的文件编译成
built-in.o
文件)
+= sound/built-in.o (将 sound 目录下所有涉及的编译成 built-in.o 文件)
+= firmware/built-in.o
net-y := net/ $(net-y) $(net-m) $(libs-y) $(libs-m))) net-y := $(patsubst %/, %/built-in.o, $(net-y))
意思是最后,将 net/目录下的所有涉及到的文件编译 built-in.o 这个文件。
从面的依赖文件展开来看,源材料就是上面这一大堆东西。这些东西如何组合成一个内核
(链接成在一块),要看 vmlinux 如何编译的。
4.vmlinux如何编译
编译时是通过这些命令来编译的。这些命令最终会生成什么东西?可以通过这里一一分析下
去。这里涉及的脚本、函数太庞大了。没精力去做。想知道上在的源材料如何编译成内核:
方法1:分析 Makefile .
方法2:直接编译内核。看编译过程。
a.rm vmlinux 先删除原来编译得到的内核。
b.make uImage V=1
(V=1 是更加详细的列出那些命令。)
我们关心详细命令的最后一条
arm-linux-ld -EL -p --no-undefined -X -o vmlinux (-o 这里生成 vmlinux 了) -T arch/arm/kernel/vmlinux.lds 链接脚本 arch/arm/kernel/head.o arch/arm/kernel/init_task.o init/built-in.o --start-group usr/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o arch/arm/common/built-in.o arch/arm/mach-s3c2410/built-in.o arch/arm/mach-s3c2400/built-in.o arch/arm/mach-s3c2412/built-in.o arch/arm/mach-s3c2440/built-in.o arch/arm/mach-s3c2442/built-in.o arch/arm/mach-s3c2443/built-in.o arch/arm/nwfpe/built-in.o arch/arm/plat-s3c24xx/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o drivers/built-in.o sound/built-in.o net/built-in.o --end-group .tmp_kallsyms2.o
链接脚本:arch/arm/kernel/vmlinux.lds
(决定内核如何排布).
链接脚本vmlinux.lds 是由vmlinux.lds.S 文件生成的。
# arch/arm/kernel/vmlinux.lds.S . = (0xc0000000) + 0x0008000;
这里一开始便指定了内核放在哪里。这显然是虚拟的地址。
.text.head : { _stext = .; _sinittext = .; *(.text.head) }
一开始是放“*”(指所有文件)的 “.text.head”段。
.init : { /*Init code and data*/ *(.init.text)
再接着是放所有文件的“.init.text”段。这些所有文件排放在相应的 “段” 中,排放的顺序就是如下“链接脚本”后面“.o”文件的排布顺序:首先放 head.o 的,等等。文件的顺序由上面这些“.o”文件出现的顺序为准。里面的代码段等等其他段的排放由“vmlinux.lds”决定,首先放“.text.head”段,其次是“.init.text”段等依次往下排(参见“vmlinux.lds”内容)。