内核编译
根据顶层Makefile找到vmlinux目标开始分析:
vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE+$(call if_changed,link-vmlinux)vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_OBJS) $(KBUILD_VMLINUX_LIBS)
根据这个展开 vmlinux-deps := arch/$(SRCARCH)/kernel/vmlinux.lds (head-y init-y core-y libs-y2 drivers-y net-y virt-y libs-y1)
# Externally visible symbols (used by link-vmlinux.sh)
export KBUILD_VMLINUX_OBJS := $(head-y) $(init-y) $(core-y) $(libs-y2) \$(drivers-y) $(net-y) $(virt-y)
export KBUILD_VMLINUX_LIBS := $(libs-y1)
export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
上面的xxxx-y就是,每个目录生成的各.o文件集合,会被打包成一个个built-in.a
ifeq ($(KBUILD_EXTMOD),)
# Objects we will link into vmlinux / subdirs we need to visit
init-y := init/
drivers-y := drivers/ sound/
drivers-$(CONFIG_SAMPLES) += samples/
net-y := net/
libs-y := lib/
core-y := usr/
virt-y := virt/
endif # KBUILD_EXTMODcore-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \$(core-y) $(core-m) $(drivers-y) $(drivers-m) \$(net-y) $(net-m) $(libs-y) $(libs-m) $(virt-y)))vmlinux-alldirs := $(sort $(vmlinux-dirs) Documentation \$(patsubst %/,%,$(filter %/, $(init-) $(core-) \$(drivers-) $(net-) $(libs-) $(virt-))))build-dirs := $(vmlinux-dirs)
clean-dirs := $(vmlinux-alldirs)init-y := $(patsubst %/, %/built-in.a, $(init-y))
core-y := $(patsubst %/, %/built-in.a, $(core-y))
drivers-y := $(patsubst %/, %/built-in.a, $(drivers-y))
net-y := $(patsubst %/, %/built-in.a, $(net-y))
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.a, $(filter-out %.a, $(libs-y)))
virt-y := $(patsubst %/, %/built-in.a, $(virt-y))
head-y = arch/arm/kernel/head.o
head-y 定义在文件 arch/arm/Makefile 中:
head-y := arch/arm/kernel/head$(MMUEXT).o当不使能 MMU 的话 MMUEXT=-nommu,如果使能 MMU 的话为空,因此 head-y为:
head-y = arch/arm/kernel/head.o
libs-y = arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o
顶层Makefile中:libs-y := lib/
在arch/arm/Makefile中,对libs-y又追加了: libs-y := arch/arm/lib/ $(libs-y) 展开后: libs-y = arch/arm/lib lib/ 回到顶层Makefile:
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)
core-y = usr/built-in.o kernel/built-in.o ...... arch/arm/crypto/built-in.o
顶层Makefile中:
core-y := usr/
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/在 arch/arm/Makefile 中会对 core-y 进行追加,代码如下
core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/
# Put arch/arm/fastfpe/ to use this.
core-$(CONFIG_FPE_FASTFPE) += $(patsubst $(srctree)/%,%,$(wildcard $(srctree)/arch/arm/fastfpe/))
core-$(CONFIG_VFP) += arch/arm/vfp/
core-$(CONFIG_XEN) += arch/arm/xen/
core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/
core-$(CONFIG_VDSO) += arch/arm/vdso/# If we have a machine-specific directory, then include it in the build.
core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
core-y += arch/arm/probes/
core-y += arch/arm/net/
core-y += arch/arm/crypto/
core-y += $(machdirs) $(platdirs)
加载顺序
section
编译器在生成.o文件时会根据所生成二进制的不同性质把它们放入相应的section中。例如函数编译后的二进制代码通常放到.text,而const关键字修饰的全局数组会放到.rodata中。GCC有除了默认的section,例如.text、.data、.bss、.debug、.dynsym等,也支持用户自定义section,在后面的内容中我们可以看到Linux大量使用GCC的扩展__attribute__ ((section(“section_name”))生成自定义section
驱动一般都会在初始化函数前面加__init;在Linux中,所有标识为__init的函数如果直接编译进入内核,成为内核镜像的一部分,在连接的时候都会放在.init.text这个区段内--#define _ _init _ _attribute_ _ ((_ _section_ _ (".init.text")))
segment
链接器在进行链接时,会根据链接脚本从输入的.o文件中挑选出感兴趣的section,把它们合并生成新的section,这些新产生的section归属于目标文件的某个segment(段),并出现在目标文件中。例如file1.o和file2.o分别有两个.text,它们在链接后生产的目标文件也会有一个.text,而这个.text既是由file1.o和file2.o的.text合并而来的
驱动加载顺序
1.makefile只能控制编译顺序,因为先编译的,所以在链接的时候分配地址的时候,就会靠前
2.链接脚本指定不同段的链接顺序;所以就产生了不同模块的加载顺序
3.initcall分配段的等级,链接到同一个段的代码,因为makefile,就会有个先后顺序了
4.也即:makefile指定单个section(built-in.a)里各符号的执行顺序,initcall指定多个section合成的segment(vmlinux)的执行顺序