Linux ELF 对象文件格式基础知识

举报
Tiamo_T 发表于 2022/06/28 11:24:19 2022/06/28
【摘要】 本文介绍了不同类型的 ELF 对象文件和 ELF 标头。

ELF 代表可执行和可链接文件格式。

ELF 用作 Linux 上目标文件的标准文件格式。在此之前,a.out 文件格式被用作标准,但最近 ELF 接管了这一标准。

ELF 支持:

  • 不同的处理器
  • 不同的数据编码
  • 不同类别的机器

本文介绍了不同类型的 ELF 对象文件和 ELF 标头。

ELF 对象文件

包含已编译代码的文件称为目标文件。对象文件可以是以下任何一种类型:

1.可重定位文件

这种类型的目标文件包含可以与其他可重定位文件链接在一起以生成可执行二进制文件或共享目标文件的数据和代码。通俗地说,可重定位文件与我们通过以下方式编译代码时生成的 .o 文件相同:

gcc -Wall -c test.c -o test.o

所以上面操作后产生的test.o就是一个可重定位的文件。

2.共享对象文件

动态链接器使用这种类型的目标文件将其与可执行文件和/或其他共享目标文件结合起来,以创建完整的过程映像。通俗地说,共享对象文件与使用 -fPIC 标志以下列方式编译代码时生成的 .so 文件相同:

gcc -c -Wall -Werror -fPIC shared.c
gcc -shared -o libshared.so shared.o

完成上述两个命令后,会生成一个共享对象文件 libshared.o 作为输出。


3. 可执行文件

这种类型的目标文件是在运行时能够执行程序的文件。用外行的话来说,它是这样的命令的输出:

 gcc -Wall test.c -o test

因此,输出“test”将是一个可执行文件,在运行时将执行 test.c 文件中编写的逻辑。

精灵头

上面描述的所有目标文件在 Linux 上都是 ELF 类型的。

通过查看这些文件中的每一个,可以很容易地证明这一点。例如,我查看了系统中的三个文件中的每一个:

可重定位文件:

$ vim func.o

^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^A^@>^@^A^@^@^@^@^@^@^@^@^@^@^@
^@UHå¿^@^@^@^@è^@^@^@^@¸^@^@^@^@è^@^@^@^@ÉÃ^@^@
 Inside func()^@^@GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3^@^T^@^@^@^@
^@^@^@^AzR^@^Ax^P^A^[^L^G^H^A^@^@^\^@^.symtab^@.strtab^@.shstrtab^
@.rela.text^@.data^@.bss^@.rodata^@.comment^@.note.GNU-stack^@.rela.eh_frame
^@^@^@^@^@^@^@^@^@^@^@^@^@

共享对象文件:

$ vim libshared.so

^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^C^@>^@^A^@^@^@ ^@^@^@^@ ^@^A^@^@^@^F^@^
^@^@^@^@^@è^A^@^@^@^@^@^@è^A^@^@^@^@^@^@^A^@^@^^@^ @^@^D^@^@^@^T^@^@^
@^C^@^@^@GNU^@·YG®z^L^ZÊ7uÈí,?^N^@^@^@^@^C^@^@^@^L^@^@^@

可执行文件:

$ vim test

^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^B^@>^@^A^@^@^@P@^@^@^@^@^@@^@^@^@^@^@<
^@^@^@D^@^@^^B^@^@^@^@^@^@^A^@^@^@^@^@^@^@/lib64/ld-linux-x86-64.so.2^@^D^
@^@^@^D^@^@^@^T^@^@^@^C^@^@^@GNU^@òÁ}CKbE;ära`6"^O^N\^C^@^@^@

所以我们看到,由于这些是二进制文件,所以除了每个文件开头的 ELF 字符串外,没有什么可以理解的。这表明这些文件只是 ELF 格式。

每个文件都以一个 ELF 标头开头,该标头几乎说明了文件的完整组织。例如,可重定位和共享目标文件包含部分,但在另一端,可执行文件由段组成。因此,根据目标文件的类型,ELF 标头会提供有关文件的详细信息。

大多数情况下,在可执行文件的情况下,ELF 标头后跟程序标头表。程序头表有助于创建过程映像。由于它有助于创建进程映像(在运行可执行文件后创建),因此,程序头表对于可执行文件是必需的,但对于可重定位和共享目标文件是可选的。

以下是 ELF 标头的组织结构:

#define EI_NIDENT 16
typedef struct {
e_ident[EI_NIDENT];
unsigned char e_type;
Elf32_Half e_machine;
Elf32_Half e_version;
Elf32_Word e_entry;
Elf32_Addr e_phoff;
Elf32_Off e_shoff;
Elf32_Off e_flags;
Elf32_Word e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;

所以我们看到上面显示的组织是一个结构体的形式。在这里详细解释每个成员会使事情变得复杂,所以让我们通过这个结构的每个成员所持有的基本含义和信息来了解该特定领域。

1. e_ident

我们已经知道 ELF 格式支持各种类型的机器、处理器等。因此,为了支持所有这些,ELF 文件中的初始信息包含有关如何独立于运行可执行文件的处理器来解释文件的信息。数组“e_ident”提供了完全相同的信息:

Name     Value      Purpose
EI_MAG     0          File identification
EI_MAG1    1          File identification
EI_MAG2    2          File identification
EI_MAG3    3          File identification
EI_CLASS   4          File class
EI_DATA    5          Data encoding
EI_VERSION 6          File version
EI_PAD     7          Start of padding bytes
EI_NIDENT  16         Size of e_ident[]
  • EI_MAG 上面的前四个字节包含幻数“0x7fELF”。
  • EI_CLASS 一个 ELF 可以有两个类,32 位或 64 位。这使得文件格式可移植。
  • EI_DATA 该成员给出数据编码的信息。简单地说,这个信息告诉我们数据是大端还是小端格式。
  • EI_VERSION 该成员提供有关目标文件版本的信息。
  • EI_PAD 此成员标记 e_indent 信息数组中未使用字节的开始。
  • EI_NIDENT 该成员提供数组 e_indent 的大小。这有助于解析 ELF 文件。

2.e_type

该成员标识目标文件的类型。例如,目标文件可以是以下类型:

Name    Value    Meaning
ET_NONE  0       No file type
ET_REL   1       Relocatable file
ET_EXEC  2       Executable file
ET_DYN   3       Shared object file
ET_CORE  4       Core file

注意:上面的列表并不详尽,但仍然提供了 ELF 可以引用的主要目标文件类型的信息。

3.电子机器

此成员提供有关 ELF 文件所需的体系结构的信息。

Name            Value      Meaning
ET_NONE           0          No machine
EM_M32            1          AT&T WE 32100
EM_SPARC          2          SPARC
EM_386            3          Intel Architecture
EM_68K            4          Motorola 68000
EM_88K            5          Motorola 88000
EM_860            7          Intel 80860
EM_MIPS           8          MIPS RS3000 Big-Endian
EM_MIPS_RS4_BE   10          MIPS RS4000 Big-Endian
RESERVED       11-16         Reserved for future use

4. 其他成员

除上述三名成员外,还有以下成员:

  • e_version:此成员提供 ELF 对象文件的版本信息。
  • e_entry:该成员提供系统必须将控制权转移到的入口点的虚拟地址信息,以便启动进程。
  • e_phoff:该成员保存程序头表的偏移量。此信息以字节为单位存储。在没有程序头表的情况下,该成员包含的信息为零。
  • e_shoff:该成员保存节头表的偏移量。与 e_phoff 一样,此信息也以字节的形式存储,并且在没有节头表的情况下,此字段包含的信息为零。
  • e_flags:该成员保存与进程特定标志相关的信息。
  • e_ehsize:此成员保存与 ELF 标头大小相关的信息,以字节为单位。
  • e_phentsize:该成员保存与目标文件的程序头表中一个条目的大小有关的信息。请注意,所有条目的大小都相同。
  • e_phnum:该成员保存与程序头表中的条目数有关的信息。
  • e_shentsize:该成员保存与节头表中一个条目的大小有关的信息。大小以字节数的形式表示。
  • e_shnum:该成员给出与节头表中的条目数相关的信息。

请注意,ephnum 和 ephentsize 的乘积给出了程序头表的总大小(以字节为单位),同样,eshnum 和 eshentsize 的乘积给出了节头表的总大小(以字节为单位)。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。