android是建立在linux的基础上,其底层代码是安装linux可执行文件——elf的格式来组装的。本文结合android中的so文件来了解elf格式,资料大多收集于网上;elf格式位于android源码:elf.h。
elf大致可分为三部分:elf头、程序头表、节区头表;当然还有上图没标出的动态符号表,
elf头:
#define EI_NIDENT 16 typedef struct { unsigned char e_ident[EI_NIDENT]; //magic Elf32_Half e_type; //type 1:重定位文件;2:可执行文件;3:共享文件 Elf32_Half e_machine; //cpu结构 Elf32_Word e_version; //版本 Elf32_Addr e_entry; //程序进入点 可执行:main;so:无用 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;
我们会发现 e_ehsize = e_phoff(为什么?看第一幅图)。在elf头中我们很容易发现其实主要分三部分:info相关,程序头相关,节区头相关;刚好对应着链接器和装载器所需内容。e_phoff、e_phentsize、e_phnum装载器必须;e_shoff、e_shentsize、e_shnum、e_shstrndx是链接器必须。
phdr头:
typedef struct elf32_phdr{ Elf32_Word p_type; //segment类型 Elf32_Off p_offset; //该segment的偏移地址 Elf32_Addr p_vaddr; //segment映射到内存中的地址 Elf32_Addr p_paddr; //该地址不会用到 Elf32_Word p_filesz; //segment大小 Elf32_Word p_memsz; //segment在内存中所占的地址空间大小 Elf32_Word p_flags; //segment可操作的读写权限 Elf32_Word p_align; //按几个字节对齐 } Elf32_Phdr;
重点是p_offset和p_filesz,它们是segment的起始地址和大小;p_type为segment类型详见elf.h中的PT开头的宏定义;p_flags为segment的可操作权限详见elf.h中的PF开头宏定义(跟linux的文件权限rwx是一样的)。
shdr头:
typedef struct { Elf32_Word sh_name; //section name:.data、.dynamic、.got、.init...... Elf32_Word sh_type; //section类型 Elf32_Word sh_flags; //section权限 Elf32_Addr sh_addr; //section映射到内存中起始地址 Elf32_Off sh_offset; //该section在文件中的偏移 Elf32_Word sh_size; //section大小 Elf32_Word sh_link; //一般来说是该section所用的string table在section header table中的索引,见参考资料3 Elf32_Word sh_info; // Elf32_Word sh_addralign;//section按几字节对齐 Elf32_Word sh_entsize; //section内容中表项所占大小,例如.dynamic为8下面解释 } Elf32_Shdr;
动态符号表(dynamic_symbol_table):
介绍完elf格式的整体框架后,来深入了解其中的联系和一些section。
.dynamic:该section包含了动态链接信息,该section属性将包含SHF_ALLOC比特位,而SHF_WRITE比特位是否为1取决于处理器;简单来说它包含着一连串的dynamic结构
typedef struct dynamic{ Elf32_Sword d_tag; union{ Elf32_Sword d_val; Elf32_Addr d_ptr; } d_un; } Elf32_Dyn;
d_tag控制d_un是d_val还是d_ptr;可通过d_tag来识别是属于哪个section(elf.h中DT开头的宏定义),d_un为d_tag在文件中的偏移量(不完全正确!,以后再补充)。例如d_tag为6则是DT_SYMTAB为.dynsym,则d_un为.dynsym为偏移量。值得一提的是在该section中,sh_addralign为4,sh_entsize为8(为什么看dynamic结构体)。
一些tips:
1 字符串符号表.shstrtab后跟着section_header_table;节区表头分布在elf文件最后,而字符串符号表往往是在最靠后的内容。
2
资料: