内容纲要

OAT文件

OAT概述

OAT在Android4.4中引入。是优化过的、用于ART虚拟机执行的DEX文件,类似Dalvik的ODEX

ART虚拟机

ART 使用 AOT(Ahead-of-Time)编译技术,在 APK 第一次安装或系统升级、重启时,通过调用 dex2oat 命令将 APK 中的 DEX 文件静态编译成 OAT 并存放到 Android 设备的 /data/dalvik-cache 或 /data/app/package 目录。dex2oat 与 dexopt 不同,dex2oat 更像一个编译器,将 DEX 中的 Dalvik 字节码编译成 Native 机器码。经过这样的操作,以后启动程序时,ART 就会提高 APK 的启动速度,从而执行生成的 OAT 文件而不是 APK 中的 DEX 文件。从 Android 5.0 开始,系统默认将 ART 作为虚拟机,程序运行速度明显变快。但有个缺点,即 OAT 的静态编译操作会影响 APK 的安装效率,导致 Android 4.4 后 APK 安装速度更长。为了使运行和安装速度都更快,Android 7.0 增加了 JIT(just-in-Time)编译。新版本的 Android 使用的是基于 JIT on AOT 的编译技术。

生成OAT文件

系统在安装 APK 时,会调用 dex2oat 自动生成 OAT 文件。也可手动执行 dex2oat 命令,为指定的 DEX 生成 OAT 文件
具体操作:在执行 dex2oat 命令时使用 --dex-file 参数指定传入的 DEX 路径(若有多个 DEX,可多次指定该参数),然后为 --oat-file 参数指定 OAT 的输出路径(在指定传入的路径时,该路径要对当前的 adb shell 用户有可写权限)

文件格式

Android 原生程序以 ELF 格式作为程序格式基础,并在此基础上增加了属于 Android 的应用程序二进制接口(Application Binary Interface, ABI)

arm-linux-androideabi 中包含 ELF 文件格式在 Android 上的参数传递、浮点指针、栈展开、异常处理等规范信息。OAT 文件格式在设计之初就考虑到最终执行的程序是 ELF 格式的这一事实,为避免过多的文件格式设计带来的麻烦,最终将 OAT 文件格式完全融入 Android 所特有的 ELF 格式。

在这里插入图片描述

一个 OAT 文件必须包含 oatdata、oatexec、oatlastword 三个符号

oatdata 符号指向的地址是 OAT 所在 ELF 的 .rodata 段,这里存放的是 OAT 文件头 OATHeader、OAT 的 DEX 文件头 OATDexFile、原始的 DEX 文件 DexFile、OAT 的 DEX 类 OatClass 等信息

oatexec 符号指向的地址是 OAT 所在 ELF 的 .text 段,这里存放的是编译生成的 Native 指令代码

oatlastword 符号指向的地址是 OAT 文件结束处在 ELF 中的文件偏移,通过它可确定 OAT 文件的内容在哪里结束

OATHeader 被定义成了 C++ 的类,可在 Android 源码文件 art/runtime/oat.h 中找到它的定义。

OATHeader

定义:

struct OatHeader {
    uint8_t magic[4];
    uint8_t version[4];
    uint32_t adler32_checksum;

    InstructionSet instruction_set;
    uint32_t instruction_set_features_bitmap;
    uint32_t dex_file_count;    
    uint32_t executable_offset;
    uint32_t interperter_to_interpreter_bridge_offset;
    uint32_t interpreter_to_compiled_code_bridge_offset;
    uint32_t jni_dlsym_lookup_offset;
    uint32_t quick_generic_jni_trampoline_offset;
    uint32_t quick_imt_conflict_trampoline_offset;
    uint32_t quick_resolution_trampoline_offset;
    uint32_t quick_to_interpreter_bridge_offset;

    int32_t image_patch_delta;
    uint32_t image_file_location_oat_checksum;
    uint32_t image_file_location_oat_data_begin;

    uint32_t key_value_store_size;
    uint8_t key_value_store[0];
};

magic:文件的标识,固定为“oat\n”,表示 OAT 的头部
version:OAT 的文件格式版本
adler32_checksum:OAT 的 adler32 校验和
instruction_set:OAT 文件使用的指令集。它的定义位于 art/runtime/arch/instruction_set.h 文件中,可以取如下值(与在 Android NDK 支持的 Native 指令平台上的值一致)。目前在 Android 平台上广泛使用的是 kArm,表示 ARM 指令集
dex_file_count:OAT 中包含的 DEX 文件个数。对普通的 APP,该值通常是 1;对一些特殊的 OAT 文件,如系统的 boot.oat,就会包含多个 DEX 文件

executable_offset:oatexec 符号所在段的开始位置与 oatdata 符号所在段的开始位置的偏移量。该值通常等于 oatdata 符号所在段 .rodata 的大小

jni_dlsym_lookup_offset:在执行过程中,若类方法要调用的另外一个方法是 JNI 函数,则要通过 jni_dlsym_lookup_offset 字段指向的一段代码来调用

OatHeader 下面是 OatDexFile 结构。在 OatHeader 的 dex_file_count 字段中有多少个 DexFile,这里就会连续有多少个 OatDexFile。OatDexFile 结构的声明位于 Android 源码文件 art/runtime/oat_file.h 中

OatDexFile

定义:

struct OatDexFile {
    uint32 dex_filename_size;
    char dex_filename_data[dex_filename_size];
    uint32 dex_file_checksum;
    uint8_t* dex_file_pointer;
    uint32_t* oat_class_offsets;
};

dex_filename_size 字段:指定 dex_filename_data 字段所占字节数,表示的是 OAT 的原 DEX 文件的完整路径

dex_filename_data 字段:存放 DEX 文件的完整路径

dex_file_checksum 字段:存放 DEX 文件的校验和

dex_file_pointer 字段:存放 DexFile 结构体距离当前段的文件偏移

oat_class_offsets 字段:指向 OatClass 的偏移列表

OatDexFile 数组下面是 DexFile 结构体数组,其中存放的是一个个完整的 DEX 文件。

DexFile

struct DexFile {
    // directly-mapped "opt" header
    const DexOptHeader* pOptHeader;

    // pointers to directly-mapped structs
    // and arrays in base DEX
    const DexHeader* pHeader;
    const DexStringId* pStringIds;
    const DexTypeId* pTypeIds;
    const DexFieldId* pFieldIds;
    const DexMethodId* pMethodIds;
    const DexProtoId* pProtoIds;
    const DexClassDef* pClassDefs;
    const DexLink* pLinkData;

    // These are mapped out of the "auxillary"
    // section, and may not be included in the file.
    const DexClassLookup* pClassLookup;
    const void* pRegisterMapPool;       // RegisterMapClassPool

    // points to start of DEX file data
    const u1* baseAddr;

    // track memory overhead for auxillary strcutures
    int overhead;

    // additional app-specific data structures associated with the DEX
    //void* auxData;
};

总结

image-20220725193056654

参考

https://blog.csdn.net/zlmm741/article/details/104774994