ART世界探险(11) - OAT文件格式分析(1) - ELF文件头(上)
既然是要探险,咱们就保持一定的深度,起码将来可以做个基于ART的黑客之类的。
所以我们针对细节多下一些工夫,先仔细分析一下OAT文件的格式。
ART的本质上是一个编译器,所以我们需要对编译、链接的主要环节都有一个比较深入的了解。想要绕过编译原理去学好ART,是不太现实的一件事情,我们选择可以让这个过程有趣和好玩。
闲扯不多说了,言归正传。
可执行文件
OAT是一种可执行文件,所以封装在一个ELF格式的可执行文件中。
可执行文件,我们可以借大家熟悉的Windows操作系统理解,就是Windows中的exe文件和dll文件。
- Windows下的exe,dll文件的格式是PE(Portable Executable)
- Linux/Android下的可执行格式是ELF(Executable Linkable Format)
- Mac/iPhone下的可执行文件的格式是Mach-O(Mach Object)
PE和ELF都是COFF格式(Common file format)的变种。
ELF文件的分类
ELF文件可以分为以下4种:
- 可重定位文件:Relocatable File,就像Windows下的.obj文件和Linux下的.o文件一样的目标文件,需要链接才可以执行。
- 真正的可执行文件:Executable File,如Windows的exe一样,是可以直接运行的程序。
- 共享目标文件:Shared Object File,像Windows的dll和Linux下的so一些,动态链接库。
- 核心转储文件:Core dump,用于意外时的地址空间的转储。
这个分类信息,在ELF文件头里面有描述,我们后面会讲到。
ELF文件头
我们把ELF文件头分成两部分来分析,我们先来看前半部分:
Magic Number
所谓的Magic Number,其实就是文件的一个标记。
ELF的Magic Number一共占4个字节,首字节为0x7f,后面分别为'E','L','F'.
只支持大写,必须是0x7f,0x45,0x4c,0x46
我们以Go语言为例,写一段解析Magic Number的代码来作为示例吧:
buf, err := ioutil.ReadFile(elfFile.elfFileName)
if err != nil {
fmt.Println("Error reading ELF:", err)
}
magic := [...]byte{0x7f, 'E', 'L', 'F'}
magic2 := buf[:4]
if (magic[0] == magic2[0]) && (magic[1] == magic2[1]) && (magic[2] == magic2[2]) && (magic[3] == magic2[3]) {
fmt.Println("It is an ELF")
} else {
fmt.Println("It is not an ELF")
return
}
位宽
- 1-代表32位
- 2-代表64位
大端还是小端
这个是在多字节的情况下,是高位在前还是低位在前。
- 1-Little Endian,小端
- 2-Big Endian,大端
操作系统
值 | 操作系统 |
---|---|
0x00 | System V |
0x03 | Linux |
0x06 | Solaris |
0x09 | FreeBSD |
在Android上肯定是0x03了。
ELF子类型
这个就是我们上面画图的那4大类型
值 | ELF子类型 |
---|---|
0x01 | 可重定位文件 |
0x02 | 可执行文件 |
0x03 | 动态链接库 |
0x04 | core dump |
芯片架构
值 | 芯片架构 |
---|---|
0x00 | 未知 |
0x03 | x86 |
0x08 | MIPS |
0x28 | ARM |
0x3e | x86_64 |
0xb7 | AArch64 |
ELF头分析示例
ELF文件头的结构(第一部分)
偏移量 | 长度 | 描述 | 备注 |
---|---|---|---|
0x00 | 4 | Magic Number | |
0x04 | 1 | 位数 | 32位还是64位 |
0x05 | 1 | 大端还是小端 | |
0x06 | 1 | 版本号 | 1 |
0x07 | 1 | 操作系统 | |
0x08 | 8 | 暂时未用到 | |
0x10 | 2 | ELF文件的子类型 | |
0x12 | 2 | 芯片架构 | |
0x14 | 4 | 又一个版本号 | 设成1 |
我们下面解析一个实际的OAT文件看看
下面是我从一个64位arm的OAT文件截取的一部分,我们来分析一下:
7F 45 4C 46 02 01 01 03 00 00 00 00 00 00 00 00
03 00 B7 00 01 00 00 00 00 00 00 00 00 00 00 00
40 00 00 00 00 00 00 00 E0 3A AA 00 00 00 00 00
00 00 00 00 40 00 38 00 07 00 40 00 12 00 11 00
06 00 00 00 04 00 00 00 40 00 00 00 00 00 00 00
40 00 00 00 00 00 00 00
- 7F 45 4C 46: Magic Number,就不多说了
- 02:64位
- 01:Little Endian,小端
- 01:版本号1
- 03:Linux
- 8个0:没用到
- 03 00:动态链接库。注意了,我们的OAT文件是so!
- B7 00: AArch64,arm64-v8a在AArch64状态下
- 01 00 00 00: 版本号1