今天主要来分析Dex动态加载C++语言的部分(Android4.0)
内存加载:
native代码:private static int openDexFile(byte[] fileContents) throws IOException
所对应的实现如下:
static void Dalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4* args , JValue* pResult)
{
ArrayObject* fileContentsObj = (ArrayObject*) args[0];
u4 length;
u1* pBytes;
RawDexFile* pRawDexFile;
DexOrJar* pDexOrJar = NULL;
if (fileContentsObj == NULL) {
dvmThrowNullPointerException("fileContents == null");
RETURN_VOID();
}
length = fileContentsObj->length;
pBytes = (u1*) malloc(length);
if (pBytes == NULL) {
dvmThrowRuntimeException("unable to allocate DEX memory");
RETURN_VOID();
}
memcpy(pBytes, fileContentsObj->contents, length);
if (dvmRawDexFileOpenArray(pBytes, length, &pRawDexFile) != 0) {
LOGV("Unable to open in-memory DEX file");
free(pBytes);
dvmThrowRuntimeException("unable to open in-memory DEX file");
RETURN_VOID();
}
LOGV("Opening in-memory DEX");
pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
pDexOrJar->isDex = true;
pDexOrJar->pRawDexFile = pRawDexFile;
pDexOrJar->pDexMemory = pBytes;
pDexOrJar->fileName = strdup("<memory>"); // Needs to be free()able.
addToDexFileTable(pDexOrJar);
RETURN_PTR(pDexOrJar);
} 首先来解释fileContentsObj这个对象的内容,从Java层传过来的byte[]类型的一个对象,当到达c++语言层的时候成了两个参数,args和pResult,然后把args这个数组中 的第一个元素直接转化成了ArrayObject对象,接下来显而易见,把这个元素的内容赋给了pBytes对象,那么这个总体的过程可以理解为一个Java层的byte[]类型的对象 转化为c++语言层的一个byte[]类型的对象; 接下来将是对dvmRawDexFileOpenArray(pBytes, length, &pRawDexFile)这个函数的分析,在分析之前,可以一起来看一下这个函数的三个参数,第一个参数和第二个 参数已经不用说了,就是一个byte[]类型的数组,以及这个数组中内容的长度,其实这个内容就是dex的字节流信息,第三个参数是一个RawDexFile的对象,这个对象的数据 结构是这样的: struct RawDexFile { char* cacheFileName; DvmDex* pDvmDex; }; 这里又看到一个新的数据对象DvmDex,再来看一下这个数据结构: struct DvmDex { DexFile* pDexFile; const DexHeader* pHeader; struct StringObject** pResStrings; struct ClassObject** pResClasses; struct Method** pResMethods; struct Field** pResFields; struct AtomicCache* pInterfaceCache; bool isMappedReadOnly; MemMapping memMap; pthread_mutex_t modLock; }; 这里又看到一个新的数据对象DexFile,再来看一下这个数据结构: struct DexFile { const DexOptHeader* pOptHeader; const DexHeader* pHeader; const DexStringId* pStringIds; const DexTypeId* pTypeIds; const DexFieldId* pFieldIds; const DexMethodId* pMethodIds; const DexProtoId* pProtoIds; const DexClassDef* pClassDefs; const DexLink* pLinkData; const DexClassLookup* pClassLookup; const void* pRegisterMapPool; // RegisterMapClassPool const u1* baseAddr; int overhead; }; 这个数据结构如果了解dex文件格式的话,就对这里的一些对象比较清楚了,其实这个DvmDex数据结构中就保存了Dex文件的各个部分的一些相关内容,具体可以看一下Dex文件 的格式,这里不做多详细的介绍 现在回到那个方法,我们继续往下玩dvmRawDexFileOpenArray这个函数, int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppRawDexFile) { DvmDex* pDvmDex = NULL; if (!dvmPrepareDexInMemory(pBytes, length, &pDvmDex)) { LOGD("Unable to open raw DEX from array"); return -1; } assert(pDvmDex != NULL); *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile)); (*ppRawDexFile)->pDvmDex = pDvmDex; return 0; } 我们可以看到进入这个函数的第一步就是建立了一个DvmDex类型的对象,并初始化为空,经过前面的分析,我们都应该有这个联系,这个pDvmDex对象不就是包含于RawDexFile 这个数据结构中的吗,再看最后第三行,(*ppRawDexFile)->pDvmDex = pDvmDex;,不用多解释,大家明白了这个赋值操作的含义,现在就来看看这个函数的中间部分到底 做了什么呢,还不是dvmPrepareDexInMemory(pBytes, length, &pDvmDex)这个东西惹的祸吗,我们继续进入其中作战, bool dvmPrepareDexInMemory(u1* addr, size_t len, DvmDex** ppDvmDex) { DexClassLookup* pClassLookup = NULL; if (!rewriteDex(addr, len, false, false, &pClassLookup, ppDvmDex)) { return false; } (*ppDvmDex)->pDexFile->pClassLookup = pClassLookup; return true; } 先来看下DexClassLookup这个数据结构长啥模样吧, struct DexClassLookup { int size; int numEntries; struct { u4 classDescriptorHash; int classDescriptorOffset; int classDefOffset; } table[1]; }; 其实很简单&#