ELF格式大致描述


文章目录

  • ELF格式
    • ELF文件类型
    • ELF文件结构
    • ELF header
    • Program Header Table
    • Section与Segment

ELF格式 ELF文件类型 ??ELF是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件的文件格式。是Linux上二进制文件的标准格式。
??ELF有四种类型:
  • 重定位文件( ET_REL ),也就是常称的目标文件
  • 可执行文件( ET_EXEC )
  • 共享目标文件( ET_DYN ),即共享对象文件、动态库文件
  • 核心转储文件( ET_CORE )
除此之外还会有类型不确定的ELF文件( ET_NONE ),即未知文件。
ELF文件结构 ??ELF由四部分组成:ELF头( ELF header )、程序头表( Program header table )、节( section )、节头表( section header table ),大致结构如下图所示:
ELF格式大致描述
文章图片

需要注意的是,ELF文件结构的排布并不一定如上图所示,而且也不一定包含上图的所有内容。但是,ELF头一定会有,且在整个文件的第一个部分。
??这篇笔记下面就是解释这张图中的信息。
ELF header ??ELF头就是一个结构体,被称为 struct ElfN_Ehdr (程序头、节头都是结构体,在图中已经标出了对应结构体的名称)。该结构主要描述了整个文件的组织,前面提到ELF头以外的内容没有固定的位置是因为它们的位置是由ELF头指出的。
??ELF头对应的结构体如下(32位):
#define EI_NIDENT (16) typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf32_Halfe_type; /* Object file type */ Elf32_Halfe_machine; /* Architecture */ Elf32_Worde_version; /* Object file version */ Elf32_Addre_entry; /* Entry point virtual address */ Elf32_Offe_phoff; /* Program header table file offset */ Elf32_Offe_shoff; /* Section header table file offset */ Elf32_Worde_flags; /* Processor-specific flags */ Elf32_Halfe_ehsize; /* ELF header size in bytes */ Elf32_Halfe_phentsize; /* Program header table entry size */ Elf32_Halfe_phnum; /* Program header table entry count */ Elf32_Halfe_shentsize; /* Section header table entry size */ Elf32_Halfe_shnum; /* Section header table entry count */ Elf32_Halfe_shstrndx; /* Section header string table index */ } Elf32_Ehdr;

??命令readelf -h可以查看ELF文件的ELF头信息:
#注释:hello是一个打印helloworld的可执行程序,该程序是64位的 $ readelf -h hello ELF 头: Magic:7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 类别:ELF64 数据:2 补码,小端序 (little endian) 版本:1 (current) OS/ABI:UNIX - System V ABI 版本:0 类型:EXEC (可执行文件) 系统架构:Advanced Micro Devices X86-64 版本:0x1 入口点地址:0x400430 程序头起点:64 (bytes into file) Start of section headers:6616 (bytes into file) 标志:0x0 本头的大小:64 (字节) 程序头大小:56 (字节) Number of program headers:9 节头大小:64 (字节) 节头数量:31 字符串表索引节头: 28 $

从上面我们可以知晓该ELF文件的类型(ELF64指的就是64位的可执行程序)、程序头的位置、节头的位置等等。至于第一行的Magic就是结构体中的那一个字符数组,正好16个字节,其中45 4c 46代表的就是"ELF"这一字符串。
Program Header Table ??程序头( program header )对应的结构体称为 struct ElfN_Phdr 。程序头是对段( segment )的描述,主要描述了如何装载可执行文件(即可执行文件的内存布局以及如何映射到内存中)。
??一个程序的段不止一个,所以程序头当然也不止一个,多个程序头组合在一起就成为了程序头表( program header table )。
??一个程序头的结构体如下:
typedef struct { Elf32_Wordp_type; /* Segment type */ Elf32_Offp_offset; /* Segment file offset */ Elf32_Addrp_vaddr; /* Segment virtual address */ Elf32_Addrp_paddr; /* Segment physical address */ Elf32_Wordp_filesz; /* Segment size in file */ Elf32_Wordp_memsz; /* Segment size in memory */ Elf32_Wordp_flags; /* Segment flags */ Elf32_Wordp_align; /* Segment alignment */ } Elf32_Phdr;

【ELF格式大致描述】??程序头还有有不同的类型,主要类型如下:
  • PT_LOAD
  • PT_DYNAMIC
  • PT_NOTE
  • PT_INTERP
  • PT_PHDR
??命令readelf -l可查看ELF文件的程序头表:
$ readelf -l helloElf 文件类型为 EXEC (可执行文件) 入口点 0x400430 共有 9 个程序头,开始于偏移量 64程序头: TypeOffsetVirtAddrPhysAddr FileSizMemSizFlagsAlign PHDR0x0000000000000040 0x0000000000400040 0x0000000000400040 0x00000000000001f8 0x00000000000001f8R E8 INTERP0x0000000000000238 0x0000000000400238 0x0000000000400238 0x000000000000001c 0x000000000000001cR1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD0x0000000000000000 0x0000000000400000 0x0000000000400000 0x00000000000006fc 0x00000000000006fcR E200000 LOAD0x0000000000000e10 0x0000000000600e10 0x0000000000600e10 0x0000000000000228 0x0000000000000230RW200000 DYNAMIC0x0000000000000e28 0x0000000000600e28 0x0000000000600e28 0x00000000000001d0 0x00000000000001d0RW8 NOTE0x0000000000000254 0x0000000000400254 0x0000000000400254 0x0000000000000044 0x0000000000000044R4 GNU_EH_FRAME0x00000000000005d0 0x00000000004005d0 0x00000000004005d0 0x0000000000000034 0x0000000000000034R4 GNU_STACK0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000RW10 GNU_RELRO0x0000000000000e10 0x0000000000600e10 0x0000000000600e10 0x00000000000001f0 0x00000000000001f0R1#...省略了部分内容...

Section与Segment ??section指的是节,而segment指的是段。一个程序会被划分为多个段,而每个段中有会被划分为节。
??段是由程序头所描述的,多个程序头又组成了程序头表;而节是由节头描述的。多个节头同样组成了一个节头表( Section Header Table )。
??节头对应的结构体称为 struct ElfN_Shdr
??结构体如下所示:
typedef struct { elf32_wordsh_name; /* Section name (string tbl index) */ elf32_wordsh_type; /* Section type */ elf32_wordsh_flags; /* Section flags */ elf32_addrsh_addr; /* Section virtual addr at execution */ elf32_offsh_offset; /* Section file offset */ elf32_wordsh_size; /* Section size in bytes */ elf32_wordsh_link; /* Link to another section */ elf32_wordsh_info; /* Additional section information */ elf32_wordsh_addralign; /* Section alignment */ elf32_wordsh_entsize; /* Entry size if section holds table */ } elf32_shdr;

??命令readelf -S可查看ELF文件的节头:
$ readelf -S hello 共有 31 个节头,从偏移量 0x19d8 开始:节头: [号] 名称类型地址偏移量 大小全体大小旗标链接信息对齐 [ 0]NULL000000000000000000000000 00000000000000000000000000000000000 [ 1] .interpPROGBITS000000000040023800000238 000000000000001c0000000000000000A001 [ 2] .note.ABI-tagNOTE000000000040025400000254 00000000000000200000000000000000A004 [ 3] .note.gnu.build-i NOTE000000000040027400000274 00000000000000240000000000000000A004 [ 4] .gnu.hashGNU_HASH000000000040029800000298 000000000000001c0000000000000000A508 [ 5] .dynsymDYNSYM00000000004002b8000002b8 00000000000000600000000000000018A618 [ 6] .dynstrSTRTAB000000000040031800000318 000000000000003d0000000000000000A001 [ 7] .gnu.versionVERSYM000000000040035600000356 00000000000000080000000000000002A502 [ 8] .gnu.version_rVERNEED000000000040036000000360 00000000000000200000000000000000A618 [ 9] .rela.dynRELA000000000040038000000380 00000000000000180000000000000018A508 [10] .rela.pltRELA000000000040039800000398 00000000000000300000000000000018AI5248 [11] .initPROGBITS00000000004003c8000003c8 000000000000001a0000000000000000AX004 [12] .pltPROGBITS00000000004003f0000003f0 00000000000000300000000000000010AX0016 [13] .plt.gotPROGBITS000000000040042000000420 00000000000000080000000000000000AX008 [14] .textPROGBITS000000000040043000000430 00000000000001820000000000000000AX0016 [15] .finiPROGBITS00000000004005b4000005b4 00000000000000090000000000000000AX004 [16] .rodataPROGBITS00000000004005c0000005c0 000000000000000f0000000000000000A004 [17] .eh_frame_hdrPROGBITS00000000004005d0000005d0 00000000000000340000000000000000A004 [18] .eh_framePROGBITS000000000040060800000608 00000000000000f40000000000000000A008 [19] .init_arrayINIT_ARRAY0000000000600e1000000e10 00000000000000080000000000000000WA008 [20] .fini_arrayFINI_ARRAY0000000000600e1800000e18 00000000000000080000000000000000WA008 [21] .jcrPROGBITS0000000000600e2000000e20 00000000000000080000000000000000WA008 [22] .dynamicDYNAMIC0000000000600e2800000e28 00000000000001d00000000000000010WA608 [23] .gotPROGBITS0000000000600ff800000ff8 00000000000000080000000000000008WA008 [24] .got.pltPROGBITS000000000060100000001000 00000000000000280000000000000008WA008 [25] .dataPROGBITS000000000060102800001028 00000000000000100000000000000000WA008 [26] .bssNOBITS000000000060103800001038 00000000000000080000000000000000WA001 [27] .commentPROGBITS000000000000000000001038 00000000000000350000000000000001MS001 [28] .shstrtabSTRTAB0000000000000000000018cc 000000000000010c0000000000000000001 [29] .symtabSYMTAB000000000000000000001070 0000000000000648000000000000001830478 [30] .strtabSTRTAB0000000000000000000016b8 00000000000002140000000000000000001 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), l (large) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)

    推荐阅读