Kbuild学习 Kbuild的学习主要从三部分学习的
驱动模块的构建 vmlinux的构建 bzImage的构建 关于Kbuild,可以参考Kbuild官方文档
关于Makefile构建linux的大体框架,见Makefile框架
须知 下面介绍一些须知
编译调试 debug流程是可以通过verbose开启,如下
1 make V=1 bzImage -j12 2 >&1 | tee build.log
更多见make help
ld了解 -m emulation模拟仿真器链接 -V 可以查看所以支持的仿真器 -T 指定链接脚本 –script=scriptfile 使用该脚本代替ld默认链接脚本 -o output 指定链接结果 –whole-archive file 将归档文件中的路径的库的符号都链接起来,后面需要跟–no-whole-archive代表界限 objcopy了解 object file(.o文件) copy和转换工具,使用方式为object input output
-j section 只处理指定的section -R remove 移除指定的section -O指定输出格式 -S strip-all 没有debug section,因此没有symbol信息 驱动模块的构建 驱动模块的构建在likely学习扩展部分:驱动模块的构建
vmlinux的构建 背景 在研究模块初始化过程中,发现__initcall__kmod_trace__397_9768_tracer_init_tracefs5
所在的section在kernel/trace/trace.o
和vmlinux中是不同的 section从.initcall5.init变动到.init.data,因此确定在object file整合到vmlinux时发生了section的整合
1 2 3 4 5 6 7 8 ➜ linux git:(master) ✗ objdump -t kernel/trace/trace.o | grep -n "tracer_init_tracefs" 120 :0000000000000000 l .initcall5.init 0000000000000000 __initcall__kmod_trace__397_9768_tracer_init_tracefs5121 :0000000000000581 l F .init.text 00000000000002b 4 tracer_init_tracefs426 :0000000000000010 l O .discard.addressable 0000000000000008 __UNIQUE_ID___addressable_tracer_init_tracefs398➜ linux git:(master) ✗ objdump -t vmlinux | grep -n "tracer_init_tracefs" 18786 :ffffffff82ebb734 l .init.data 0000000000000000 __initcall__kmod_trace__397_9768_tracer_init_tracefs518787 :ffffffff82d5e986 l F .init.text 00000000000002b 4 tracer_init_tracefs
vmlinux构建过程 Makefile研究编译构建vmlinux的规则,下面是一个总体的框架
graph LR
Makefile --> Kbuild.include -->指明kbuild规则为build=scripts/Makefile.build
Kbuild.include -->定义编译规则函数
定义编译规则函数-->newer-prereqss是否需要更新即依赖比目标新
定义编译规则函数-->cmd_command获取
cmd_command获取-->if_changed_dep
cmd_command获取-->if_changed_rule
Makefile --> arch/x86/Makefile --> head-y -->需要首先链接到vmlinux非built-in.a部分
Makefile --> Kbuild:arch/x86/kernel/Makefile --> extra-y是一些构建vmlinux部分非built-in.a的部分
Makefile -->目标vmlinux-->vmlinux.lds-->descend
目标vmlinux -->KBUILD_VMLINUX_OBJS-->descend
目标vmlinux -->KBUILD_VMLINUX_LIBS-->descend
descend-->Makefile.build-->targets-for-builtin-->通过.lds.S生成.lds -->控制section顺序 vmlinux的编译规则如下:
1 2 vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE +$(call if_changed_dep,link-vmlinux)
重点是vmlinux-deps
1 2 3 4 5 6 7 8 9 10 11 12 13 vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_OBJS) $(KBUILD_VMLINUX_LIBS) //主要分为三大块 export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds KBUILD_VMLINUX_OBJS := $(head-y) $(patsubst %/,%/built-in.a, $(core-y)) KBUILD_VMLINUX_OBJS += $(addsuffix built-in.a, $(filter %/, $(libs-y))) KBUILD_VMLINUX_OBJS += $(patsubst %/, %/lib.a, $(filter %/, $(libs-y))) KBUILD_VMLINUX_OBJS += $(patsubst %/,%/built-in.a, $(drivers-y)) KBUILD_VMLINUX_LIBS := $(filter-out %/, $(libs-y)) //这三大块都依赖 $(sort $(vmlinux-deps) $(subdir-modorder)): descend ; 依赖descend
vmlinux.lds vmlinux.lds依赖descend,descend的行为调用scripts/Makefile.build去编译,默认目标为__build
1 2 3 4 5 __build: $(if $(KBUILD_BUILTIN), $(targets-for -builtin)) \ $(if $(KBUILD_MODULES), $(targets-for -modules)) \ $(subdir-ym) $(always-y) @: endif
依赖为$(targets-for-builtin),其实理解就是内建目标
1 targets-for -builtin := $(extra-y)
通过arch/x86/Kbuild指定编译的arch/x86/kernel/Makefile中有如下
1 2 extra-y += vmlinux.lds CPPFLAGS_vmlinux.lds += -U$(UTS_MACHINE)
因此vmlinux.lds作为依赖先要满足,规则为
1 2 3 4 5 6 7 # Linker scripts preprocessor (.lds.S -> .lds) # --------------------------------------------------------------------------- quiet_cmd_cpp_lds_S = LDS $@ cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -U$(ARCH) \ -D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $< $(obj)/%.lds: $(src)/%.lds.S FORCE $(call if_changed_dep,cpp_lds_S)
关于cpp_lds_S
和cmd_cpp_lds_S
的关系,见cmd_ 其实也可以从代码中看出来
1 2 3 4 cmd = @set -e; $(echo-cmd) $($(quiet)redirect) $(cmd_$(1 )) echo-cmd = $(if $($(quiet)cmd_$(1 )),\ echo ' $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';) cmd是在函数中的,第一个参数为command,如果command不为空,就去执行cmd_command命令
如果存在依赖比目标新的情况(即$?)等,那么默认执行$(cmd_and_fixdep)
命令
1 2 3 4 5 6 7 8 newer-prereqs = $(filter-out $(PHONY),$?) if -changed-cond = $(newer-prereqs)$(cmd-check)$(check-FORCE)if_changed_dep = $(if $(if -changed-cond),$(cmd_and_fixdep),@:) 如果vmlinux.lds不存在,那么就会执行$(cmd_and_fixdep) cmd_and_fixdep = $(cmd); \ scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).cmd;\ rm -f $(depfile)
因此最后的执行命令为cmd_cpp_lds_S
,实际展开为如下
1 2 3 "gcc -E -Wp,-MMD,arch/x86/kernel/.vmlinux.lds.d -nostdinc -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/ uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/compiler-version.h -include ./include/linux/kconfig.h -D__KERNEL__ -Ux86_64 -P -Ux86 -D__ASSEMBLY__ -DLINKER_SCRIPT -o arch/x86/kernel /vmlinux.lds arch/x86/kernel/vmlinux.lds.S"
KBUILD_VMLINUX_OBJS KBUILD_VMLINUX_OBJS为4次增加,下面将分4次结合kbuild/makefiles.html
来分析
1 2 3 4 KBUILD_VMLINUX_OBJS := $(head-y) $(patsubst %/,%/built-in.a, $(core-y) ) KBUILD_VMLINUX_OBJS += $(addsuffix built-in.a, $(filter %/, $(libs-y) )) KBUILD_VMLINUX_OBJS += $(patsubst %/, %/lib.a, $(filter %/, $(libs-y) )) KBUILD_VMLINUX_OBJS += $(patsubst %/,%/built-in.a, $(drivers-y) )
part 1 第一部分$(head-y) $(patsubst %/,%/built-in.a, $(core-y))
打印为如下
1 arch/x86/kernel/head_64.o arch/x86/kernel/head64.o arch/x86/kernel/ebda.o arch/x86/kernel/platform-quirks.o init/built-in.a usr/built-in.a arch/x86/built-in.a kernel/built-in.a certs/built-in.a mm/built-in.a fs/built-in.a ipc/built-in.a security/built-in.a crypto/built-in.a block/built-in.a"
由两部分组成,head-y和built-in.a部分
head-y在arch/x86/Makefile中有定义,是需要首先链接到vmlinux的部分
1 2 3 4 head-y := arch/x86/kernel/head_$(BITS) .o head-y += arch/x86/kernel/head$(BITS) .o head-y += arch/x86/kernel/ebda.o head-y += arch/x86/kernel/platform-quirks.o
built-in.a是啥?从打印信息来看,每个主目录下$(core-y)都有一个built-in.a(每一个$(obj-y)下也有),这是Kbuild编译$(obj-y)部分之后将object文件归档的,之后会通过链接脚本scripts/link-vmlinux.sh
链接到vmlinux
1 2 3 4 ➜ kernel git:(master) ✗ pwd /github/linux/kernel ➜ kernel git:(master) ✗ file built-in.a built-in.a: thin archive with 1718579819 symbol entries
part 2&3 第二部分$(addsuffix built-in.a, $(filter %/, $(libs-y)))
和第三部分KBUILD_VMLINUX_OBJS += $(patsubst %/, %/lib.a, $(filter %/, $(libs-y)))
打印为
1 2 lib/built-in.a arch/x86/lib/built-in.a lib/lib.a arch/x86/lib/lib.a
libs-y的组成如下,官方doc介绍 ,工具将libs-y下的目标打包为库lib.a,并将路径归档为built-in.a
1 2 3 4 libs-y := lib/ libs-y += arch/x86/lib/
part 4 第4部分$(patsubst %/,%/built-in.a, $(drivers-y))
的打印为
1 drivers/built-in.a sound/built-in.a net/built-in.a virt/built-in.a arch/x86/pci/built-in.a arch/x86/power/built-in.a arch/x86/video/built-in.a"
这是一些驱动部分
KBUILD_VMLINUX_LIBS KBUILD_VMLINUX_LIBS := $(filter-out %/, $(libs-y)) 因为libs-y
只有lib/
和arch/x86/lib/
两部分,因此KBUILD_VMLINUX_LIBS
为空
链接依赖总结 在链接脚本链接前,需要以下依赖
graph LR
依赖--> vmlinux-deps
vmlinux-deps-->非built-in-->vmlinux.lds
非built-in-->head-y
vmlinux-deps-->built-in
built-in.a-->core-y
built-in.a-->drivers-y
built-in.a-->libs-y
built-in-->built-in.a
built-in-->lib.a-->libs-y 整体依赖如下:
1 arch/x86/kernel/vmlinux.lds arch/x86/kernel/head_64.o arch/x86/kernel/head64.o arch/x86/kernel/ebda.o arch/x86/kernel/platform-quirks.o init/built-in.a usr/built-in.a arch/x86/built-in.a kernel/built-in.a certs/built-in.a mm/built-in.a fs/built-in.a ipc/built-in.a security/built-in.a crypto/built-in.a block/built-in.a lib/built-in.a arch/x86/lib/built-in.a lib/lib.a arch/x86/lib/lib.a drivers/built-in.a sound/built-in.a net/built-in.a virt/built-in.a arch/x86/pci/built-in.a arch/x86/power/built-in.a arch/x86/video/built-in.a "
脚本链接vmlinux link-vmlinux.sh
描述链接过程如下,其实lib/lib.a已经合并到KBUILD_VMLINUX_OBJS
1 2 3 4 5 6 7 8 9 10 # vmlinux # ^ # | # +--< $(KBUILD_VMLINUX_OBJS) # | +--< init/built-in.a drivers/built-in.a mm/built-in.a + more # | # +--< $(KBUILD_VMLINUX_LIBS) # | +--< lib/lib.a + more # | # +-< ${kallsymso} (see description in KALLSYMS section)
最后链接执行的语句为:
1 2 3 cmd_link-vmlinux = \ $(CONFIG_SHELL) $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)" ; \ $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true )
实际扩展为:
1 sh scripts/link-vmlinux.sh "ld" " -m elf_x86_64" "--emit-relocs --discard-none -z max-page-size=0x200000 --build-id=sha1 --orphan-handling=warn" ; true
脚本链接过程 脚本链接过程如下:
0.1通过modpost_link
使用ld默认链接生成vmlinux.o所以此时section是没有经过处理的 0.2通过objtool_link
将vmlinux.o中的模块symbol导出生成vmlinux.symvers, 见scripts/mod/modpost.c
1.1先vmlinux_link
链接通过ld
链接归档生成.tmp_vmlinux.kallsyms1,此时包含了所有的section 和section,(但是文档上说__kallsyms是空的, 没有发现除了地址变化外的其他不同) 1.2通过kallsyms
生成.tmp_vmlinux.kallsyms1.S,这是一个包含symbol的汇编源代码,可以查看文件scripts/kallsyms.c
1 nm -n .tmp_vmlinux.kallsyms1 | scripts/kallsyms --all-symbols --absolute-percpu --base-relative >.tmp_vmlinux.kallsyms1.S
1.3 gcc通过-I选项指定头文件路径进行编译生成.tmp_vmlinux.kallsyms1.o,此时应该包含正确的kallsyms类symbol
2.1vmlinux_link
链接通过ld
链接.tmp_vmlinux.kallsyms1.o和归档为.tmp_vmlinux.kallsyms2,但由于新增加部分,导致地址发生了偏移 2.2通过kallsyms
生成.tmp_vmlinux.kallsyms1.S,新的包含symbol的汇编源代码 2.3 gcc通过-I选项指定头文件路径进行编译生成.tmp_vmlinux.kallsyms2.o
3.1 vmlinux_link链接通过
ld`链接.tmp_vmlinux.kallsyms2.o和归档为vmlinux
4.mksysmap vmlinux System.map
通过nm
提取符号表为System.map
5.sorttable vmlinux
进行符号表排序,见scripts/sorttable.c
6.符号表检验机制:将.tmp_vmlinux.kallsyms2
生成的符号表.tmp_System.map
和System.map做校验
graph TB
scripts/link-vmlinux.sh --> 1.ld默认链接生成vmlinux.o -->2.提取模块symbol生成vmlinux.symvers
1.ld默认链接生成vmlinux.o -->3.提取.modinfo到modules.builtin.modinfo
scripts/link-vmlinux.sh --> 4.生成.tmp_vmlinux.kallsyms1 -->5.生成.tmp_vmlinux.kallsyms1.S
5.生成.tmp_vmlinux.kallsyms1.S -->6.生成.tmp_vmlinux.kallsyms1.o
6.生成.tmp_vmlinux.kallsyms1.o -->7.生成.tmp_vmlinux.kallsyms2
7.生成.tmp_vmlinux.kallsyms2-->8.生成.tmp_vmlinux.kallsyms2.S
8.生成.tmp_vmlinux.kallsyms2.S-->9.生成.tmp_vmlinux.kallsyms2.o
9.生成.tmp_vmlinux.kallsyms2.o -->10.生成vmlinux -->11.生成System.map
7.生成.tmp_vmlinux.kallsyms2 -->12.生成tmp_System.map 从链接脚本脚本的打印来看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ➜ linux git:(master) ✗ make V=1 bzImage -j12 2 >&1 | tee build.log ld -m elf_x86_64 -r -o vmlinux.o --whole-archive arch/x86/kernel/head_64.o arch/x86/kernel/head64.o arch/x86/kernel/ebda.o arch/x86/kernel/platform-quirks.o init/built-in.a usr/built-in.a arch/x86/built-in.a kernel/built-in.a certs/built-in.a mm/built-in.a fs/built-in.a ipc/built-in.a security/built-in.a crypto/built-in.a block/built-in.a lib/built-in.a arch/x86/lib/built-in.a lib/lib.a arch/x86/lib/lib.a drivers/built-in.a sound/built-in.a net/built-in.a virt/built-in.a arch/x86/pci/built-in.a arch/x86/power/built-in.a arch/x86/video/built-in.a --no-whole-archive --start-group --end-group ➜ linux git:(master) ✗ objdump -t vmlinux.o | grep -n "tracer_init_tracefs" 32887 :0000000000000018 l .initcall5.init 0000000000000000 __initcall__kmod_trace__397_9768_tracer_init_tracefs532888 :000000000002e986 l F .init.text 00000000000002b 4 tracer_init_tracefs33178 :0000000000004 c28 l O .discard.addressable 0000000000000008 __UNIQUE_ID___addressable_tracer_init_tracefs398ld -m elf_x86_64 --emit-relocs --discard-none -z max-page-size=0x200000 --build-id=sha1 --orphan-handling=warn --script=./arch/x86/kernel/vmlinux.lds --strip-debug -o .tmp_vmlinux.kallsyms1 --whole-archive arch/x86/kernel/head_64.o arch/x86/kernel/head64.o arch/x86/kernel/ebda.o arch/x86/kernel/platform-quirks.o init/built-in.a usr/built-in.a arch/x86/built-in.a kernel/built-in.a certs/built-in.a mm/built-in.a fs/built-in.a ipc/built-in.a security/built-in.a crypto/built-in.a block/built-in.a lib/built-in.a arch/x86/lib/built-in.a lib/lib.a arch/x86/lib/lib.a drivers/built-in.a sound/built-in.a net/built-in.a virt/built-in.a arch/x86/pci/built-in.a arch/x86/power/built-in.a arch/x86/video/built-in.a --no-whole-archive --start-group --end-group ... ➜ linux git:(master) ✗ objdump -t .tmp_vmlinux.kallsyms1 | grep -n "tracer_init_tracefs" 18778 :ffffffff82cbb734 l .init.data 0000000000000000 __initcall__kmod_trace__397_9768_tracer_init_tracefs5 >>.init.data section18779 :ffffffff82b5e986 l F .init.text 00000000000002b 4 tracer_init_tracefsld -m elf_x86_64 --emit-relocs --discard-none -z max-page-size=0x200000 --build-id=sha1 --orphan-handling=warn --script=./arch/x86/kernel/vmlinux.lds --strip-debug -o .tmp_vmlinux.kallsyms2 --whole-archive arch/x86/kernel/head_64.o arch/x86/kernel/head64.o arch/x86/kernel/ebda.o arch/x86/kernel/platform-quirks.o init/built-in.a usr/built-in.a arch/x86/built-in.a kernel/built-in.a certs/built-in.a mm/built-in.a fs/built-in.a ipc/built-in.a security/built-in.a crypto/built-in.a block/built-in.a lib/built-in.a arch/x86/lib/built-in.a lib/lib.a arch/x86/lib/lib.a drivers/built-in.a sound/built-in.a net/built-in.a virt/built-in.a arch/x86/pci/built-in.a arch/x86/power/built-in.a arch/x86/video/built-in.a --no-whole-archive --start-group --end-group .tmp_vmlinux.kallsyms1.o ld -m elf_x86_64 --emit-relocs --discard-none -z max-page-size=0x200000 --build-id=sha1 --orphan-handling=warn --script=./arch/x86/kernel/vmlinux.lds -o vmlinux --whole-archive arch/x86/kernel/head_64.o arch/x86/kernel/head64.o arch/x86/kernel/ebda.o arch/x86/kernel/platform-quirks.o init/built-in.a usr/built-in.a arch/x86/built-in.a kernel/built-in.a certs/built-in.a mm/built-in.a fs/built-in.a ipc/built-in.a security/built-in.a crypto/built-in.a block/built-in.a lib/built-in.a arch/x86/lib/built-in.a lib/lib.a arch/x86/lib/lib.a drivers/built-in.a sound/built-in.a net/built-in.a virt/built-in.a arch/x86/pci/built-in.a arch/x86/power/built-in.a arch/x86/video/built-in.a --no-whole-archive --start-group --end-group .tmp_vmlinux.kallsyms2.o
bzImage的构建 到此时,vmlinux已经构建成功,开始bzImage构建 bzImage构建参考官方boot image构建
bzImage的流程构建框架 bzImage的流程构建框架如下:
graph TB
setup.bin-->bzImage
vmlinux.bin -->bzImage
zoffset.h -->bzImage
setup.elf-->setup.bin
setup.ld -->setup.elf
header.o -->setup.elf
header.S -->header.o
version.o -->setup.elf
version.c -->version.o
compressed/vmlinux.lds--> compressed/vmlinux -->vmlinux.bin
compressed/vmlinux -->zoffset.h
compressed/vmlinux-bin.gz-->piggy.S --> piggo.o -->compressed/vmlinux
kasler.o -->compressed/vmlinux
compressed/vmlinux.bin -->compressed/vmlinux-bin.gz
compressed/vmlinux.relocs -->compressed/vmlinux-bin.gz
vmlinux --> compressed/vmlinux.bin bzImage构建时的文件框架 bzImage构建时的Makefile框架是一个动态的包含和调用,如下
graph LR
arch/x86/Makefile -->scripts/Makefile.build
scripts/Makefile.build -->scripts/Kbuild.include
scripts/Makefile.build -->scripts/Makefile.lib
scripts/Makefile.build -->1.arch/x86/boot/Makefile -->scripts/Makefile.build
scripts/Makefile.build -->2.arch/x86/boot/compressed/Makefile bzImage的构建过程 依赖规则为:
1 2 3 4 5 6 7 8 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 $(Q)ln -fsn ../../x86/boot/bzImage $(objtree)/arch/$(UTS_MACHINE)/boot/$@
扩展为:
1 make -f ./scripts/Makefile.build obj=arch/x86/boot arch/x86/boot/bzImage
而scripts/Makefile.build此时又包含了arch/x86/boot/Makefile
1 2 kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) include $(or $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Makefile) >>include 了arch/x86/boot/Makefile
定义了bzImage的依赖和cmd行为
1 2 3 4 5 6 7 8 9 quiet_cmd_image = BUILD $@ silent_redirect_image = >/dev/null cmd_image = $(obj)/tools/build $(obj)/setup.bin $(obj)/vmlinux.bin \ $(obj)/zoffset.h $@ $($(quiet)redirect_image) $(obj)/bzImage: $(obj)/setup.bin $(obj)/vmlinux.bin $(obj)/tools/build FORCE $(call if_changed,image) @$(kecho) 'Kernel: $@ is ready' ' (#'`cat .version`')'
bzImage的主要构成为为setup.bin和vmlinux.bin,构建为arch/x86/boot/tools/build
setup.bin setup.bin用来引导探测内存等的,依赖setup.elf
1 2 3 OBJCOPYFLAGS_setup.bin := -O binary $(obj)/setup.bin: $(obj)/setup.elf FORCE $(call if_changed,objcopy)
setup.elf是由arch/x86/boot下的header.o等通过ld链接而来
1 2 $(obj)/setup.elf: $(src)/setup.ld $(SETUP_OBJS) FORCE $(call if_changed,ld)
setup.ld是作为链接脚本,指定了内核入口点等重要的内核启动信息 而setup.elf有一些附加信息对内核没有用,所以才二进制导入到setup.bin
vmlinux.bin vmlinux.bin的构建在arch/x86/boot/compressed/Makefile
中描述如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # create a compressed vmlinux image from the original vmlinux # # vmlinuz is: # decompression code (*.o) # asm globals (piggy.S), including: # vmlinux.bin.(gz|bz2|lzma|...) # # vmlinux.bin is: # vmlinux stripped of debugging and comments >>vmlinux去掉.comments 和 .debuggingXXX 等section后 # vmlinux.bin.all is: # vmlinux.bin + vmlinux.relocs # vmlinux.bin.(gz|bz2|lzma|...) is: # (see scripts/Makefile.lib size_append) # compressed vmlinux.bin.all + u32 size of vmlinux.bin.all
vmlinux.bin的依赖为一个被压缩过后的vmlinux
1 2 3 OBJCOPYFLAGS_vmlinux.bin := -O binary -R .note -R .comment -S $(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE $(call if_changed,objcopy)
compressed/vmlinux
将除了.note和.comment以外的section部分以二进制方式导入到vmlinux.bin中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ➜ boot git:(master) ✗ file vmlinux.bin vmlinux.bin: data ➜ boot git:(master) ✗ file compressed/vmlinux compressed/vmlinux: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), statically linked, not stripped ➜ boot git:(master) ✗ objdump -h compressed/vmlinux compressed/vmlinux: 文件格式 elf64-x86-64 节: Idx Name Size VMA LMA File off Algn 0 .head.text 0000040d 0000000000000000 0000000000000000 00200000 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .rodata..compressed 009b6c04 000000000000040d 000000000000040d 0020040d 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .text 0000cbdf 00000000009b7020 00000000009b7020 00bb7020 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 3 .rodata 00001d26 00000000009c3c00 00000000009c3c00 00bc3c00 2**5 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .data 000002d4 00000000009c5930 00000000009c5930 00bc5930 2**4 CONTENTS, ALLOC, LOAD, DATA 5 .bss 00018070 00000000009c5c40 00000000009c5c40 00bc5c04 2**5 ALLOC 6 .pgtable 00012000 00000000009de000 00000000009de000 00bc5c04 2**12 ALLOC 7 .comment 00000011 0000000000000000 0000000000000000 00bc5c04 2**0 CONTENTS, READONLY 8 .got.plt 00000018 00000000009f0000 00000000009f0000 00bc5c18 2**3 CONTENTS
compressed/vmlinux
的依赖如下:
1 2 $(obj)/compressed/vmlinux: FORCE $(Q)$(MAKE) $(build)=$(obj)/compressed $@
展开为:
1 make -f ./scripts/Makefile.build obj=arch/x86/boot/compressed arch/x86/boot/compressed/vmlinux
而此时include的东西根据obj的不同也开始不同,现在相当于变成了include arch/x86/boot/compressed/Makefile
1 2 kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) include $(or $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Makefile)
因此,compressed/vmlinux
的依赖变为如下:之后将这些链接起来,链接脚本为compressed/vmlinux.lds
1 2 $(obj)/vmlinux: $(vmlinux-objs-y) $(efi-obj-y) FORCE $(call if_changed,ld)
其中包括 compressed/vmlinux.lds
和其他的一些库,比如piggy.o和进行内核地址随机化的kaslr.o, piggy.o需要通过如下生成
1 2 3 4 5 quiet_cmd_mkpiggy = MKPIGGY $@ cmd_mkpiggy = $(obj)/mkpiggy $< > $@ targets += piggy.S $(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE $(call if_changed,mkpiggy)
通过mkpiggy工具处理compressed/vmlinux.bin.gz
生成pigggy.S,compressed/vmlinux.bin.gz
的依赖如下:
1 2 3 4 vmlinux.bin.all-y := $(obj)/vmlinux.bin vmlinux.bin.all-$(CONFIG_X86_NEED_RELOCS) += $(obj)/vmlinux.relocs $(obj)/vmlinux.bin.gz: $(vmlinux.bin.all-y) FORCE $(call if_changed,gzip)
在.config中CONFIG_X86_NEED_RELOCS为y,因此需要两个依赖compressed/vmlinux.bin
和compressed/vmlinux.relocs
1 2 3 4 5 OBJCOPYFLAGS_vmlinux.bin := -R .comment -S $(obj)/vmlinux.bin: vmlinux FORCE $(call if_changed,objcopy)
如上,compressed/vmlinux.bin
是vmlinux去掉.comment section和 strip-all(没有 debug secion部分)的二进制数 如下,compressed/vmlinux.relocs
是通过relocs处理vmlinux生成的
1 2 3 4 5 6 7 8 9 CMD_RELOCS = arch/x86/tools/relocs quiet_cmd_relocs = RELOCS $@ cmd_relocs = $(CMD_RELOCS) $< > $@;$(CMD_RELOCS) --abs -relocs $< $(obj)/vmlinux.relocs: vmlinux FORCE $(call if_changed,relocs) vmlinux.bin.all-y := $(obj)/vmlinux.bin vmlinux.bin.all-$(CONFIG_X86_NEED_RELOCS) += $(obj)/vmlinux.relocs