Kbuild 编译 Linux 内核系列(十)
Author: Wei Yang
作者公众号:杨小伟的世界
真没有想到,在编译过内核的源码目录下,你可以找到两个同叫 vmlinux 的文件。
$find . -name vmlinux
./vmlinux
./arch/x86/boot/compressed/vmlinux
怎么样,你之前有发现过么?这还是在探索 vmlinux.bin 的过程中发现的秘密。
他们究竟都是干什么用的?有什么联系?和 bzImage 之间有关联么?让我们来揭开这神秘的面纱。
隐藏的vmlinux
老规矩,先来看看 vmlinux.bin 的规则。
$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
嗯,看到刚才 find 中发现的 vmlinux 了不?原来 vmlinux.bin 是这个 vmlinux 通过 objcopy 而来。那这个都包含了谁? 又和根目录下的 vmlinux 有什么关系呢?你们俩真是太像了。
$(obj)/compressed/vmlinux: FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
原来老人家还有一个单独的目录,再次调用了 make 命令。
揭开面纱
检验基本功的时候又来了,还记得这个命令究竟是做了什么么?还记得这个时候,规则文件是要去哪里找么?如果想不起来可以去回顾前面几篇入门文章。
在 arch/x86/boot/compressd/Makefile
中,找到了规则:
$(obj)/vmlinux: $(vmlinux-objs-y) FORCE
$(call if_changed,check_data_rel)
$(call if_changed,ld)
偷个懒,猜一下,这个 vmlinux 就是把变量 vmlinux-objs-y 中的所有目标链接而成的。是不是觉得易如反掌了?
好,那来看看这个变量里都有谁。
vmlinux-objs-y := $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \
$(obj)/string.o $(obj)/cmdline.o $(obj)/error.o \
$(obj)/piggy.o $(obj)/cpuflags.o
好少啊,感觉比 setup.bin 还少。貌似挺简单的啊,这么着就算是把整个 bzImage 的编译流程都走完了~
真的完了么? 你有没有发现什么不对劲?
对了,我们的根目录的 vmlinux 呢?难道放到启动目录下的内核里面没有根目录的 vmlinux ?不对啊,根目录的 vmlinux 可是包含了所有内核真正的代码的啊。
是的,你发现的没错,我们还没有真正走到内核编译的最深处。
石破惊天
也不知道是什么机缘巧合,竟然发现在 arch/x86/boot/compress/
目录下面的 piggy.S这 个文件是编译时生成的。
在我的环境上,这个文件看上去像是这样:
.section ".rodata..compressed","a",@progbits
.globl z_input_len
z_input_len = 6957106
.globl z_output_len
z_output_len = 22677744
.globl input_data, input_data_end
input_data:
.incbin "arch/x86/boot/compressed/vmlinux.bin.gz"
input_data_end:
怎么样,你有没有觉得大跌眼镜?简直就是 FXXK 。
在汇编代码中直接包含了一个文件。让我们看看这个 incbin 语句是什么含义吧。
You can use INCBIN to include executable files, literals, or any arbitrary data. The contents of the file are added to the current ELF section, byte for byte, without being interpreted in any way.
哥们直接把整个内核给包进来了,小生佩服。
再烧一次脑
为了确认,我们再来看看这个 vmlinux.bin.gz 是不是真的是内核的代码。
注意,下面的关系还真是有点烧脑。
先来看 vmlinux.bin.gz :
vmlinux.bin.all-y := $(obj)/vmlinux.bin
$(obj)/vmlinux.bin.gz: $(vmlinux.bin.all-y) FORCE
$(call if_changed,gzip)
那再来看 $(obj)/vmlinux.bin
。注意哦,这个已经是 arch/x86/boot/compressed/vmlinux.bin
,而不是 arxh/x86/boot/vmlinux.bin
了。怎么样,是不是有点烧脑?
$(obj)/vmlinux.bin: vmlinux FORCE
$(call if_changed,objcopy)
而这个时候的依赖, vmlinux ,就是根目录下的 vmlinux了 。
At last。
终于,我们看清楚了内核代码是怎么样打包到 bzImage 中了。
一张图解说
我知道,你肯定已经晕的不能再晕了。我们还是用一张图来解说一下。
vmlinux
||
\/
boot/compressed/vmlinux.bin
||
\/
boot/compressed/vmlinux.bin.gz
||
\/
piggy.o head_$(BITS).o
misc.o
cmdline.o
error.o
cpuflsgs.o
||
\/
boot/compressed/vmlinux
||
\/
boot/vmlinux.bin
是不是稍微的清晰了那么一些些?
真相大白
终于知道了内核还会被压缩,还会被放在一个叫 piggy.o 的文件中,然后再被打包成一个 bzImage 放到启动目录中被加载。
没想到内核的大侠们也挺逗的,起个名字尽然还有点冷幽默。
整个内核的编译过程已经基本走完了,你可能会问知道这个编译的过程除了好玩还能有什么用呢?
我想除了能用来参加面试之外,可能还有一个原因,那就是你有机会明白内核启动时的页表是长什么样的。嗯?你不知道?不着急,你的内核之旅可能才刚刚开始。
本文是 LinuxStory 柴米油盐计划的投稿文章,由 LinuxStory 整理发布。原作者署名为:Wei Yang 。转载请注明出处,否则必究相关责任。
本文链接:https://linuxstory.org/kbuild-linux-10/