File layout
ELF文件有两种视图。程序头表(Program Header)显示在运行时使用的段(Segments),而节头表(Section Header)则列出了二进制文件的所有节(Sections)的集合。程序头表主要用于运行时加载和链接,而节头表则用于调试和符号表等其他目的。程序头表和节头表都是ELF文件中的重要部分,用于描述和组织文件的结构和内容。
ELF头文件定义了使用32位还是64位地址。该头文件包含三个受此设置影响并偏移其后的其他字段的字段。对于32位和64位二进制文件,ELF头文件分别为52或64字节长。
开头四个字节就是0x7F 接上45 4c 46(elf的ascll)
然后一个字节是看看文件种类是64还是32
该字节被设置为1或2,分别表示小端或大端字节序。这会影响从偏移0x10开始的多字节字段的解释方式。
设置为1表示ELF的原始版本和当前版本。
设置目标平台,通常为0
该字段进一步指定了ABI版本。它的解释取决于目标ABI。Linux内核(至少2.6之后)没有对其进行定义,因此对于静态链接的可执行文件,它会被忽略。在这种情况下,EI_PAD的偏移和大小为8。 如果e_ident[EI_OSABI] == 3,则glibc 2.12+将该字段视为动态链接器的ABI版本:它定义了动态链接器功能的列表,将e_ident[EI_ABIVERSION]视为共享对象(可执行文件或动态库)所请求的功能级别,并且如果请求了未知功能,则拒绝加载它,即e_ident[EI_ABIVERSION]大于已知功能的最大值。
当前未使用,应填充为零。
识别对象文件类型。
指令架构
是进程开始执行的入口点的内存地址。该字段的长度取决于先前定义的格式,可以是32位或64位。
指向程序头表的起始位置。它通常紧随文件头之后,对于32位和64位的ELF可执行文件,偏移量分别为0x34或0x40。
指向节头表的起始位置。
对于目标架构的解释取决于具体情况。
该字段包含此标头的大小,通常为64位格式为64字节,32位格式为52字节。
该字段包含程序头表条目的大小。
该字段包含程序头表中条目的数量。
这是节表的
该字段包含包含节名称的节头表条目的索引。
然后是elf header的结尾
Program header
程序头表告诉系统如何创建进程镜像。它位于文件偏移量e_phoff处,并由e_phnum个条目组成,每个条目的大小为e_phentsize。在32位ELF和64位ELF中,布局略有不同,因为为了对齐原因,p_flags在不同的结构位置上。每个条目的结构如下:
0x00000000 PT_NULL 程序头表条目未使用
0x00000001 PT_LOAD 可加载的段
0x00000002 PT_DYNAMIC 动态链接信息
0x00000003 PT_INTERP 解释器信息
0x00000004 PT_NOTE 辅助信息
0x00000005 PT_SHLIB 保留
0x00000006 PT_PHDR 包含程序头表的段
0x00000007 PT_TLS 线程本地存储模板
0x60000000 PT_LOOS 下面参见
0x6FFFFFFF PT_HIOS
0x70000000 PT_LOPROC
0x7FFFFFFF PT_HIPROC PT_LOOS到PT_HIOS(PT_LOPROC到PT_HIPROC)是保留范围,用于操作系统(处理器)特定的语义。
p_flags 段相关的标志(64位结构的位置)。
p_offset 段在文件镜像中的偏移量。
p_vaddr 段在内存中的虚拟地址。
p_paddr 在物理地址相关的系统中,保留用于段的物理地址。
p_filesz 文件镜像中段的大小(以字节为单位)。可能为0。
p_memsz 内存中段的大小(以字节为单位)。
p_flags 段相关的标志(32位结构的位置)。
p_align 0和1表示无对齐要求。否则应为正的、整数的2的幂,p_vaddr等于p_offset对p_align取模。
程序头的结尾(大小)
程序头部表(phdr)
程序头部表(PHT)主要用于运行时。它描述了程序的各个部分应该如何被加载到内存中。简单地说,PHT告诉操作系统如何创建程序的进程映像。它包含一系列的条目,每个条目都指向ELF文件中的一个段(segment),这些段需要被映射到进程的虚拟地址空间中。比如,它会指明哪一部分是代码,哪一部分是数据,哪一部分需要初始化等等。操作系统根据这些信息,决定如何将文件中的内容加载到内存,以及如何设置内存的访问权限(如只读、可执行等)。
节头部表(shdr)
节头部表(SHT)则主要用于链接时和调试时。它描述了文件中所有的节(section)的属性和位置。每个节包含了程序的不同类型的数据,比如代码、数据、符号表、重定位信息等。SHT中的每个条目都定义了一个节的名称、大小、类型等信息。链接器(Linker)利用这些信息来处理符号解析和重定位,而调试器(Debugger)则可以利用这些信息来提供程序的调试功能。
区别与作用总结
- 目的不同:phdr面向的是程序的执行过程,主要用于运行时;而shdr面向的是程序的结构和链接过程,主要用于链接时和调试时。
- 内容不同:phdr关注于如何将程序加载到内存并执行;shdr包含了更详细的程序结构信息,包括代码、数据、调试信息等。
- 使用者不同:phdr主要由操作系统的加载器(Loader)使用,shdr则主要由链接器和调试器使用。
在一些情况下,一个ELF文件可能只包含程序头部表而没有节头部表,特别是在那些只需加载执行而不需要进行链接或调试的环境中,这样可以减小文件的大小,加快加载速度。例如,很多嵌入式系统和操作系统的内核就是这样处理的。