ELF 文件格式

ELF (Executable and Linkable Format)是一种为可执行文件,目标文件,共享链接库和内核转储(core dumps)准备的标准文件格式。 Linux和很多类Unix操作系统都使用这个格式。 让我们来看一下64位ELF文件格式的结构以及内核源码中有关于它的一些定义。

一个ELF文件由以下三部分组成:

  • ELF头(ELF header) - 描述文件的主要特性:类型,CPU架构,入口地址,现有部分的大小和偏移等等;

  • 程序头表(Program header table) - 列举了所有有效的段(segments)和他们的属性。 程序头表需要加载器将文件中的节加载到虚拟内存段中;

  • 节头表(Section header table) - 包含对节(sections)的描述。

现在让我们对这些部分有一些更深的了解。

ELF头(ELF header)

ELF头(ELF header)位于文件的开始位置。 它的主要目的是定位文件的其他部分。 文件头主要包含以下字段:

  • ELF文件鉴定 - 一个字节数组用来确认文件是否是一个ELF文件,并且提供普通文件特征的信息;

  • 文件类型 - 确定文件类型。 这个字段描述文件是一个重定位文件,或可执行文件,或...;

  • 目标结构;

  • ELF文件格式的版本;

  • 程序入口地址;

  • 程序头表的文件偏移;

  • 节头表的文件偏移;

  • ELF头(ELF header)的大小;

  • 程序头表的表项大小;

  • 其他字段...

你可以在内核源码种找到表示ELF64 header的结构体 elf64_hdr

这个结构体定义在 elf.h

节(sections)

所有的数据都存储在ELF文件的节(sections)中。 我们通过节头表中的索引(index)来确认节(sections)。 节头表表项包含以下字段:

  • 节的名字;

  • 节的类型;

  • 节的属性;

  • 内存地址;

  • 文件中的偏移;

  • 节的大小;

  • 到其他节的链接;

  • 各种各样的信息;

  • 地址对齐;

  • 这个表项的大小,如果有的话;

而且,在linux内核中结构体 elf64_shdr 如下所示:

elf.h

程序头表(Program header table)

在可执行文件或者共享链接库中所有的节(sections)都被分为多个段(segments)。 程序头是一个结构的数组,每一个结构都表示一个段(segments)。 它的结构就像这样:

在内核源码中。

elf64_phdr 定义在相同的 elf.h 文件中.

EFL文件也包含其他的字段或结构。 你可以在 Documentation 中查看。 现在我们来查看一下 vmlinux 这个ELF文件。

vmlinux

vmlinux 也是一个可重定位的ELF文件。 我们可以使用 readelf 工具来查看它。 首先,让我们看一下它的头部:

我们可以看出 vmlinux 是一个64位可执行文件。 我们可以从 Documentation/x86/x86_64/mm.txt 读到相关信息:

之后我们可以在 vmlinux ELF文件中查看这个地址:

值得注意的是,startup_64 例程的地址不是 ffffffff80000000, 而是 ffffffff81000000。 现在我们来解释一下。

我们可以在 arch/x86/kernel/vmlinux.lds.S 看见如下的定义 :

其中,__START_KERNEL 定义如下:

从这个文档中看出,__START_KERNEL_map 的值是 ffffffff80000000 以及 __PHYSICAL_START 的值是 0x1000000。 这就是 startup_64的地址是 ffffffff81000000的原因了。

最后我们通过以下命令来得到程序头表的内容:

这里我们可以看出五个包含节(sections)列表的段(segments)。 你可以在生成的链接器脚本 - arch/x86/kernel/vmlinux.lds 中找到所有的节(sections)。

就这样吧。 当然,它不是ELF(Executable and Linkable Format)的完整描述,但是如果你想要知道更多,可以参考这个文档 - 这里

最后更新于