ELF文件是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。
1、文件结构视图
2、elf重定义的数据类型
在linux-4.10/include/uapi/linux/elf.h
7 /* 32-bit ELF base types. */
8 typedef __u32 Elf32_Addr;
9 typedef __u16 Elf32_Half;
10 typedef __u32 Elf32_Off;
11 typedef __s32 Elf32_Sword;
12 typedef __u32 Elf32_Word;
13
14 /* 64-bit ELF base types. */
15 typedef __u64 Elf64_Addr;
16 typedef __u16 Elf64_Half;
17 typedef __s16 Elf64_SHalf;
18 typedef __u64 Elf64_Off;
19 typedef __s32 Elf64_Sword;
20 typedef __u32 Elf64_Word;
21 typedef __u64 Elf64_Xword;
22 typedef __s64 Elf64_Sxword;
知道了重定义的数据类型,我们才知道对应的结构体占用的字节数,方便读取和解析文件
3、elf文件的文件头
该结构体定义在 linux-4.10/include/uapi/linux/elf.h
201 #define EI_NIDENT 16
202
203 typedef struct elf32_hdr{
204 unsigned char e_ident[EI_NIDENT];
205 Elf32_Half e_type;
206 Elf32_Half e_machine;
207 Elf32_Word e_version;
208 Elf32_Addr e_entry; /* Entry point */
209 Elf32_Off e_phoff;
210 Elf32_Off e_shoff;
211 Elf32_Word e_flags;
212 Elf32_Half e_ehsize;
213 Elf32_Half e_phentsize;
214 Elf32_Half e_phnum;
215 Elf32_Half e_shentsize;
216 Elf32_Half e_shnum;
217 Elf32_Half e_shstrndx;
218 } Elf32_Ehdr;
219
220 typedef struct elf64_hdr {
221 unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
222 Elf64_Half e_type;
223 Elf64_Half e_machine;
224 Elf64_Word e_version;
225 Elf64_Addr e_entry; /* Entry point virtual address */
226 Elf64_Off e_phoff; /* Program header table file offset */
227 Elf64_Off e_shoff; /* Section header table file offset */
228 Elf64_Word e_flags;
229 Elf64_Half e_ehsize;
230 Elf64_Half e_phentsize;
231 Elf64_Half e_phnum;
232 Elf64_Half e_shentsize;
233 Elf64_Half e_shnum;
234 Elf64_Half e_shstrndx;
235 } Elf64_Ehdr;
1)e_ident[EI_NIDENT] 长度为16 char数组,可以理解为文件的标识
2)e_type 该elf文件的类型
/* These constants define the different elf file types */
64 #define ET_NONE 0
65 #define ET_REL 1 //可重定位文件,一般是编译后的.o文件
66 #define ET_EXEC 2 //
67 #define ET_DYN 3 //
68 #define ET_CORE 4 //coredump文件
69 #define ET_LOPROC 0xff00
70 #define ET_HIPROC 0xffff
3)e_machine 运行时所需的cpu体系结构
4)e_version 文件的版本
5)e_entry; 可执行程序的入口地址
6)e_phoff 程序头表(programheader table)相对于文件开始位置的偏移量
7)e_shoff 节头表(sectionheader table)相对于文件开始位置的偏移量
8)e_flags相关文件的特定处理器标志
9)e_ehsize elf header的大小
10)e_phentsize 程序头表(program headertable)中每一项的大小
11)e_phnum 程序头表的数目,也就是有多少项
12)e_shentsize节头表(sectionheader table)中每一项的大小
13)e_shnum 节头表的数目,也就是有多少项
14)e_shstrndx 节名字字符表在节头表(sectionheader table)中的索引,也就是在那一项
该结构体定义在 linux-4.10/include/uapi/linux/elf.h
typedef struct elf32_phdr{
244 Elf32_Word p_type;
245 Elf32_Off p_offset;
246 Elf32_Addr p_vaddr;
247 Elf32_Addr p_paddr;
248 Elf32_Word p_filesz;
249 Elf32_Word p_memsz;
250 Elf32_Word p_flags;
251 Elf32_Word p_align;
252 } Elf32_Phdr;
253
254 typedef struct elf64_phdr {
255 Elf64_Word p_type;
256 Elf64_Word p_flags;
257 Elf64_Off p_offset; /* Segment file offset */
258 Elf64_Addr p_vaddr; /* Segment virtual address */
259 Elf64_Addr p_paddr; /* Segment physical address */
260 Elf64_Xword p_filesz; /* Segment size in file */
261 Elf64_Xword p_memsz; /* Segment size in memory */
262 Elf64_Xword p_align; /* Segment alignment, file & memory */
263 } Elf64_Phdr;
1)p_type描述的段的类型
25 #define PT_NULL 0 //未使用
26 #define PT_LOAD 1 //可加载段
27 #define PT_DYNAMIC 2 //给出动态链接的信息
28 #define PT_INTERP 3 //解释器段
29 #define PT_NOTE 4 //附加信息的位置和大小
30 #define PT_SHLIB 5 //保留
31 #define PT_PHDR 6 //程序头表自身位置和大小
32 #define PT_TLS 7 /* Thread local storage segment */
33 #define PT_LOOS 0x60000000 /* OS-specific */
34 #define PT_HIOS 0x6fffffff /* OS-specific */
35 #define PT_LOPROC 0x70000000
36 #define PT_HIPROC 0x7fffffff
37 #define PT_GNU_EH_FRAME 0x6474e550
38
39 #define PT_GNU_STACK (PT_LOOS + 0x474e551)
2)p_flags 此成员给出与段相关的标志
3)p_offset 此成员给出从文件头到该段第一个字节的偏移
4)p_vaddr 此成员给出段的第一个字节将被放到内存中的虚拟地址
5)p_paddr此成员仅用于与物理地址相关的系统中
6)p_filesz 此成员给出段在文件映像中所占的字节数
7)p_memsz 此成员给出段在内存映像中占用的字节数
8)p_align给出段在文件中和内存中如何对齐,数值 0 和 1 表示不需要对齐
程序表头中的每一项,就是上面文件结构视图中的一个segment,一个segment 可能包含多个section。
5、elf文件中的节头表
elf文件中的程序表头typedef struct elf32_shdr {
303 Elf32_Word sh_name;
304 Elf32_Word sh_type;
305 Elf32_Word sh_flags;
306 Elf32_Addr sh_addr;
307 Elf32_Off sh_offset;
308 Elf32_Word sh_size;
309 Elf32_Word sh_link;
310 Elf32_Word sh_info;
311 Elf32_Word sh_addralign;
312 Elf32_Word sh_entsize;
313 } Elf32_Shdr;
314
315 typedef struct elf64_shdr {
316 Elf64_Word sh_name; /* Section name, index in string tbl */
317 Elf64_Word sh_type; /* Type of section */
318 Elf64_Xword sh_flags; /* Miscellaneous section attributes */
319 Elf64_Addr sh_addr; /* Section virtual addr at execution */
320 Elf64_Off sh_offset; /* Section file offset */
321 Elf64_Xword sh_size; /* Size of section in bytes */
322 Elf64_Word sh_link; /* Index of another section */
323 Elf64_Word sh_info; /* Additional section information */
324 Elf64_Xword sh_addralign; /* Section alignment */
325 Elf64_Xword sh_entsize; /* Entry size if section holds table */
326 } Elf64_Shdr;
1)sh_name 节的名称,在节区头部字符串中的索引
2)sh_type 节区内容的分类
3)sh_flags节的属性
4)sh_addr如果此节的内容将出现在进程空间里,这个字段给出了该节在内存中起始地址。
5)sh_offset如果此节在文件中占用一定的字节,这个字段给出了该节在整个文件中的起始偏移量。
6)sh_size如果此节在文件中占用一定的字节,这个字段给出了该节在文件中的字节大小
7)sh_link如果另一个节与这个节相关联,这个字段给出了相关的节在节头中的索引
8)sh_info 附加信息
9)sh_addralign地址对齐。
10)sh_entsize 代表字节大小的数,对某些节才有意义
package hxiong;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ELFReader {
public static final String ELF_FILE_PATH="E:\\private\\AOSP\\ndk\\tool\\readelf\\logcat";
public static final String ELF_="";
public static final String ELF_IDENT="e_ident";
public static final String ELF_TYPE="e_type";
public static final String ELF_MACHINE="e_machine";
public static final String ELF_VERSION="e_version";
public static final String ELF_ENTRY="e_entry";
public static final String ELF_PHOFF="e_phoff;";
public static final String ELF_SHOFF="e_shoff";
public static final String ELF_FLAGS="e_flags";
public static final String ELF_EHSIZE="e_ehsize";
public static final String ELF_PHENTSIZE="e_phentsize";
public static final String ELF_PHNUM="e_phnum";
public static final String ELF_SHENTSIZE="e_shentsize";
public static final String ELF_SHNUM="e_shnum";
public static final String ELF_SHSTRNDX="e_shstrndx";
//
public static final int ELF_32=1;
public static final int ELF_64=2;
public static final int ERROR=-1;
/* These constants are for the segment types stored in the image headers */
public static final int PT_LOAD = 1;
public static final int PT_DYNAMIC = 2;
public static final int PT_INTERP = 3;
public static void main(String[] args) {
ELFReader elfReader=new ELFReader();
elfReader.readELF(ELF_FILE_PATH);
}
public void readELF(String elfPath){
File file=new File(elfPath);
if(file.exists()&&file.isFile()){
readELF(file);
}else{
System.out.println("error:"+elfPath+"is not exit or not a file.");
}
}
private void readELF(File elfFile){
if(elfFile==null){
System.out.println("error:elfFile is null.");
}else{
FileInputReader fReader=new FileInputReader(elfFile);
readELF(fReader);
fReader.close();
}
}
private void readELF(FileInputReader fReader){
byte[] ident=fReader.readBytes(16);
System.out.println("-------------- elf header --------------");
printfBytes(ELF_IDENT,ident);
if(ident[4]==ELF_32){
readELF32(fReader);
}else if(ident[4]==ELF_64){
readELF64(fReader);
}else{
System.out.println("error:unknown type.");
}
}
//elf32文件的解析就不写出来了,参照elf64的解析过程即可
private void readELF32(FileInputReader fReader){
}
private void readELF64(FileInputReader fReader){
printfShort(ELF_TYPE,fReader.readShort());
printfShort(ELF_MACHINE,fReader.readShort());
printfInt(ELF_VERSION,fReader.readInt());
long elfEntry=fReader.readLong();
long elfPhOff=fReader.readLong();
long elfShOff=fReader.readLong();
printfLong(ELF_ENTRY,elfEntry);
printfLong(ELF_PHOFF,elfPhOff);
printfLong(ELF_SHOFF,elfShOff);
printfInt(ELF_FLAGS,fReader.readInt());
printfShort(ELF_EHSIZE,fReader.readShort());
short elfPhSize=fReader.readShort();
short elfPhNum=fReader.readShort();
short elfShSize=fReader.readShort();
short elfShNum=fReader.readShort();
short elfShStrIndx=fReader.readShort();
printfShort(ELF_PHENTSIZE,elfPhSize);
printfShort(ELF_PHNUM,elfPhNum);
printfShort(ELF_SHENTSIZE,elfShSize);
printfShort(ELF_SHNUM,elfShNum);
printfShort(ELF_SHSTRNDX,elfShStrIndx);
if(elfPhNum>0){
readELFProgramHeader(fReader,elfPhOff,(int)elfPhSize,(int)elfPhNum);
}
if(elfShNum>0){
readELFSectionHeader(fReader,elfShOff,(int)elfShSize,(int)elfShNum);
}
if(elfShStrIndx>0){
readELFShStr(fReader,elfShOff,(int)elfShSize,(int)elfShStrIndx);
}
}
private void readELFProgramHeader(FileInputReader fReader,long offset,int size,int num){
System.out.println();
System.out.println("-------------- program header --------------");
int segmentType=0;
int segmentFlags=0;
long segmentOffset=0;
long segmentSize=0;
if(fReader.markAndReset(offset)){
System.out.println("index\tp_type p_flags p_offset p_vaddr p_paddr p_filesz p_memsz p_align");
for(int i=0;i0){ //PT_INTERP
System.out.println();
System.out.println("-------------- interp segment --------------");
if(fReader.markAndReset(segmentOffset)){
byte[] segmentContent=fReader.readBytes((int)segmentSize);
System.out.println("interp is:"+new String(segmentContent));
}
}
}
}
private void readELFSectionHeader(FileInputReader fReader,long offset,int size,int num){
System.out.println();
System.out.println("-------------- section header --------------");
if(fReader.markAndReset(offset)){
System.out.println("index\tsh_name sh_type sh_flags sh_add sh_offset sh_size sh_link sh_info sh_addralign sh_entsize");
for(int i=0;i
-------------- elf header --------------
e_ident: 127 69 76 70 2 1 1 0 0 0 0 0 0 0 0 0
e_type: 3
e_machine: 183
e_version: 1
e_entry: 9924
e_phoff;: 64
e_shoff: 33240
e_flags: 0
e_ehsize: 64
e_phentsize: 56
e_phnum: 9
e_shentsize: 64
e_shnum: 29
e_shstrndx: 28
-------------- program header --------------
index p_type p_flags p_offset p_vaddr p_paddr p_filesz p_memsz p_align
[0] 6 4 64 64 64 504 504 8
[1] 3 4 568 568 568 21 21 1
[2] 1 5 0 0 0 28932 28932 4096
[3] 1 6 30608 34704 34704 2184 2272 4096
[4] 2 6 31328 35424 35424 624 624 8
[5] 4 4 592 592 592 56 56 4
[6] 1685382480 4 28864 28864 28864 68 68 4
[7] 1685382481 6 0 0 0 0 0 0
[8] 1685382482 6 30608 34704 34704 2160 2160 8
-------------- interp segment --------------
interp is:/system/bin/linker64
-------------- section header --------------
index sh_name sh_type sh_flags sh_add sh_offset sh_size sh_link sh_info sh_addralign sh_entsize
[0] 0 0 0 0 0 0 0 0 0 0
[1] 11 1 2 568 568 21 0 0 1 0
[2] 19 7 2 592 592 24 0 0 4 0
[3] 39 7 2 616 616 32 0 0 4 0
[4] 58 11 2 648 648 2424 5 1 8 24
[5] 66 3 2 3072 3072 2171 0 0 1 0
[6] 74 1879048182 2 5248 5248 64 4 0 8 0
[7] 84 1879048191 2 5312 5312 202 4 0 2 2
[8] 97 1879048190 2 5516 5516 80 5 2 4 0
[9] 112 4 2 5600 5600 768 4 0 8 24
[10] 122 4 66 6368 6368 2064 4 11 8 24
[11] 127 1 6 8432 8432 1408 0 0 8 16
[12] 132 1 6 9840 9840 12564 0 0 4 0
[13] 138 1 2 22404 22404 6107 0 0 4 0
[14] 146 1 2 28512 28512 352 0 0 8 0
[15] 156 1 2 28864 28864 68 0 0 4 0
[16] 170 16 3 34704 30608 16 0 0 8 0
[17] 185 14 3 34720 30624 16 0 0 8 0
[18] 197 15 3 34736 30640 16 0 0 8 0
[19] 209 1 3 34752 30656 672 0 0 8 0
[20] 222 6 3 35424 31328 624 5 0 8 16
[21] 231 1 3 36048 31952 104 0 0 8 0
[22] 236 1 3 36152 32056 712 0 0 8 0
[23] 245 1 3 36864 32768 24 0 0 8 0
[24] 251 8 3 36888 32792 88 0 0 8 0
[25] 256 1 48 0 32792 99 0 0 1 1
[26] 265 7 0 0 32892 28 0 0 4 0
[27] 288 1 0 0 32920 12 0 0 1 0
[28] 1 3 0 0 32932 303 0 0 1 0
-------------- section string table --------------
index value
[0]
[1] .shstrtab
[2] .interp
[3] .note.android.ident
[4] .note.gnu.build-id
[5] .dynsym
[6] .dynstr
[7] .gnu.hash
[8] .gnu.version
[9] .gnu.version_r
[10] .rela.dyn
[11] .rela.plt
[12] .text
[13] .rodata
[14] .eh_frame
[15] .eh_frame_hdr
[16] .preinit_array
[17] .init_array
[18] .fini_array
[19] .data.rel.ro
[20] .dynamic
[21] .got
[22] .got.plt
[23] .data
[24] .bss
[25] .comment
[26] .note.gnu.gold-version
[27] .gnu_debuglink