ELF格式学习资料整理[gliethttp] ELF(Executable and Linkable Format 可执行可连接格式)目标文件格式,由Unix系统实验室设计,现在成为Linux最常用的目标文件格式。虽然和其它的目标文件格式(比如ECOFF和a.out)相比,ELF在性能上有轻微的开销,但它给人的感觉更灵活。ELF可执行文件包括可执行代码(有时叫做text)和数据(data)。执行映像中的表描述了程序应该如何放到进程的虚拟内存中。静态连接的映像是用连接程序(ld)或者连接编辑器创建的,单一的映像中包括了运行该映像需要的所有的代码和数据。这个映像也描述了该映像在内存中的布局和要执行的第一部分代码在映像中的地址。 ELF映像文件的开始是一个文件头数据结构,它集中描述了整个ELF文件的信息。如ELF文件标识、文件类型、适用的机器型号、入口的虚拟地址、程序头的个数、程序头表的开始位置、每个程序头的大小、section头的个数、section头表的开始位置、每个section头的大小等。该结构定义如下: typedef __u32 Elf32_Addr; typedef __u16 Elf32_Half; typedef __u32 Elf32_Off; typedef __s32 Elf32_Sword; typedef __u32 Elf32_Word; #define EI_NIDENT 16 ELF头结构 typedef struct elf32_hdr{ unsigned char e_ident[EI_NIDENT]; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; /* Entry point */ 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; 0x00.e_ident[EI_NIDENT]16字节定义 名称 索引号 意义 固定值 EI_MAG0 0 File identification 0x7F EI_MAG1 1 File identification 'E' EI_MAG2 2 File identification 'L' EI_MAG3 3 File identification 'F' EI_CLASS 4 File class EI_DATA 5 Data encoding EI_VERSION 6 File version EI_PAD 7 Start of padding bytes 未使用字节开始位置 前4个字节叫魔术值,固定为0x7F,'E','L','F' 0x04.e_ident[4] = EI_CLASS = 01表示是32-bit objects,02是64bit,00非法 0x05.e_ident[5] = EI_DATA = 编码方式:02表示big endian,01表示little endian 0x06.e_ident[6] = EI_VERSION = ELF头的版本号,目前只能设置为'1' 0x07.e_ident[ 7-0xF] = 未使用字节空间全0 0x10.e_type = 该成员确定该object的类型.0表示无效类型;1表示可重定为文件;2表示可执行文件;3共享目标文件;4内核文件 0x12.e_machine = 该成员变量指出了运行该程序需要的体系结构; 结构名称 索引值 EM_NONE 0 EM_M32 1 EM_SPARC 2 EM_386 3 EM_68K 4 EM_88K 5 EM_486 6 EM_860 7 EM_MIPS 8 EM_MIPS_RS4_BE 10 EM_PARISC 15 EM_SPARC32PLUS 18 EM_PPC 20 EM_PPC64 21 EM_S390 22 EM_ARM 40 EM_SH 42 EM_SPARCV9 43 EM_IA_64 50 EM_X8664 62 EM_CRIS 76 EM_ALPHA 0x9026 EM_S390_OLD 0xA390 0x14.e_version = 这个成员确定object文件的版本.1表示当前版本 0x18.e_entry = 程序入口对应的虚地址 0x1C.e_phoff = 第一个程序头表结构在文件中的偏移量,该偏移量从文件头开始算起. 0x20.e_shoff = 第一个节头表结构在文件中的偏移 0x24.e_flags = 处理器相关的标志,未定义处理器标记的处理器,此值为0 0x28.e_ehsize = 该成员保存着ELF头大小(以字节计数) 0x2A.e_phentsize = 程序头表结构的大小,所有的入口都是同样的大小 0x2C.e_phnum = 该文件中所包程序头表的个数 0x2E.e_shentsize = 每一个节头表大小,所有的入口都是同样的大小 0x30.e_shnum = 该文件中所包节头表的个数 0x32.e_shstrndx = 与各个节头表名字字符表相关的节头表索引号[存放相应节头表名的节头表索引].
程序头表结构 整个ELF文件包含多个程序头,每个程序头描述文件中一个程序段的信息。如:类型(程序头、共享库、解释器、装入、动态连接等)、标志(读、写、执行等)、在文件中的位置、在虚拟空间中的开始地址、在物理内存中的开始地址、段在文件上的大小、在内存中的大小等。文件中所有的程序头集中起来组成一个表,即程序头表,程序头表的信息在文件头中描述。其定义如下: typedef struct elf32_phdr{ Elf32_Word p_type; Elf32_Off p_offset; /* Segment file offset */ Elf32_Addr p_vaddr; /* Segment virtual address */ Elf32_Addr p_paddr; /* Segment physical address */ Elf32_Word p_filesz; /* Segment size in file */ Elf32_Word p_memsz; /* Segment size in memory */ Elf32_Word p_flags; Elf32_Word p_align; /* Segment alignment, file & memory */ } Elf32_Phdr; 0x00.p_type = 该成员指出了这个数组的元素描述了什么类型的段,或怎样解释该数组元素的信息. PT_NULL 0 PT_LOAD 1 PT_DYNAMIC 2 PT_INTERP 3 PT_NOTE 4 PT_SHLIB 5 PT_PHDR 6 PT_LOPROC 0x70000000 PT_HIPROC 0x7fffffff PT_NULL:该数组元素未使用;其他的成员值是未定义的.这种类型让程序头表忽略入口. PT_LOAD:该数组元素指定一个可载入的段,由p_filesz和p_memsz描述.文件中 字节被映射到内存段中.如果该段的内存大小(p_memsz)比文件大小(p_filesz) 要大,则多出的字节将象段初始化区域那样保持为0.文件的大小不会比内存大小值大. 在程序头表中,可载入段入口是以p_vaddr的升序排列的. PT_DYNAMIC:该数组元素指定动态链接信息. PT_INTERP:该数组元素指定一个null-terminated路径名的位置和大小(作为解释程序). 这种段类型仅仅对可执行文件有意义(尽管它可能发生在一个共享object上); 它在一个文件中只能出现一次.如果它出现,它必须先于任何一个可载入段入口. PT_NOTE:该数组元素指定辅助信息的位置和大小. PT_SHLIB:该段类型保留且具有未指定的语义.具有一个这种类型数组元素的程序并不遵守ABI. PT_PHDR:该数组元素(如果出现),指定了程序头表本身的位置和大小(包括在文件中和在该程序的内存映像中). 更进一步来说,它仅仅在该程序头表是程序内存映像的一部分时才有效.如果它出现, 它必须先于任何可载入段入口. PT_LOPROC through PT_HIPROC:该范围中的值保留用于特定处理器的语义. 注意:除非在别处的特殊要求,所有的程序头的段类型是可选的.也就是说,一个文件的程序头表也许仅仅包含和其内容相关的元素. 0x04.p_offset = 该段的驻留位置相对于文件开始处的偏移. 0x08.p_vaddr = 该段在内存中的首字节地址. 0x0C.p_paddr = 在物理地址定位有关联的系统中,该成员是为该段的物理地址而保留的.由于System V忽略了应用程序 的物理地址定位,该成员对于可执行文件和共享的object而言未指定内容. 0x10.p_filesz = 文件映像中该段的字节数. 0x14.p_memsz = 内存映像中该段的字节数. 0x18.p_flags = 和该段相关的标志. PF_R 0x4//可读 PF_W 0x2//可写 PF_X 0x1//可执行 0x1C.p_align = 就象在后面“载入程序”部分中所说的那样,可载入的进程段必须有合适的p_vaddr、p_offset值,取页面大小的模. 该成员给出了该段在内存和文件中排列值.0和1表示不需要排列.否则,p_align必须为正的2的幂, 并且p_vaddr应当等于p_offset模p_align. 节头表结构 整个ELF文件还包含多个section头,每个section头描述文件中一个程序片段的信息。如:名字、类型、标志(写、分配、执行)、开始虚拟地址、在文件上的位置、大小等。文件中所有的section头集中起来组成一个表,即section头表,section头表的信息在文件头中描述。其定义如下: typedef struct { Elf32_Word sh_name; /* Section name, index in string tbl (yes Elf32) */ Elf32_Word sh_type; /* Type of section (yes Elf32) */ Elf32_Word sh_flags; /* Miscellaneous section attributes */ Elf32_Addr sh_addr; /* Section virtual addr at execution */ Elf32_Off sh_offset; /* Section file offset */ Elf32_Word sh_size; /* Size of section in bytes */ Elf32_Word sh_link; /* Index of another section (yes Elf32) */ Elf32_Word sh_info; /* Additional section information (yes Elf32) */ Elf32_Word sh_addralign;/* Section alignment */ Elf32_Word sh_entsize; /* Entry size if section holds table */ } Elf32_Shdr; 0x00.sh_name = 该成员指定了这个section的名字.它的值是节头表e_shstrndx的索引,以NULL空字符结束. 0x04.sh_type = 节头表的类型 SHT_NULL 0 SHT_PROGBITS 1 SHT_SYMTAB 2 SHT_STRTAB 3 SHT_RELA 4 SHT_HASH 5 SHT_DYNAMIC 6 SHT_NOTE 7 SHT_NOBITS 8 SHT_REL 9 SHT_SHLIB 10 SHT_DYNSYM 11 SHT_LOPROC 0x70000000 SHT_HIPROC 0x7fffffff SHT_LOUSER 0x80000000 SHT_HIUSER 0xffffffff SHT_NULL:该值表明该section头是无效的;它没有相关的section.该section的其他成员的值都是未定义的. SHT_PROGBITS:该section保存被程序定义了的一些信息,它的格式和意义取决于程序本身. SHT_SYMTAB and SHT_DYNSYM:这些sections保存着一个符号表(symbol table).一般情况下,一个 object文件每个类型section仅有一个,但是,在将来,这个约束可能被放宽. 典型的是,SHT_SYMTAB为连接器提供标号,当然它也有可能被动态连接时使用. 作为一个完整的符号表,它可能包含了一些动态连接时不需要的标号. 因此,一个object文件可能也包含了一个SHT_DYNSYM的section,它保存着 一个动态连接时所需最小的标号集合来节省空间. SHT_STRTAB:该section保存着一个字符串表.一个object文件可以包含多个字符串表的section. SHT_RELA:该section保存着具有明确加数的重定位入口.就象object文件32位的Elf32_Rela类型. 一个object文件可能有多个重定位的sections. SHT_HASH:该标号保存着一个标号的哈希(hash)表.所有的参与动态连接的object一定包含了一个 标号哈希表(hash table).当前的,一个object文件可能只有一个哈希表. SHT_DYNAMIC:该section保存着动态连接的信息.当前的,一个object可能只有一个动态 的section,但是,将来这个限制可能被取消. SHT_NOTE:该section保存着其他的一些标志文件的信息. SHT_NOBITS:该类型的section在文件中不占空间,但是类似SHT_PROGBITS.尽管该section 不包含字节,sh_offset成员包含了概念上的文件偏移量. SHT_REL:保存着具有明确加数的重定位的入口.就象object文件32位类型Elf32_Rel类型, 一个object文件可能有多个重定位的sections. SHT_SHLIB:该section类型保留但语意没有指明.包含这个类型的section的程序是不符合ABI的. SHT_LOPROC through SHT_HIPROC:在这范围之间的值为特定处理器语意保留的. SHT_LOUSER:该变量为应用程序保留的索引范围的最小边界. SHT_HIUSER:该变量为应用程序保留的索引范围的最大边界.在SHT_LOUSER和HIUSER的 section类型可能被应用程序使用,这和当前或者将来系统定义的section类型是不矛盾的. 0x08.sh_flags = sections支持位的标记,用来描述多个属性. SHF_WRITE 0x1 SHF_ALLOC 0x2 SHF_EXECINSTR 0x4 SHF_MASKPROC 0xf0000000 SHF_WRITE:该section包含了在进程执行过程中可被写的数据. SHF_ALLOC:该section在进程执行过程中占据着内存.一些控制section不存在一个 object文件的内存映象中;对于这些sections,这个属性应该关掉. SHF_EXECINSTR:该section包含了可执行的机器指令. SHF_MASKPROC:所有的包括在这掩码中的位为特定处理语意保留的. 0x0C.sh_addr = 假如该section将出现在进程的内存映象空间里,该成员给出了一个该section在内存中的位置.否则,为0. 0x10.sh_offset= 该成员变量给出了该section的字节偏移量(从文件开始计数).SHT_NOBITS型的section 在文件中不占空间,它的sh_offset成员定位在文件中的概念上的位置. 0x14.sh_size = 该section的字节大小.除非这个section的类型为SHT_NOBITS,否则该section将在文件中将 占有sh_size个字节.SHT_NOBITS类型的section可能为非0的大小,但是不占文件空间. 0x18.sh_link = 该成员保存了一个section报头表的索引连接,它的解释依靠该section的类型. 0x20.sh_info = 该成员保存着额外的信息,它的解释依靠该section的类型. 0x24.sh_addralign=一些sections有地址对齐的约束.例如,假如一个section保存着双字,系统就必须确定整个 section是否双字对齐.所以sh_addr的值以sh_addralign的值作为模,那么一定为0.当然, 仅仅0和正的2的次方是允许的.值0和1意味着该section没有对齐要求. 0x28.sh_entsize= 一些sections保存着一张固定大小入口的表,就象符号表.对于这样一个section来说,该成员给出了 每个入口的字节大小.如果该section没有保存着一张固定大小入口的表,该成员就为0. 以上是粗略的整理,以后用到再深入理解.elf中文翻译文档
|