ELF文件格式

在介绍ELF格式之前,先简单说明一下可执行文件的生成流程:1)编写C源文件,或汇编源文件;2)准备共享库格式的目标文件(shared object file),如数学库、标准库;2)用编译器(compiler)将C编译成可重定位格式的目标文件(relocatable object file),用汇编器(assembler)将汇编源文件编译成可重定位格式的目标文件;3)用连接器(linker)将第二步的共享个库文件和第三步生成的目标文件链接生成可执行文件(executable file)。

 

ELFexcutable and linking format)是一种可执行可链接格式的二进制文件,它可以用来表示relocatable fileexecutable file或者shared object file,这三者都是目标文件(object file)。所谓“可执行”指可以被调入内存供CPU直接运行;“可链接”指多个ELF格式的目标文件可以被链接在一起形成一个可执行文件。下图左边是可链接格式的ELF文件格式,右边是可执行格式的ELF文件格式。

无论是linking view还是execution viewELF文件,他们都包含一个ELF Header,它包含文件的基本信息。ELF自定义了一些类型,并强制规定了他们所占的字节个数,以实现跨平台,如Elf32_Half2字节、Elf32_Word4字节、Elf32_Off4字节等。

ELF Header数据结构
e_ident[EI_NIDENT]是一个有16个字节的数组,e_ident[0]=0x7fe_ident[1]=”E”e_ident[2]=”L” e_ident[3]=”F” e_ident[4]指示ELFCLASS0ELFCLASSNONE1ELFCLASS322ELFCLASS64),e_ident[5]指示程序中的数据格式(0:无效;1:小端;2:大端),e_ident[6]指示版本固定为0x1e_ident[7]e_ident[14]固定为0e_ident[15]固定为16用于指示e_ident[]数组有16个元素。e_type指示该ELF文件的类型,占2字节,可以为ET_NONE(0, No file type)/ ET_REL(1, Relocatable file)/ ET_EXEC(2, Executable file)/ ET_DYN(3, Shared object file)/ ET_CORE(4, Core file)/ ET_LOPROC(0xff00, Processor-specific)/ ET_HIPROC(0xffff, Processor-specific),常见的是ET_EXECET_DYNET_RELe_machine定义CPU的指令集架构,ELF标准中定义的有EM_NONE(0)/ EM_M32(1)/ EM_SPARC(2)/ EM_386(3)/ EM_68K(4)/ EM_88K(5)/ EM_860(6)/EM_MIPS(7),其它的值保留,有用户使用,比如EM_OR32=0x8472e_version定义该ELF文件的版本号。e_entry定义程序的启动地址,也即CPU启动后读取的第一条指令所在的地址。e_phoff定义代表Program header table的数据结构在文件中的以字节为单位的偏移量,为0表示没有Program header tablee_shoff定义代表Section header table数据结构在文件中的以字节为单位的偏移量,为0表示没有Section header tablee_flags定义了和CPU相关的一些参数,不同的CPUe_flags的定义是不一样的。e_ehsize定义ELF header这个数据结构的以字节为单位的实际大小,之所以出现这个参数,是因为各个平台上ELF自定义类型所表示的字节数不同。一个Program header table包含多个entry即多个Program header,每个entry的占用的文件字节数相同,e_phentsize定义了每个entry的大小,e_phnum定义当前Program header tableentry的个数。一个Section header table包含多个entry即多个Section header,每个entry的占用的文件字节数相同,e_shentsize定义了每个entry的大小,e_shnum定义当前Section header tableentry的个数。在Section header table中有一个特殊的entry,它定义了Section header table中各个entry的名字,e_shstrndx用于指示这个特殊的entrySection header table中的索引,即它在第几个entry,这个特殊的entry称为section name string table
Code

前面已经提到,Section header table有多个entry,其实每个entry都是一个Elf32_Shdr类型数据结构,用这个数据结构可以找到每个Section在文件中的位置,e_shentsize=sizeof(Elf32_Shdr)e_shnum=numof(Elf32_Shdr)sh_name是一个字符串数组的索引,它指示当前Section header所描述的这个Section的名字在Section name string table中的位置。sh_type指示当前Section header的类型,SHT_NULL0,该Section header所指示的section无效,不占用文件空间)/ SHT_PROGBITS1,该Section header所指示的section包含程序指令)/ SHT_SYMTAB2,该Section header所指示的section包含重定位符号表)/ SHT_STRTAB3,该Section header所指示的section包含字符表)/ SHT_RELA4,重定位相关,用于链接过程)/ SHT_HASH5,重定位相关,用于链接过程)/ SHT_DYNAMIC6,用于链接过程)/ SHT_NOTE7,信息说明)/ SHT_NOBITS8,,该Section header所指示的section不占用文件字节,除此之外和SHT_PROGBITS含义相同) /SHT_REL9,重定位相关)/ SHT_SHLIB10,保留类型)/ SHT_DYNSYM11,重定位相关)/ SHT_LOPROC0x70000000- SHT_HIPROC0x7fffffff)(这一范围的类型值跟CPU相关)/ SHT_LOUSER0x80000000- SHT_HIUSER0xffffffff)(这一范围的类型值保留给应用程序)。sh_flags指示当前Section header所对应的section的属性,WRITE0x1,当前Section header所对应的section包含可写的数据)/ALLOC0x2,当前Section header所对应的section在程序执行时占用实际的存储空间)/EXECINSTR0x4,当前Section header所对应的section包含可执行的指令)/MASKPROC0xf0000000,保留)。sh_addr指示当前Section header所对应的section在内存中起始地址。sh_offset指示当前Section header所对应的section在文件中以字节为单位的偏移量。sh_size指示当前Section header所对应的section在文件中占用的字节数。sh_linksh_info供链接器使用。sh_addralign指示对sh_addr的对其要求。有一些特殊的section,它内部仍然包含多个entry,每个entry大小相同,如symbol tablesh_entsize用于指示这个特殊的section中每个entry的大小。

Code

上表列出了一些常见的section.bss中,包含未初始化的全局数据变量,这些变量不占用ELF文件的空间(SHT_NOBITS),但占用实际内存空间(SHF_ALLOC),在程序运行启动时由OS负责初始化为0.comment包含版本控制信息。.data.data1包含已经初始化的全局数据变量,这些变量占用ELF文件空间,也占用实际内存空间。.fini包含主函数退出时执行的指令。.init包含主函数执行前所执行的指令。.rodata.rodata1包含只读数据。.shstrtab包含section header string table.text包含程序指令。

typedef struct
{
Elf32_Word     p_type;      
/* Segment type */
Elf32_Off       p_offset;      
/* Segment offset in file */
Elf32_Addr    p_vaddr;      
/* Segment virtual address in memory*/
Elf32_Addr     p_paddr;      
/* Segment physical address */
Elf32_Word     p_filesz;      
/* Segment size in file */
Elf32_Word     p_memsz;     
/* Segment size in memory */
Elf32_Word     p_flags;       
/* Segment flags */
Elf32_Word     p_align;      
/* Segment alignment */
} Elf32_Phdr;
 

前面已经提到,Program header table有多个entry,其实每个entry都是一个Elf32_Phdr类型数据结构,用这个数据结构可以找到每个Segment在文件中的位置,e_phentsize=sizeof(Elf32_Phdr)e_phnum=numof(Elf32_Phdr),每个Segment包含若干个section,只有对executable fileshared object file才能存在Program headerp_type指示当前Program header所指的Segment的类型,PT_NULL0,指示无效的segment/ PT_LOAD1,指示当前Program header所指的Segment须加载到内存中执行)/ PT_DYNAMIC2/ PT_INTERP3/ PT_NOTE4/ PT_SHLIB5/ PT_PHDR6/PT_LOPROC0x70000000/PT_HIPROC0x7fffffff)。p_offset指示Program header所指的Segment在文件中偏移量。p_vaddr指示Program header所指的Segment在内存中的虚拟地址。p_vaddr指示Program header所指的Segment在内存中的物理地址,一般可忽略。p_filesz指示Program header所指的Segment在文件中的大小。p_memsz指示Program header所指的Segment在内存中的大小,一般地,p_memsz>=p_filesz,对p_filesz不足的内存区域填0

 

 

 

 

posted @ 2009-07-04 23:41  brianhxh  阅读(5662)  评论(0编辑  收藏  举报