OAT文件简介
OAT文件
是在Android4.4中引入的。OAT是优化过的、用于ART虚拟机执行的DEX文件,类似于Dalvik的ODEX文件。OAT文件遵循ELF格式。ELF是Unix系统上可执行文件,目标文件,共享库和Core dump文件的标准格式。ELF全称是Executable and Linkable Format,该文件格式如下图所示:
ELF文件格式
每个ELF文件包含一个ELF头信息,以及文件数据。
头信息描述了整个文件的基本属性,例如ELF文件版本,目标机器型号,程序入口地址等。
文件数据包含三种类型的数据:
- 程序表(Program header table):该数据会影响系统加载进程的内存地址空间
- 段表(Section header table):描述了ELF文件中各个段的(Section)信息
- 若干个段。常见的段包括:
- 代码段(.text):程序编译后的指令
- 只读数据段(.rodata):只读数据,通常是程序里面的只读变量和字符串常量
- 数据段:(.data):初始化了的全局静态变量和局部静态变量
- BSS端(.bss):未初始化的全局变量和局部静态变量
关于ELF文件格式的详细说明可以参见维基百科:Executable and Linkable Format ,这里不再深入讨论。
下面我们再来看一下OAT文件的格式:
OAT文件格式
从这个图中我们看到,OAT文件中包含的内容有:
- ELF Header:ELF头信息。
- oatdata symbol:oatdata符号,其地址指向了OAT头信息。
- Header:Oat文件的头信息,详细描述了Oat文件中的内容。例如:Oat文件的版本,Dex文件个数,指令集等等信息。Header,Dex File数组以及Class Metadata数组都位于ELF的只读数据段(.rodata)中。
- Dex File数组:生成该Oat文件的Dex文件,可能包含多个。
- Class Metadata数组:Dex中包含的类的基本信息,可能包含多个。通过其中的信息可以索引到编译后的机器码。
- 编译后的方法代码数组:每个方法编译后对应的机器码,可能包含多个。这些内容位于代码段(.text)中。
我们可以通过/art/目录下的这些源码文件来详细了解Oat文件的结构:
- compiler/oat_witer.h
- compiler/oat_writer.cc
- dex2oat/dex2oat.cc
- runtime/oat.h
- runtime/oat.cc
- runtime/oat_file.h
- runtime/oat_file.cc
- runtime/image.h
- runtime/image.cc
Oat文件的主要组成结构如下表所示:
Oat文件的主要组成结构如下表所示:
字段名称 | 说明 |
---|---|
OatHeader | Oat文件头信息 |
OatDexFile数组 | Dex文件的详细信息 |
Dex数组 | .dex文件的拷贝 |
TypeLookupTable数组 | 用来辅助查找Dex文件中的类 |
ClassOffsets数组 | OatDexFile中每个类的偏移表 |
OatClass数组 | 每个类的详细信息 |
padding | 如果需要,通过填充padding来让后面的内容进行页面对齐 |
OatMethodHeader | Oat文件中描述方法的头信息 |
MethodCode | 类的方法代码,OatMethodHeader和MethodCode会交替出现多次 |
dex文件可以通过dexdump
工具进行分析。oat文件也有对应的dump工具,这个工具就叫做oatdump
。
通过adb shell连上设备之后,可以通过输入oatdump
来查看该命令的帮助:
angler:/ # oatdump
No arguments specified
Usage: oatdump [options] ...
Example: oatdump --image=$ANDROID_PRODUCT_OUT/system/framework/boot.art
Example: adb shell oatdump --image=/system/framework/boot.art
--oat-file=<file.oat>: specifies an input oat filename.
Example: --oat-file=/system/framework/boot.oat
--image=<file.art>: specifies an input image location.
Example: --image=/system/framework/boot.art
--app-image=<file.art>: specifies an input app image. Must also have a specified
boot image and app oat file.
...
例如:可以通过–list-classes命令参数来列出dex文件中的所有类:
oatdump --list-classes --oat-file=/data/dalvik-cache/arm64/system@app@Calendar@Calendar.apk@classes.dex
boot.oat 与 boot.art
任何应用程序都不是孤立存在的,几乎所有应用程序都会依赖Android Framework中提供的基础类,例如Activity
,Intent
,Parcel
等类。所以在应用程序的代码中,自然少不了对于这些类的引用。因此,在上图中我们看到,代码(.text)段中的的代码会引用Framework Image和Framrwork Code中的内容。
考虑到几乎所有应用都存在这种引用关系,在运行时都会依赖于Framework中的类,因此系统如何处理这部分逻辑就是非常重要的了,因为这个处理的方法将影响到所有应用程序。
在AOSP编译时,会将所有这些公共类放到专门的一个Oat文件中,这个文件就是:boot.oat。与之配合的还有一个boot.art文件。
我们可以在设备上的/data/dalvik-cache/[platform]/目录下找到这两个文件:
-rw-r--r-- 1 root root 11026432 1970-06-23 01:35 system@framework@boot.art
-rw-r--r-- 1 root root 31207992 1970-06-23 01:35 system@framework@boot.oat
boot.art中包含了指向boot.oat中方法代码的指针,它被称之为启动镜像(Boot Image),并且被加载的位置是固定的。boot.oat被加载的地址紧随着boot.art。
包含在启动镜像中的类是一个很长的列表,它们在这个文件中配置:frameworks/base/config/preloaded-classes
。从Android L(5.0)之后的版本开始,设备厂商可以在设备的device.mk中通过PRODUCT_DEX_PREOPT_BOOT_FLAGS
这个变量来添加配置到启动镜像中的类。像这样:
PRODUCT_DEX_PREOPT_BOOT_FLAGS += --image-classes=<filename>
系统在初次启动时,会根据配置的列表来生成boot.oat和boot.art两个文件(读者也可以手动将/data/dalvik-cache/目录下文件都删掉来让系统重新生成),生成时的相关日志如下:
1249:10-04 04:25:45.700 530 530 I art : GenerateImage: /system/bin/dex2oat --image=/data/dalvik-cache/arm64/system@framework@boot.art --dex-file=/system/framework/core-oj.jar
--dex-file=/system/framework/core-libart.jar --dex-file=/system/framework/conscrypt.jar
--dex-file=/system/framework/okhttp.jar --dex-file=/system/framework/core-junit.jar
--dex-file=/system/framework/bouncycastle.jar --dex-file=/system/framework/ext.jar --dex-file=/system/framework/framework.jar --dex-file=/system/framework/telephony-common.jar
--dex-file=/system/framework/voip-common.jar --dex-file=/system/framework/ims-common.jar
--dex-file=/system/framework/apache-xml.jar --dex-file=/system/framework/org.apache.http.legacy.boot.jar --oat-file=/data/dalvik-cache/arm64/system@framework@boot.oat --instruction-set=arm64 -
-instruction-set-features=smp,a53
--base=0x6f96c000 --runtime-arg -Xms64m --runtime-arg -Xmx64m
--compiler-filter=verify-at-runtime
--image-classes=/system/etc/preloaded-classes
--compiled-classes=/system/etc/compiled-classes -j4
--instruction-set-variant=cor
OAT文件使用
- 如果有
.oat
文件(即.dex
文件的 AOT 二进制文件),ART 会直接使用该文件。虽然.oat
文件会定期生成,但文件中不一定会包含经过编译的代码(即 AOT 二进制文件)。 - 如果
.oat
文件不含经过编译的代码,ART 会通过 JIT 和解释器执行.dex
文件。