×

ODEX格式及生成过程

96
difcareer
2016.06.12 18:04* 字数 586

Apk在安装(installer)时,就会进行验证和优化,目的是为了校验代码合法性及优化代码执行速度,参见Dalvik Optimization and Verification Withdexopt

验证和优化后,会产生ODEX文件,运行Apk的时候,直接加载ODEX,避免重复验证和优化,加快了Apk的响应时间。

先来看一张ODEX文件的结构图:


ODEX文件的结构图

下面将围绕这张图,结合dexopt的代码,说明ODEX的生成过程。

一. 首先将一个空的DexOptHeader写入ODEX文件

dexOptCreateEmptyHeader(cacheFd)

二. 从Apk中提取classes.dex,追加到ODEX文件

dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd)

此时文件结构是这样的:


三. 修改Dex内容

rewriteDex(((u1*) mapAddr) + dexOffset, dexLength, doVerify, doOpt, &pClassLookup, NULL)

这一步是验证和优化的核心,具体见:

verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt)
... 
verifyAndOptimizeClass(pDexFile, clazz, pClassDef, doVerify, doOpt)
...
dvmVerifyClass(clazz)
...
dvmOptimizeClass(clazz, false)

其中dvmVerifyClass和dvmOptimizeClass都是针对类中的方法,具体做了哪些事情参考源码。

四. 因为3修改了Dex内容,所以要重新计算Dex的checksum

updateChecksum(dexAddr, dexLength, pHeader)

五. 往ODEX文件后面追加Dependenices内容

writeDependencies(fd, modWhen, crc)

所谓的Dependenicies,是指Dex文件之间的依赖,比如App会依赖于framework的Dex。当framework的Dex发生变化时,App的ODEX文件将失效,需要重新生成。
writeDependencies中将bootclasspath下的dex文件都加入到依赖中。
此时文件结构是这样的:


六. 将优化的其他内容追加到ODEX文件

writeOptData(fd, pClassLookup, pRegMapBuilder)

其中按照不同类型的内容,分为三个chunk写入:

writeChunk(fd, (u4) kDexChunkClassLookup, pClassLookup, pClassLookup->size)
writeChunk(fd, (u4) kDexChunkRegisterMaps,  pRegMapBuilder->data, pRegMapBuilder->size)
writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)

此时的文件结构是这样的:



其中:

dexCreateClassLookup(pDvmDex->pDexFile)

此处根据Dex生成了pClassLookup,pClassLookup你可以简单理解为一张hash表,里面保存了classDescriptor到classDef的映射。

dvmGenerateRegisterMaps(pDvmDex)

此处根据DvmDex生成了RegisterMaps,RegisterMaps的作用是为了标记方法中寄存器引用的对象,在快速GC时,不会释放这些对象。具体参考Dalvik虚拟机中RegisterMap结构解析

七. 根据所有的内容,改写第一步中DexOptHeader的相关字段值。

至此,ODEX文件完整生成。

参考资料:

  1. Dalvik Optimization and Verification Withdexopt
  2. Android系统ODEX文件格式解析
  3. Dalvik虚拟机中RegisterMap结构解析

如果觉得我的文章对你有帮助,想要打赏我,请扫下面的微信(吐槽下简书的打赏提现略坑)


Paste_Image.png
Android
Web note ad 1