PE文件格式和ELF文件格式(下)----ELF文件

故事扮演 提交于 2021-02-18 12:54:21

1   简介

 

    可执行链接格式(Executable and Linking Format)最初是由UNIX 系统实验室(UNIX System Laboratories,USL)开发并发布的,作为应用程序二进制接口(Application Binary Interface,ABI )的一部分。工具接口标准(Tool Interface Standards,TIS)委员会将还在发展的ELF 标准选作为一种可移植的目标文件格式,可以在32 位Intel 体系结构上的很多操作系统中使用[1, 2] 。

 

    ELF 标准的目的是为软件开发人员提供一组二进制接口定义,这些接口可以延伸到多种操作环境,从而减少重新编码、重新编译程序的需要。接口的内容包括目标模块格式、可执行文件格式以及调试记录信息与格式等。

 

    TIS 给出的Portable   Formats   Specification   1.1 版本中主要针对三种不同类型的目标文件作了规定,并规定了程序加载与动态链接相关过程细节,给出了标准ANSI C 和libc例程必须提供的符号[1] 。在该组织随后发布的 Executable and  Linking  Format(ELF) Specification 1.2 版本中则分如下三个部分,主要的不同点是对与操作系统相关的部分进行了重新组织:

  

2   相关标准

 

2.1   System V ABI

 

    System V Application Binary Interface(ABI)为已编译的应用程序定义了系统接口,同时也为安装脚本支持提供了一个最小环境。其目的是为应用程序提供一个标准的二进制接口,使得这些程序能够运行在符合 X/Open Commen   Application  Environment Specification, Issue 4.2 和System V Interf ace Defin ition, Fourth Edition 的操作系统上。二进制要包含与不同处理器体系结构相关的信息,所以 ABI并不是只有一个规范,而是一个规范体系。System V ABI  由两个部分组成:一个通用的部分,描述System V 在所有硬件平台上都一致的接口;一个处理器相关的部分,描述特定于某个处理器体系结构的具体实现。

 

2.2   LSB

 

    由于我们所关心的主要是 Linux 平台上目标文件的格式,所以 Linux 标准 LSB(Linux Standard Base)也是重要的参考资料。LSB 的目标是增强不同的Linux 发布版本之间的兼容性,与ABI 类似,也由两个部分组成: 

  gLSB  (Generic LSB )适用于所有体系结构

  archLSB  (Architecture Specific LSB )特定某种体系结构的LSB

    目前,LSB  由SourceForge 开放源码项目社区提供支持。

 

3    ELF 文件格式

 

3.1  简介

 

    目标文件有三种类型:

 

     可重定位文件(Relocatable File ) 包含适合于与其他目标文件链接来创建可执行文件或者共享目标文件的代码和数据。

     可执行文件(Executable File)  包含适合于执行的一个程序,此文件规定了 exec() 如何创建一个程序的进程映像。

     共享目标文件 (Shared Object File) 包含可在两种上下文中链接的代码和数据。首先链接编辑器可以将它和其它可重定位文件和共享目标文件一起处理,生成另外一个目标文件。其次,动态链接器(Dynamic Linker)可能将它与某个可执行文件以及其它共享目标一起组合,创建进程映像。

    目标文件全部是程序的二进制表示,目的是直接在某种处理器上直接执行。

 

3.1.1  目标文件中的数据表示

 

    目标文件格式支持  8 位字节/32 位体系结构。不过这种格式是可以扩展的,目标文件因此以某些机器独立的格式表达某些控制数据,使得能够以一种公共的方式来识别和解释其内容。目标文件中的其它数据使用目标处理器的编码结构,而不管文件在何种机器上创建。

 

                            表  1 ELF 中常用数据格式

 

                   名称       大小  对齐               目的 

               Elf32_Addr    4    4   无符号程序地址 

               Elf32_Half    2    2   无符号中等整数 

               Elf32_Off     4    4   无符号文件偏移 

               Elf32_SWord   4    4   有符号大整数 

               Elf32_Word    4    4   无符号大整数 

               unsigned char  1   1   无符号小整数 

 

    目标文件中的所有数据结构都遵从“ 自然”大小和对齐规则。如果必要,数据结构可以包含显式的补齐,例如为了确保4 字节对象按4 字节边界对齐。数据对齐同样适用于文件内部。

 

3.2   目标文件格式

 

     目标文件既要参与程序链接又要参与程序执行。出于方便性和效率考虑,目标文件格式提供了两种并行视图,分别反映了这些活动的不同需求。

 

                                图  1  目标文件格式

                          链接视图                 执行视图 

                          ELF 头部               ELF 头部 

                     程序头部表(可选)                程序头部表 

                           节区 1 

                                                 段  1 

                             ... 

                           节区  n 

                                                 段  2 

                             ... 

                             ...                  ... 

                         节区头部表            节区头部表(可选) 

 

    文件开始处是一个 ELF 头部(ELF      Header ),用来描述整个文件的组织。节区部分包含链接视图的大量信息:指令、数据、符号表、重定位信息等等。

 

    程序头部表 (Program Header Table ),如果存在的话,告诉系统如何创建进程映像。用来构造进程映像的目标文件必须具有程序头部表,可重定位文件不需要这个表。 

    节区头部表(Section   Heade   Table )包含了描述文件节区的信息,每个节区在表中都有一项,每一项给出诸如节区名称、节区大小这类信息。用于链接的目标文件必须包含节区头部表,其他目标文件可以有,也可以没有这个表。

 

      注意:尽管图中显示的各个组成部分是有顺序的,实际上除了  ELF  头部表以外,其他节区和段都没有规定的顺序

 

3.3   ELF Header 部分

 

    文件的最开始几个字节给出如何解释文件的提示信息。这些信息独立于处理器,也独立于文件中的其余内容。ELF Header 部分可以用下图中的数据结构表示:

 

                            图  2  ELF Header 数据结构

 

        #define EI_NIDENT 16 

        typedef struct{ 

           unsigned char   e_ident[EI_NIDENT]; 

           Elf32_Half  e_type; 

           Elf32_Half  e_machine; 

           Elf32_Word  e_version; 

           Elf32_Addr  e_entry; 

           Elf32_Off  e_phoff; 

           Elf32_Off  e_shoff; 

           Elf32_Word  e_flags; 

           Elf32_Half  e_ehsize; 

           Elf32_Half  e_phentsize; 

           Elf32_Half  e_phnum; 

           Elf32_Half  e_shentsize; 

           Elf32_Half  e_shnum; 

           Elf32_Half  e_shstrndx; 

        }Elf32_Ehdr; 

 

    其中,e_ident 数组给出了ELF 的一些标识信息,这个数组中不同下标的含义如表  2 所示:

 

                               表  2 e_ident[] 标识索引

 

                  名称          取值  目的 

                  EI_MAG0       0   文件标识 

                  EI_MAG1       1   文件标识 

                  EI_MAG2       2   文件标识 

                  EI_MAG3       3   文件标识 

                  EI_CLASS      4   文件类 

                  EI_DATA       5   数据编码 

                  EI_VERSION    6   文件版本 

                  EI_PAD        7   补齐字节开始处 

                  EI_NIDENT    16  e_ident[]大小 

 

    这些索引访问包含以下数值的字节:

 

                              表  3 e_ident[]的内容说明

 

        索引          说明 

 

 魔数(Magic Number),标志此文件是一个  ELF           目标文件。

 

                    名称         取值  位置 

 

        EI_MAG0  到  EI_MAG0   0x7f   e_ident[EI_MAG0] 

 

        EI_MAG3     EI_MAG1   'E'    e_ident[EI_MAG1] 

 

                    EI_MAG2   'L'    e_ident[EI_MAG2] 

 

                    EI_MAG3   'F'    e_ident[EI_MAG3] 

                     

                    标识文件的类别,或者说,容量。

 

                    名称              取值  位置 

 

                    ELFCLASSNONE    0    非法类别 

 

        EI_CLASS    ELFCLASS32      1    32  位目标 

 

                    ELFCLASS64      2    64  位目标 

 

                    ELFCLASS32  支持虚存范围  4  GB。ELFCLASS64       是为  64 位预

                    留的,不过文件中的其他内容都没有针对  64                位定义。

 

        EI_DATA     字节  e_ident[EI_DATA] 给出处理器特定数据的数据编码方式。

 

                    名称            取值  位置 

 

                    ELFDATANONE   0    非法数据编码 

 

                    ELFDATA2LSB   1    高位在前 

 

                    ELFDATA2MSB   2    低位在前 

                    

       EI_VERSION  ELF 头部的版本号码,不前此值必须是  EV_CURRENT。 

 

       EI_PAD      标记  e_ident 中未使用字节的开始。初始化为  0 。 

 

    某些目标文件控制结构可以增长,因为  ELF 头部能够给出它们的实际大小。如果目标文件格式确实发生改变,可能会发生控制结构比预期的大或者小的情况。在这种情况下,忽略这些信息是允许的。至于对 “缺失”信息的处理方式则要依赖于上下文,如果定义了扩展的话,将会对这些做出规定。

 

     注意:在  32   位  Intel 体系结构上要求:

     1、标志

 

      位置             取值 

 

      e_ident[EI_CLASS] ELFCLASS32 

 

      e_ident[EI_DATA] ELFDATA2LSB

 

     2、处理器标识(e_machine)成员必须是  EM_386。

 

    ELF Header 中各个字段的说明如表  4 :

 

                         表 4 ELF Header 中各个字段的含义

 

    成员          说明 

 

    e_ident     目标文件标识 

 

                目标文件类型:

                 名称            取值     含义 

                ET_NONE        0       未知目标文件格式 

                ET_REL         1      可重定位文件 

                ET_EXEC        2      可执行文件 

    e_type      ET_DYN         3      共享目标文件 

                ET_CORE        4      Core 文件(转储格式) 

                ET_LOPROC      0xff00   特定处理器文件 

                ET_HIPROC      0xffff  特定处理器文件 

                ET_LOPROC  和  ET_HIPROC  之间的取值用来标识与处理器相关的文件

                格式。

 

                给出文件的目标体系结构类型

                 名称        取值  含义 

                EM_NONE   0     未指定 

                EM_M32     1    AT&T WE 32100 

    e_machine 

                EM_SPARC   2    SPARC 

                EM_386     3    Intel 80386 

                EM_68K     4    Motorola 68000 

                EM_88K     5    Motorola 88000 

                EM_860     7    Intel 80860 

                EM_MIPS    8    MIPS RS3000 

                其它值都是保留的。特定处理器的  ELF            名称会使用机器名来进行区分。

 

                目标文件版本

                 名称          取值 含义 

    e_version 

                EV_NONE      0    非法版本 

                EV_CURRENT   1    当前版本 

                 

    e_entry     程序入口的虚拟地址。如果目标文件没有程序入口,可以为  0 。 

 

                程序头部表格(Program   Header  Table)的偏移量(按字节计算)。如果文

    e_phoff 

                件没有程序头部表格,可以为  0 。 

 

                节区头部表格 (Section Header Table )的偏移量 (按字节计算)。如果文件

    e_shoff 

                没有节区头部表格,可以为  0 。 

 

                保存与文件相关的,特定于处理器的标志。标志名称采用 EF_machine_flag

    e_flags 

                的格式。 

 

    e_ehsize    ELF 头部的大小(以字节计算)。 

 

    e_phentsize  程序头部表格的表项大小(按字节计算)。 

 

    e_phnum     程序头部表格的表项数目。可以为  0 。 

 

    e_shentsize  节区头部表格的表项大小(按字节计算)。 

 

    e_shnum     节区头部表格的表项数目。可以为  0 。 

 

                节区头部表格中与节区名称字符串表相关的表项的索引。如果文件没有节

    e_shstrndx 

                区名称字符串表,此参数可以为  SHN_UNDEF 。 

 

3.4  节区(Sections)

 

    节区中包含目标文件中的所有信息,除了:ELF 头部、程序头部表格、节区头部表格。节区满足以下条件:

 

    (1). 目标文件中的每个节区都有对应的节区头部描述它,反过来,有节区头部不意味着有节区。

    (2). 每个节区占用文件中一个连续字节区域(这个区域可能长度为  0 )。

    (3). 文件中的节区不能重叠,不允许一个字节存在于两个节区中的情况发生。

    (4). 目标文件中可能包含非活动空间(INACTIVE   SPACE )。这些区域不属于任何头部和节区,其内容未指定。

 

3.4.1 节区头部表格

 

    ELF  头部中,e_shoff    成员给出从文件头到节区头部表格的偏移字节数;e_shnum给出表格中条目数目;e_shentsize给出每个项目的字节数。从这些信息中可以确切地定位节区的具体位置、长度。

 

    节区头部表格中比较特殊的几个下标如下:

 

                          表  5 节区头部表格中的特殊下标

 

      名称                取值        说明 

 

      SHN_UNDEF         0         标记未定义的、缺失的、不相关的,或者没有含义的

                                  节区引用 

 

      SHN_LORESERVE     OXFF00    保留索引的下界 

 

      SHN_LOPROC        0XFF00 

                                  保留给处理器特殊的语义 

      SHN_HIPROC        0XFF1F 

 

                                  包含对应引用量的绝对取值。这些值不会被重定位所

      SHN_ABS           OXFFF1 

                                  影响 

 

                                  相对于此节区定义的符号是公共符号。如  FORTRAN

      SHN_COMMON        OXFFF2 

                                  中  COMMON   或者未分配的  C     外部变量。 

 

      SHN_HIRESERVE     0XFFFF    保留索引的上界 

 

介于  SHN_LORESERVE   和  SHN_HIRESERVE   之间的表项不会出现在节区头部表中。

 

3.4.2 节区头部

 

    每个节区头部可以用如下数据结构描述:

 

                              图  3 节区头部数据结构

 

      typedef struct{ 

         Elf32_Word sh_name; 

         Elf32_Word sh_type; 

         Elf32_Word sh_flags; 

         Elf32_Addr sh_addr; 

         Elf32_Off  sh_offset; 

         Elf32_Word sh_size; 

         Elf32_Word sh_link; 

         Elf32_Word sh_info; 

         Elf32_Word sh_addralign; 

         Elf32_Word sh_entsize; 

      }Elf32_Shdr; 

 

    对其中各个字段的解释如下:

 

                              表  6 节区头部字段说明

 

        成员         说明 

 

                   给出节区名称。是节区头部字符串表节区(Section  Header  String  sh_name Table Section )的索引。名字是一个  NULL    结尾的字符串。 

        sh_type    为节区的内容和语义进行分类。参见节区类型。 

        sh_flags   节区支持  1   位形式的标志,这些标志描述了多种属性。如果节区将出现在进程的内存映像中,此成员给出节区的第一个字 sh_addr 节应处的位置。否则,此字段为  0 。 

 

                   此成员的取值给出节区的第一个字节与文件头之间的偏移。不过,sh_offset  SHT_NOBITS  类型的节区不占用文件的空间,因此其  sh_offset  成员给出的是其概念性的偏移。 

 

                   此成员给出节区的长度(字节数)。除非节区的类型是 sh_size 

                   SHT_NOBITS ,否则节区占用文件中的  sh_size         字节。类型为SHT_NOBITS  的节区长度可能非零,不过却不占用文件中的空间。

 

        sh_link   此成员给出节区头部表索引链接。其具体的解释依赖于节区类型。 

        sh_info   此成员给出附加信息,其解释依赖于节区类型。 

 

                  某些节区带有地址对齐约束。例如,如果一个节区保存一个doubleword,那么系统必须保证整个节区能够按双字对齐。sh_addr

        sh_addralign   对  sh_addralign 取模,结果必须为  0 。目前仅允许取值为  0         和  2  的幂次数。数值  0     和  1 表示节区没有对齐约束。 

 

                  某些节区中包含固定大小的项目,如符号表。对于这类节区,此成 sh_entsize  员给出每个表项的长度字节数。 如果节区中并不包含固定长度表项的表格,此成员取值为  0 。 

 

    索引为零 (SHN_UNDEF )的节区头部也是存在的,尽管此索引标记的是未定义的节区引用。这个节区的内容固定如下:

 

                          表  7 SHN_UNDEF(0)节区的内容

 

       字段名称          取值               说明

 

       sh_name       0                无名称

 

       sh_type       SHT_NULL         非活动

 

       sh_flags      0                无标志

 

       sh_addr       0                无地址

 

       sh_offset     0                无文件偏移

 

       sh_size       0                无尺寸大小

 

       sh_link       SHN_UNDEF        无链接信息

 

       sh_info       0                无辅助信息

 

       sh_addralign  0                无对齐要求

 

       sh_entsize    0                无表项

 

3.4.2.1  节区类型—sh_type 字段 

 

    节区类型定义如表  8 :

 

                               表  8 节区类型定义

 

      名称           取值                             说明 

 

                           此值标志节区头部是非活动的,没有对应的节区。此节区头部

  SHT_NULL          0 

                           中的其他成员取值无意义。 

  SHT_PROGBITS      1      此节区包含程序定义的信息,其格式和含义都由程序来解释。

 

                           此节区包含一个符号表。目前目标文件对每种类型的节区都只

                           能包含一个,不过这个限制将来可能发生变化。

  SHT_SYMTAB        2 

                           一般,SHT_SYMTAB     节区提供用于链接编辑(指  ld          而言)

                           的符号,尽管也可用来实现动态链接。

 

  SHT_STRTAB        3      此节区包含字符串表。目标文件可能包含多个字符串表节区。

 

                           此节区包含重定位表项,其中可能会有补齐内容 (addend ),例

  SHT_RELA          4      如  32 位目标文件中的  Elf32_Rela   类型。目标文件可能拥有多

                           个重定位节区。 

 

                           此节区包含符号哈希表。所有参与动态链接的目标都必须包含

  SHT_HASH          5      一个符号哈希表。目前,一个目标文件只能包含一个哈希表,

                           不过此限制将来可能会解除。 

 

                           此节区包含动态链接的信息。目前一个目标文件中只能包含一

  SHT_DYNAMIC       6 

                           个动态节区,将来可能会取消这一限制。 

  SHT_NOTE          7      此节区包含以某种方式来标记文件的信息。 

 

                           这种类型 的节区不 占用文件中的空间,其他方面和

  SHT_NOBITS        8      SHT_PROGBITS  相似。尽管此节区不包含任何字节,成员

                           sh_offset 中还是会包含概念性的文件偏移 

 

                           此节区包含重定位表项,其中没有补齐 (addends),例如  32              位

  SHT_REL           9       目标文件中的  Elf32_rel  类型。目标文件中可以拥有多个重定

                           位节区。 

 

                           此节区被保留,不过其语义是未规定的。包含此类型节区的程

  SHT_SHLIB         10 

                           序与  ABI  不兼容。 

 

                           作为一个完整的符号表,它可能包含很多对动态链接而言不必

  SHT_DYNSYM        11     要的符号。因此,目标文件也可以包含一个  SHT_DYNSYM                 节

                           区,其中保存动态链接符号的一个最小集合,以节省空间。 

  SHT_LOPROC    0X70000000 

                           这一段(包括两个边界),是保留给处理器专用语义的。 

  SHT_HIPROC    OX7FFFFFFF 

  SHT_LOUSER    0X80000000  此值给出保留给应用程序的索引下界。 

  SHT_HIUSER    0X8FFFFFFF  此值给出保留给应用程序的索引上界。 

 

    其它的节区类型是保留的。

 

3.4.2.2  sh_flags  字段 

 

    sh_flags 字段定义了一个节区中包含的内容是否可以修改、是否可以执行等信息。如果一个标志位被设置,则该位取值为 1。未定义的各位都设置为0。

 

                         表  9 节区头部的  sh_flags 字段取值

 

                     名称                 取值 

 

                     SHF_WRITE          0x1 

 

                     SHF_ALLOC          0x2 

 

                     SHF_EXECINSTR      0x4 

 

                     SHF_MASKPROC       0xF0000000 

 

    其中已经定义了的各位含义如下:

 

  SHF_WRITE:      节区包含进程执行过程中将可写的数据。

  SHF_ALLOC:      此节区在进程执行过程中占用内存。某些控制节区并不出现于目标

    文件的内存映像中,对于那些节区,此位应设置为 0。

  SHF_EXECINSTR:       节区包含可执行的机器指令。

  SHF_MASKPROC:        所有包含于此掩码中的四位都用于处理器专用的语义。

 

 

3.4.2.3  sh_link 和 sh_info 字段 

 

    根据节区类型的不同,sh_link  和  sh_info         的具体含义也有所不同:

 

                          表  10 sh_link 和 sh_info 字段解释

 

            sh_type               sh_link                  sh_info 

 

                       此节区中条目所用到的字符串表格

        SHT_DYNAMIC                                  0 

                       的节区头部索引 

 

                       此哈希表所适用的符号表的节区头

        SHT_HASH                                     0 

                       部索引 

 

        SHT_REL                                      重定位所适用的节区的

                       相关符号表的节区头部索引 

        SHT_RELA                                     节区头部索引 

 

                                                     最后一个局部符号(绑

        SHT_SYMTAB

                       相关联的字符串表的节区头部索引  定  STB_LOCAL )的符

        SHT_DYNSYM

                                                     号表索引值加一 

       其它              SHN_UNDEF                     0 

 

3.4.3 特殊节区

 

    很多节区中包含了程序和控制信息。下面的表中给出了系统使用的节区,以及它们的类型和属性。

 

                               表  11 常见特殊节区

 

    名称       类型           属性            含义 

 

                                        包含将出现在程序的内存映像中的为初始

                          SHF_ALLOC   +  化数据。根据定义,当程序开始执行,系统

    .bss     SHT_NOBITS 

                          SHF_WRITE     将把这些数据初始化为  0 。此节区不占用文

                                        件空间。 

 

    .comment   SHT_PROGBITS   (无)       包含版本控制信息。 

 

                          SHF_ALLOC   + 

    .data    SHT_PROGBITS 

                          SHF_WRITE     这些节区包含初始化了的数据,将出现在程

                          SHF_ALLOC   +  序的内存映像中。 

    .data1   SHT_PROGBITS 

                          SHF_WRITE 

 

    .debug   SHT_PROGBITS   (无)         此节区包含用于符号调试的信息。 

 

                                        此节区包含动态链接信息。节区的属性将包

    .dynamic  SHT_DYNAMIC               含  SHF_ALLOC  位。是否  SHF_WRITE    位

                                        被设置取决于处理器。 

 

                                        此节区包含用于动态链接的字符串,大多数

    .dynstr  SHT_STRTAB   SHF_ALLOC     情况下这些字符串代表了与符号表项相关

                                         的名称。 

 

    .dynsym  SHT_DYNSYM   SHF_ALLOC     此节区包含了动态链接符号表。 

 

                                        此节区包含了可执行的指令,是进程终止代

                          SHF_ALLOC   + 

    .fini    SHT_PROGBITS               码的一部分。程序正常退出时,系统将安排

                          SHF_EXECINSTR

                                        执行这里的代码。 

 

    .got     SHT_PROGBITS               此节区包含全局偏移表。 

 

    .hash    SHT_HASH     SHF_ALLOC     此节区包含了一个符号哈希表。 

 

                                        此节区包含了可执行指令,是进程初始化代

                          SHF_ALLOC   +  码的一部分。当程序开始执行时,系统要在

    .init    SHT_PROGBITS 

                          SHF_EXECINSTR 开始调用主程序入口之前(通常指  C             语言

                                         的  main 函数)执行这些代码。 

 

                                        此节区包含程序解释器的路径名。如果程序

                                        包含一个可加载的段,段中包含此节区,那

    .interp  SHT_PROGBITS   

                                        么节区的属性将包含 SHF_ALLOC  位,否

                                        则该位为  0 。 

 

                                        此节区包含符号调试的行号信息,其中描述

    .line    SHT_PROGBITS   (无)          了源程序与机器指令之间的对应关系。其内

                                        容是未定义的。 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!