读书频道 > 系统 > linux > 深度探索Linux操作系统系统构建和原理解析
3.1.4 映像的格式
2013-09-28 15:40:32     我来说两句 
收藏    我要投稿   
全书一共8章:第1章介绍了如何准备工作环境。在第2章中构建了编译工具链,这是后面构建操作系统各个组件的基础。在这一章中,不仅详细讲解了工具链的构建过程,而且还通过对编译链接过程的探讨,深入讨论了工具链  立即去当当网订购

不知读者留意到没有,无论是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的提交者给出了原因:
This allows other boot loaders such as the Xen domain builder the
opportunity to extract the ELF file.

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

正所谓“螳螂捕蝉,黄雀在后”。内核的非压缩部分调用函数decompress解压内核后,紧接着就调用了函数parse_elf来处理ELF格式的内核映像,代码如下:
linux-3.7.4/arch/x86/boot/compressed/misc.c:
asmlinkage void decompress_kernel(...)
{
    ...
    decompress(input_data, input_len, ...);
    parse_elf(output);
    ...
}

static void parse_elf(void *output)
{
    ...
    for (i = 0; i < ehdr.e_phnum; i++) {
        phdr = &phdrs[i];

        switch (phdr->p_type) {
        case PT_LOAD:
#ifdef CONFIG_RELOCATABLE
            dest = output;
            dest += (phdr->p_paddr - LOAD_PHYSICAL_ADDR);
#else
            dest = (void *)(phdr->p_paddr);
#endif
            memcpy(dest, output + phdr->p_offset, phdr->p_filesz);
            break;
        default: /* Ignore other PT_* */ break;
        }
    }

    free(phdrs);
}

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

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

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

将传递给命令objcopy的参数追加“-O binary”,如下面使用黑体标识的部分:
linux-3.7.4/arch/x86/boot/compressed/Makefile:
OBJCOPYFLAGS_vmlinux.bin :=  -R .comment –S -O binary
$(obj)/vmlinux.bin: vmlinux FORCE
    $(call if_changed,objcopy)

(2)注释掉parse_elf

既然内核压缩部分已经是裸二进制格式的了,解压后自然不再需要调用函数parse_elf了。
linux-3.7.4/arch/x86/boot/compressed/misc.c:

asmlinkage void decompress_kernel(...)
{
    ...
    decompress(input_data, input_len, ...);
    /* parse_elf(output); */
    ...
}

点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:3.1.3 有效载荷——vmlinux
下一篇:3.2.1 kbuild简介
相关文章
图文推荐
3.3.6 GNOME的软件管
3.3.5 GNOME的文件管
3.3.4 GNOME的窗口管
3.3.3 收藏夹和快捷
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站