3.1.4 映像的格式

不知读者留意到没有,无论是setup.bin、vmlinux.bin,还是vmlinux.bin.gz,命名中都包含"bin"的字样,这是开发者有意为之,还是机缘巧合?显然,这个bin不是开发人员随意杜撰的,而是binary的缩写,表示文件格式是裸二进制(raw binary)的。

读者可能有个困惑,在Linux操作系统中二进制文件的格式不是使用ABI(Application Binary Interface)规定的ELF吗?

没错,在Linux作为操作系统的hosted environment环境下,二进制文件使用ELF格式,操作系统也提供ELF文件的加载器。但是,操作系统本身确是工作在freestanding environment环境下。操作系统显然不能强制要求Bootloader也提供ELF加载器。而且,操作系统映像也没有必要使用ELF格式来组织,将代码和数据顺次存放即可,即所谓的裸二进制格式。所以,内核映像都采用裸二进制格式进行组织。

但是,从Linux 2.6.26版本开始,内核的压缩部分,即有效载荷部分,采用了ELF格式。至于为什么采用ELF格式,Patch的提交者给出了原因:

3.1.4 映像的格式 - 图1

我们知道,在解压内核映像后,将会跳转到解压映像的开头执行。但是,ELF文件的开头并不是代码段的开始,而是ELF文件头,也就是说,并不是CPU可执行的机器指令。显然,当内核映像不是裸二进制格式时,我们需要有一个ELF加载器来将ELF格式的内核映像转化为裸二进制格式。那么谁来充当这个ELF加载器呢?

正所谓“螳螂捕蝉,黄雀在后”。内核的非压缩部分调用函数decompress解压内核后,紧接着就调用了函数parse_elf来处理ELF格式的内核映像,代码如下:

3.1.4 映像的格式 - 图2

在ELF文件中,存放代码和数据的段的类型是PT_LOAD,因此,仅处理这个类型的段即可。在函数parse_elf中,对于类型是PT_LOAD的段,其按照Program Header Table中的信息,将它们移动到链接时指定的物理地址处,即p_paddr。当然,如果内核是可重定位的,还要考虑内核实际加载地址与编译时指定的加载地址的差值。

事实上,如果Bootloader不是所谓的"the Xen domain builder",我们完全没有必要保留内核的压缩部分为ELF格式,并略去启动时进行的"parse_elf"。具体方法如下:

(1)将压缩部分链接为裸二进制格式

将传递给命令objcopy的参数追加"-O binary",如下面使用黑体标识的部分:

3.1.4 映像的格式 - 图3

(2)注释掉parse_elf

既然内核压缩部分已经是裸二进制格式的了,解压后自然不再需要调用函数parse_elf了。

3.1.4 映像的格式 - 图4