ELF头文件学习
ELF文件原名Executable and Linking Format,译为“可执行可连接格式”。
ELF规范中把ELF文件宽泛的称为“目标文件”,这与我们平时的理解不同。一般的,我们把编译但没有链接的文件(比如Linux下的.o文件)称为目标文件。而ELF文件仅指链接好的可执行文件。在ELF规范中,所用符合ELF规范的文件都成为ELF文件,也成为目标文件,这两个名字意义相同。
经过编译但没有连接的文件则称为“可重定位文件 (relocatable file)”或“待重定位文件 (relocatable file)”。本文采用与ELF规范相同的命名方式,所以当提到可重定位文件时,一般可以理解为惯常所说的目标文件;而提到目标文件时,即指各种类型的 ELF文件。
ELF文件定义如下表格所示:
ELF文件 | 可重定位文件(relocatable file) | 可重定位文件(relocatable file),用于与其它目标文件进行连接以构建可执行文件或动态链接库。可重定位文件就是常说的目标文件,由源文件编译而成,但还没有连接成可执行文件。在 UNIX系统下,一般有扩展名”.o”。之所以称其为“可重定位”,是因为在这些文件中,如果引用到其它目标文件或库文件中定义的符号(变量或者函数)的话,只是给出一个名字,这里还并不知道这个符号在哪里,其具体的地址是什么。需要在连接的过程中,把对这些外部符号的引用重新定位到其真正定义的位置上,所以称目标文件为“可重定位”或者“待重定位”的。 |
共享目标文件(shared object file) | 即动态连接库文件。它在以下两种情况下被使用:第一,在连接过程中与其它动态链接库或可重定位文件一起构建新的目标文件;第二,在可执行文件被加载的过程中,被动态链接到新的进程中,成为运行代码的一部分。 | |
可执行文件(executable file ) | 经过连接的,可以执行的程序文件。 |
ELF文件的作用有两个:一是用于构建程序,构建动态链接库或可执行程序等,主要体现在链接过程。二是用于运行程序。在这两种情况下,我们可以从不同的视角看待同一个目标文件。
从连接的角度和运行的角度,可以分别把目标文件的组成部分做以下划分:
1. ELF头文件
位于文件最开始处,包含整个文件的结构信息。
2. 节(section)
是专门用于连接过程而言的,在每个节中包含指令数据、符号数据、重定位数据等等。
3. 程序头表
在运行过程中是必须的,在链接过程中是可选的,因为它的作用是告诉系统如何创建进程的映像。
4. 节头表
包含文件中所用节的信息。
下面看一下Linux内核对ELF头文件的定义。
linux+v2.6.36/include/linux/elf.h:
|
我使用Ultraedit打开一个ELF文件,来分析它的前52个字节:
e_ident[0] | 7F | 目标文件最开头的前四个字节。 文件标志,表示是ELF文件 EI_MAG0 = 0, EI_MAG1 = 1, EI_MAG2 = 2, EI_MAG3 = 3 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_ident[1] | 45 == ‘E’ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_ident[2] | 4C == ’L’ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_ident[3] | 46 == ‘F’ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_ident[4] | 01 | 文件类别,取值如下:(EI_CLASS = 4) ELFCLASSNONE 0 非法目标文件 ELFCLASS32 1 32为目标文件 ELFCLASS64 2 64为目标文件 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_ident[5] | 01 | 编码格式,取值如下:(EI_DATA = 5) ELFDATANONE 0 非法编码格式 ELFDATA2LSB 1 小端编码格式 ELFDATA2MSB 2 大端编码格式 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_ident[6] | 01 | 文件版本,(EI_VERSION),为1表明是目前版本 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_ident[7] to e_ident[15] | 00 00 00 00 00 00 00 00 00 | 这就个字节暂且不使用,留在以后扩展。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_type | 00 01 | 此字段表明文件属于那种类型: ET_NONE 0 未知文件类型 ET_REL 1 可重定位文件 ET_EXEC 2 可执行文件 ET_DYN 3 动态链接库文件 ET_CORE 4 CORE文件 ET_LOPROC 0xff00 特定处理器文件扩展下边界 ET_HIPROC 0xffff 特定处理器文件扩展上边界 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_machine | 00 03 | 体系结构类型 : Intel 80386 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_version | 00 00 00 01 | 目前版本 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_entry | 00 00 00 00 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_phoff | 00 00 00 00 | 没有程序头部表。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_shoff | 00 00 00 FC | e_ident[0]是文件的第一个字节,从它开始FC(252)字节后就是节的数据信息。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_flags | 00 00 00 00 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_ehsize | 00 34 | 52字节的ELF头部结构体 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_phentsize | 00 00 | 因为没有程序头部表,所以大小为0 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_phnum | 00 00 | 同上 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_shentsize | 00 28 | 40字节的节区头部表 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_shnum | 00 0B | 节区头部表的表项有B(11)个 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e_shstrndx | 00 08 | 节头表中与节名字表相对应的表项的索引。 |
下面的一段程序用来读ELF文件的头部:readelf_h.c:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <errno.h>
8
9 #define FALSE 0
10 #define TURE 1
11 #define MAX_SIZE 52
12
13 typedef short int Elf32_Half;
14 typedef int Elf32_Word;
15 typedef Elf32_Word Elf32_Addr;
16 typedef Elf32_Word Elf32_Off;
17 /*Elf头部文件部分重要数据*/
18 typedef struct{
19 Elf32_Half e_type;
20 Elf32_Half e_machine;
21 Elf32_Word e_version;
22 Elf32_Addr e_entry; //程序入口的虚拟地址,如果目标文件没有程序入口,为0
23
24 Elf32_Off e_phoff; //程序头部表格的偏移量(按字节),如果文件中没有,为0
25
26 Elf32_Off e_shoff; //节区头部表格的偏移量(按字节),如果文件中没有,为0
27
28 Elf32_Word e_flags; //
29
30 Elf32_Half e_ehsize; //ELF头部的大小
31
32 Elf32_Half e_phentsize; //程序头部表格的表项大小。
33
34 Elf32_Half e_phnum; //程序头部表格的表项数目。
35
36 Elf32_Half e_shentsize; //节区头部表格的表项大小。
37
38 Elf32_Half e_shnum; //节区头部表格的表项数目。
39
40 Elf32_Half e_shstrndx;
41 }Elf_lan;
42 static Elf_lan lan_elf;
43 int OpenElf(char *filename)
44 {
45 int fd;
46 fd = open(filename, O_RDONLY);
47 if(fd == -1){
48 printf("Open %s Error!\n", filename);
49 return FALSE;
50 }
51 return fd;
52 }
53 //读取Elf头部表函数 :int ReadElf(int fd);
54
55 int ReadElf(int fd)
56 {
57 char str[MAX_SIZE];
58 int num;
59 memset(str, 0, MAX_SIZE);
60 if((num = read(fd, str, 52)) != 52){
61 perror("File NO ELF!\n");
62 return FALSE;
63 }
64 if((str[0] == 0x7f) && (str[1] == 'E') && (str[2] == 'L') && (str[3] == 'F')){
65 printf("This is ELF file.\n");
66 printf("文件类别: ");
67 switch(str[4]){
68 case 0:
69 printf("非法目标文件\n");
70 break;
71 case 1:
72 printf("32位目标文件\n");
73 break;
74 case 2:
75 printf("64位目标文件\n");
76 break;
77 default:
78 break;
79 }
80 printf("编码格式: ");
81 switch(str[5]){
82 case 0:
83 printf("非法编码格式\n");
84 break;
85 case 1:
86 printf("小端编码格式\n");
87 break;
88 case 2:
89 printf("大端编码格式\n");
90 break;
91 default:
92 break;
93 }
94 printf("文件版本: ");
95 if(str[6] == 1){
96 printf("当前版本\n");
97 }else{
98 printf("NULL\n");
99 }
100 printf("目标文件类型: ");
101 lan_elf.e_type = *((Elf32_Half *)&str[16]);
102 printf("e_type = %d\t", lan_elf.e_type);
103 switch(lan_elf.e_type){
104 case 0:
105 printf("未知文件类型\n");
106 break;
107 case 1:
108 printf("可重定位文件类型\n");
109 break;
110 case 2:
111 printf("可执行文件\n");
112 break;
113 case 3:
114 printf("动态链接库文件\n");
115 break;
116 case 4:
117 printf("CORE文件\n");
118 break;
119 default:
120 break;
121 }
122 printf("体系结构为:");
123 lan_elf.e_machine = *((Elf32_Half *)&str[18]);
124 printf("e_machine = %d\n", lan_elf.e_machine);
125 switch(lan_elf.e_machine){
126 case 0:
127 printf("未知体系结构");
128 break;
129 case 3:
130 printf("Intel 8086");
131 }
132 printf("版本信息: ");
133 lan_elf.e_version = *((Elf32_Word *)&str[20]);
134 if(lan_elf.e_version == 1){
135 printf("当前版本\n");
136 }else{
137 printf("NULL\n");
138 }
139 printf("程序入口的虚拟地址:");
140 lan_elf.e_entry = *((Elf32_Word *)&str[24]);
141 printf("0x%x\n", lan_elf.e_entry);
142
143 printf("程序头部表格的偏移量(按字节): ");
144 lan_elf.e_phoff = *((Elf32_Off *)&str[28]);
145 printf("0x%x, %d\n", lan_elf.e_phoff, lan_elf.e_phoff);
146
147 printf("节区头部表格的偏移量(按字节): ");
148 lan_elf.e_shoff = *((Elf32_Off *)&str[32]);
149 printf("0x%x, %d\n", lan_elf.e_shoff, lan_elf.e_shoff);
150
151 printf("处理器标志位: ");
152 lan_elf.e_flags = *((Elf32_Off *)&str[36]);
153 printf("%d\n", lan_elf.e_flags);
154
155 printf("ELF头文件大小: ");
156 lan_elf.e_ehsize = *((Elf32_Half *)&str[40]);
157 printf("0x%x, %d\n", lan_elf.e_ehsize, lan_elf.e_ehsize);
158
159 printf("程序头部表大小: ");
160 lan_elf.e_phentsize = *((Elf32_Half *)&str[42]);
161 printf("0x%x, %d\n", lan_elf.e_phentsize, lan_elf.e_phentsize);
162
163 printf("程序头部表的数目:");
164 lan_elf.e_phnum = *((Elf32_Half *)&str[44]);
165 printf("0x%x, %d\n", lan_elf.e_phnum, lan_elf.e_phnum);
166
167 printf("节区头部表大小: ");
168 lan_elf.e_shentsize = *((Elf32_Half *)&str[46]);
169 printf("0x%x, %d\n", lan_elf.e_shentsize, lan_elf.e_shentsize);
170
171 printf("节区头部表数目: ");
172 lan_elf.e_shnum = *((Elf32_Half *)&str[48]);
173 printf("0x%x, %d\n", lan_elf.e_shnum, lan_elf.e_shnum);
174
175 printf("节头表与节名字相对应的表项的索引: ");
176 lan_elf.e_shstrndx = *((Elf32_Half *)&str[50]);
177 printf("0x%x, %d\n", lan_elf.e_shstrndx, lan_elf.e_shstrndx);
178 return TURE;
179 }else{
180 perror("File NO ELF!\n");
181 return FALSE;
182 }
183 }
184
185 int main(int argc, char *argv[])
186 {
187 int boolen;
188 if(argc == 2){
189 boolen = OpenElf(argv[1]);
190 if(boolen == FALSE){
191 return -1;
192 }
193 ReadElf(boolen);
194 }
195
196 return 0;
197 }
$./readelf_lan readelf_lan.o
This is ELF file.
文件类别: 32位目标文件
编码格式: 小端编码格式
文件版本: 当前版本
目标文件类型: e_type = 1 可重定位文件类型
体系结构为:e_machine = 3
Intel 8086版本信息: 当前版本
程序入口的虚拟地址:0x0
程序头部表格的偏移量(按字节): 0x0, 0
节区头部表格的偏移量(按字节): 0xc00, 3072
处理器标志位: 0
ELF头文件大小: 0x34, 52
程序头部表大小: 0x0, 0
程序头部表的数目:0x0, 0
节区头部表大小: 0x28, 40
节区头部表数目: 0xc, 12
节头表与节名字相对应的表项的索引: 0x9, 9