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文件的作用有两个:一是用于构建程序,构建动态链接库或可执行程序等,主要体现在链接过程。二是用于运行程序。在这两种情况下,我们可以从不同的视角看待同一个目标文件。

从连接的角度和运行的角度,可以分别把目标文件的组成部分做以下划分:


Linux ELF文件学习(1)_ide

1. ELF头文件

    位于文件最开始处,包含整个文件的结构信息。

2. 节(section)

    是专门用于连接过程而言的,在每个节中包含指令数据、符号数据、重定位数据等等。

3. 程序头表

    在运行过程中是必须的,在链接过程中是可选的,因为它的作用是告诉系统如何创建进程的映像。

4. 节头表

    包含文件中所用节的信息。

下面看一下Linux内核对ELF头文件的定义。

​linux+v2.6.36/include/linux/elf.h​:


/* 32-bit ELF base types. */
typedef __u32 Elf32_Addr;
typedef __u16 Elf32_Half;
typedef __u32 Elf32_Off;
typedef __s32 Elf32_Sword;
typedef __u32 Elf32_Word;

 #define EI_NIDENT 16

 typedef struct elf32_hdr{
  unsigned char e_ident[EI_NIDENT]; //16字节的信息,下文详细解释
  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;  //ELF头部的大小
  Elf32_Half e_phentsize;  //程序头部表的表项大小
  Elf32_Half e_phnum;  //程序头部表的数目
  Elf32_Half e_shentsize; //节区头部表的表项大小
  Elf32_Half e_shnum; //节区头部表的数目
  Elf32_Half e_shstrndx; //
 } Elf32_Ehdr;  //此结构体一共52个字节

我使用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