Android 编译优化——OAT文件

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 文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值