文章目录
- ELF格式
- ELF文件类型
- ELF文件结构
- ELF header
- Program Header Table
- Section与Segment
ELF格式 ELF文件类型 ??ELF是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件的文件格式。是Linux上二进制文件的标准格式。
??ELF有四种类型:
- 重定位文件( ET_REL ),也就是常称的目标文件
- 可执行文件( ET_EXEC )
- 共享目标文件( ET_DYN ),即共享对象文件、动态库文件
- 核心转储文件( ET_CORE )
ELF文件结构 ??ELF由四部分组成:ELF头( ELF header )、程序头表( Program header table )、节( section )、节头表( section header table ),大致结构如下图所示:
文章图片
需要注意的是,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 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)