Android平台ELF文件格式简单介绍
基于Linux的操作系统可执行文件以Executable Link Format的形式存在,并属于COFF(Common File Format)的一种变体。与Windows系统的Portable Executable(PE)文件格式类似,ELF结构包含静态链接指令和动态链接入口点等关键特征。Android操作系统内核基于Linux框架实现后,在原生应用开发中发挥了重要作用,并与基于Linux的操作系统应用在功能上高度一致。
Android平台的游戏逻辑主要通过C和C++语言实现这一过程。经过编译生成的可执行文件遵循ELF格式标准。该平台的游戏逻辑架构示意图如图所示:

Android NDK提供了方便查看ELF文件格式的工具包。其中其名称为arm-linux-androideabi-readelf。该工具包位于NDK根目录下的特定子目录中具体路径为:android-ndk-r9c.toolchains-arm-linux-androideabi-4.6.prebuilt.windows-x86_64.bin。有关该工具包的具体使用方法可通过下图进一步了解

本章以libTest.so为例进行说明。接下来将深入分析ELF文件的结构与特征,并帮助读者更好地掌握Android平台原生应用程序的基本知识。
1.1.1文件头信息
该特定类型的文件编码元数据用于描述程序的基本属性信息,其中包括指定版本号、目标机器类型以及程序入口位置等内容。这些编码元数据以二进制形式呈现时占据内存空间为0x34字节。具体实例中的ELF编码元数据以二进制形式呈现

上图中标注为蓝色的数据块代表 elf 文件头的二进制内存内容,在右侧端部区域中可见明显的 'elf' 字符串标记;通过观察右侧端部的区域中可见的 'elf' 字符串即可确定该内存块是否属于 elf 格式。 arm-linux-androideabi 读取 elf 文件头信息的工具能够提供详细的查看方式
利用arm-linux-androideabi-readelf工具查看ELF文件头信息命令如下:
arm-linux-androideabi-readelf.exe -h libTest.so
对应获取的文件头信息如下:

上图重要信息解释如下:
Magic标识符:用于标识文件为特定ELF目标文件的关键数据块。其首字节固定设置为0x7F(十进制127),随后的三个字节依次对应字符'E'(ASCII编码)、'L'(ASCII编码)与'F'(ASCII编码)。Magic标识符中还包含了以下关键数据:文件版本号、机器架构类型以及文件类型等重要参数信息。
Type:表明文件类型,“DYN”表示实例所使用的文件属于动态链接库文件。
Machine:标识文件所采用指令类型,“ARM“表示当前ELF采用Arm指令集。
Start of program headers:表示程序头信息的起始偏移。
Start of section headers: 表示节表头信息的起始。
Size of program headers:表示程序头信息总共大小。
Number of program headers:表示程序头信息总共包含的项数。
Size of section headers: 表示节表头信息总共大小。
Number of section headers: 表示节表头信息总共包含的项数。
下面附上ELF文件头每项数据定义:
型别说明:elf�₂_hdr结构体
包含无符号字符数组用于标识符表信息
存储机器类型信息
记录机器架构信息
保存版本号信息
指定入口点位置
定义堆保护字节偏移量
定义栈保护字节偏移量
保存标志位字段信息
记录段落结束地址长度大小
指定物理进程空间大小
记录物理进程数目信息
保存堆结束地址长度大小数据项
记录堆进程数目信息
保存堆字符串索引值数据项
} Elf�₂_Ehdr;
ELF文件头属于最开始的信息,从整理描述ELF文件下面各子项信息。
1.1.2 程序头信息
ELF文件中的程序头部分构成一种数据结构。
每一个数据结构则用于描述一个特定的代码段或是系统为启动准备所需的各种信息。
在ELF文件中,每一个段(Segment)通常包含一个或多个区域(Section)。
在这种架构下各段之间通过共享相同的地址空间实现模块间的动态链接。
typedef struct elf32_phdr{
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr; /* virtual address /
Elf32_Addr p_paddr; / ignore /
Elf32_Word p_filesz; / segment size in file /
Elf32_Word p_memsz; / size in memory */
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
通过arm-linux-androideabi-readelf工具获取ELF文件程序头信息的相关指令如下:
arm-linux-androideabi-readelf.exe -l libTest.so
获取到的ELF文件程序头信息如下图所示:

在图中展示了7个数据片段。每个片段的FLg标记提供了获取其内存属性所需的信息。实际上,在这7个数据片段中只有两类片段具有实际意义:那些标记为‘LOAD’的片段。具体而言,每个片段提供了以下关键信息:其在文件中的起始位置、在内存中的起始位置、整体大小、访问权限以及对齐方式。其中仅有两类片段具有实际意义:那些标记为‘LOAD’的片段(即第2号和第3号片段)。
命令输出的具体信息中包含'Section to Segment mapping'这一项数据
1.1.3 节表头信息
在ELF文件中包含了多种节段信息,在这些节段信息中详细说明了 Elf32_Shdr 结构的具体组成和作用机制
typedef struct {
Elf32_Word sh_name; /* name of section, index */
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr; /* memory address, if any */
Elf32_Off sh_offset;
Elf32_Word sh_size; /* section size in file */
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize; /* fixed entry size, if have */
} Elf32_Shdr;
采用arm-linux-androideabi-readelf工具对ELF文件进行节表头信息查询,请参考以下具体命令:
arm-linux-androideabi-readelf.exe -S libTest.so
获取到的ELF文件节表头信息如下图所示:

从节表头信息中可以包含每个节的名称及其长度等内容,并提供内存占用情况以及文件之间的相对位置关系等详细信息。作为示例样本集合,在实验数据集中共有21个不同的阶段被记录下来;其中' . text '这一特定类型的段落具有极高的频率度,在所有阶段中占据主导地位;大部分编译后的代码最终存储在' . text '这一特定类型的数据段中。
ELF文件格式主要包含前述介绍的三个组成部分:包括ELF文件头、ELF程序头和ELF节表头。通过前述学习内容, 读者有助于掌握Android平台原生ELF文件格式的具体结构
