Sholck

不积跬步,无以至千里.不积小流,无以成江海

0%

Kbuild-Makefile学习

Kbuild学习

Kbuild的学习主要从三部分学习的

  1. 驱动模块的构建
  2. vmlinux的构建
  3. 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_tracefs5
121:0000000000000581 l F .init.text 00000000000002b4 tracer_init_tracefs
426: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_tracefs5
18787:ffffffff82d5e986 l F .init.text 00000000000002b4 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_Scmd_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
//Makefile
libs-y := lib/
//arch/x86/Makefile
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
//1.先链接生成vmlinux.o,因为是按照ld默认规则生成,故没有部分section的消失,即section的整合
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

//此时vmlinux.o的section还没有整合
➜ 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_tracefs5
32888:000000000002e986 l F .init.text 00000000000002b4 tracer_init_tracefs
33178:0000000000004c28 l O .discard.addressable 0000000000000008 __UNIQUE_ID___addressable_tracer_init_tracefs398

//2,先链接生成.tmp_vmlinux.kallsyms1
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 --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
...

//此时.tmp_vmlinux.kallsyms1的section已经整合完毕
➜ 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 section
18779:ffffffff82b5e986 l F .init.text 00000000000002b4 tracer_init_tracefs


//3.链接生成.tmp_vmlinux.kallsyms2
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 --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

//4. 链接生成 vmlinux
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
//arch/x86/Makefile
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.bincompressed/vmlinux.relocs

1
2
3
4
5
OBJCOPYFLAGS_vmlinux.bin :=  -R .comment -S
$(obj)/vmlinux.bin: vmlinux FORCE
$(call if_changed,objcopy)

//objcopy -R .comment -S vmlinux arch/x86/boot/compressed/vmlinux.bin

如上,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

//arch/x86/tools/relocs vmlinux > arch/x86/boot/compressed/vmlinux.relocs;arch/x86/tools/relocs --abs-relocs vmlinux