首页
社区
课程
招聘
[原创]Dex动态加载的C语言部分
发表于: 2015-1-27 17:00 23781

[原创]Dex动态加载的C语言部分

2015-1-27 17:00
23781

今天主要来分析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];
};

其实很简单,都是一些最基本的数据类型,也不知道这些代码代码什么意思,黑灯瞎火的,继续向下走吧,
现在来看下rewriteDex这个函数干了些什么,
static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
    DexClassLookup** ppClassLookup, DvmDex** ppDvmDex)
{
    DexClassLookup* pClassLookup = NULL;
    u8 prepWhen, loadWhen, verifyOptWhen;
    DvmDex* pDvmDex = NULL;
    bool result = false;
    const char* msgStr = "???";

    if (dexSwapAndVerify(addr, len) != 0)
        goto bail;

    if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
        LOGE("Unable to create DexFile");
        goto bail;
    }

    pClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
    if (pClassLookup == NULL)
        goto bail;
    pDvmDex->pDexFile->pClassLookup = pClassLookup;
    //doVerify和doOpt的值都为false,所以直接跳转到bail,于是这个函数就又执行完了,下面再回溯到前面为执行完的代码
    if (!doVerify && !doOpt) {
        result = true;
        goto bail;
    }

    prepWhen = dvmGetRelativeTimeUsec();

    if (!loadAllClasses(pDvmDex))
        goto bail;
    loadWhen = dvmGetRelativeTimeUsec();

    if (!dvmCreateInlineSubsTable())
        goto bail;

    verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt);
    verifyOptWhen = dvmGetRelativeTimeUsec();

    if (doVerify && doOpt)
        msgStr = "verify+opt";
    else if (doVerify)
        msgStr = "verify";
    else if (doOpt)
        msgStr = "opt";
    LOGD("DexOpt: load %dms, %s %dms",
        (int) (loadWhen - prepWhen) / 1000,
        msgStr,
        (int) (verifyOptWhen - loadWhen) / 1000);

    result = true;

bail:
    if (pDvmDex != NULL) {
        pDvmDex->pDexFile->pClassLookup = NULL;
    }
    if (ppDvmDex == NULL || !result) {
        dvmDexFileFree(pDvmDex);
    } else {
        *ppDvmDex = pDvmDex;
    }

    if (ppClassLookup == NULL || !result) {
        free(pClassLookup);
    } else {
        *ppClassLookup = pClassLookup;
    }

    return result;
}

一看可能先吓了一跳,怎么这个函数一下子多了那么多代码,有点不敢看了,其实我想说这是好事啊,这说明你离成功已经不远啦,战士们,Come On吧,

dexSwapAndVerify这个函数就不详细讲了,有兴趣的可以自己详细看,主要是用来交换Dex文件的的字节码的顺序的,这个和你使用的系统处理器是大端的还是小端的有密切
的关系;
接下来看dvmDexFileOpenPartial(addr, len, &pDvmDex)这个函数:
int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
{
    DvmDex* pDvmDex;
    DexFile* pDexFile;
    int parseFlags = kDexParseDefault;
    int result = -1;

    pDexFile = dexFileParse((u1*)addr, len, parseFlags);
    if (pDexFile == NULL) {
        LOGE("DEX parse failed");
        goto bail;
    }
    pDvmDex = allocateAuxStructures(pDexFile);
    if (pDvmDex == NULL) {
        dexFileFree(pDexFile);
        goto bail;
    }

    pDvmDex->isMappedReadOnly = false;
    *ppDvmDex = pDvmDex;
    result = 0;

bail:
    return result;
}
这里可以看到新建了一个DexFile类型的对象,这个主要是用来描述Dex的整个文件的信息,上面已经介绍过了,

接下来看其中的pDexFile = dexFileParse((u1*)addr, len, parseFlags)这个函数,
(这里先说明吓,parseFlags这个参数的值是kDexParseDefault赋予的,kDexParseDefault是一个枚举变量,它的值为0,所以parseFlags的值为0;)
DexFile* dexFileParse(const u1* data, size_t length, int flags)
{
    DexFile* pDexFile = NULL;
    const DexHeader* pHeader;
    const u1* magic;
    int result = -1;

    //判断dex文件得长度是不是大于dex头部得长度,如果比头部的长度都小,那就什么都可以免谈了,好吗!!!
    if (length < sizeof(DexHeader)) {
        LOGE("too short to be a valid .dex");
        goto bail;      /* bad file format */
    }
    //如果dex文件长度是合法的,那么可以给这个还未初始化的pDexFile对象进行全0的初始化了
    pDexFile = (DexFile*) malloc(sizeof(DexFile));
    if (pDexFile == NULL)
        goto bail;      /* alloc failure */
    memset(pDexFile, 0, sizeof(DexFile));

    //这里主要是用来判断是否这个dex文件是一个优化过的dex文件,如果是优化过的dex文件,那么就执行里面的操作,如果是未优化过的,直接可以跳过这一整个
    //if操作了,为了简单起见,暂时就先跳过这个处理dex opt头部的操作吧
      if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
        magic = data;
        if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
            LOGE("bad opt version (0x%02x %02x %02x %02x)",
                 magic[4], magic[5], magic[6], magic[7]);
            goto bail;
        }

        pDexFile->pOptHeader = (const DexOptHeader*) data;
        LOGV("Good opt header, DEX offset is %d, flags=0x%02x",
            pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);

        /* parse the optimized dex file tables */
        if (!dexParseOptData(data, length, pDexFile))
            goto bail;

        /* ignore the opt header and appended data from here on out */
        data += pDexFile->pOptHeader->dexOffset;
        length -= pDexFile->pOptHeader->dexOffset;
        if (pDexFile->pOptHeader->dexLength > length) {
            LOGE("File truncated? stored len=%d, rem len=%d",
                pDexFile->pOptHeader->dexLength, (int) length);
            goto bail;
        }
        length = pDexFile->pOptHeader->dexLength;
    }
    //好了,刚才我们已经跳过了这个处理dex opt头部的操作,下面我们就继续战斗,离结束不远啦,坚持住阿!!!
    //下面将来看dexFileSetupBasicPointers(pDexFile,data)这个函数,相信有意识的都应该此刻开始兴奋了,因为这就是关键点了!详细分析请看下面
    dexFileSetupBasicPointers(pDexFile, data);
    pHeader = pDexFile->pHeader;

    //再一次判断头部的magic的8个字节是否是合法的
    if (!dexHasValidMagic(pHeader)) {
        goto bail;
    }

     //这里flags为0,不用再纠结了,跳过吧
     if (flags & kDexParseVerifyChecksum) {
        u4 adler = dexComputeChecksum(pHeader);
        if (adler != pHeader->checksum) {
            LOGE("ERROR: bad checksum (%08x vs %08x)",
                adler, pHeader->checksum);
            if (!(flags & kDexParseContinueOnError))
                goto bail;
        } else {
            LOGV("+++ adler32 checksum (%08x) verified", adler);
        }

        const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
        if (pOptHeader != NULL) {
            adler = dexComputeOptChecksum(pOptHeader);
            if (adler != pOptHeader->checksum) {
                LOGE("ERROR: bad opt checksum (%08x vs %08x)",
                    adler, pOptHeader->checksum);
                if (!(flags & kDexParseContinueOnError))
                    goto bail;
            } else {
                LOGV("+++ adler32 opt checksum (%08x) verified", adler);
            }
        }
    }

    //这里kVerifySignature为0,不用纠结了,跳过吧
    if (kVerifySignature) {
        unsigned char sha1Digest[kSHA1DigestLen];
        const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +
                            kSHA1DigestLen;

        dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);
        if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {
            char tmpBuf1[kSHA1DigestOutputLen];
            char tmpBuf2[kSHA1DigestOutputLen];
            LOGE("ERROR: bad SHA1 digest (%s vs %s)",
                dexSHA1DigestToStr(sha1Digest, tmpBuf1),
                dexSHA1DigestToStr(pHeader->signature, tmpBuf2));
            if (!(flags & kDexParseContinueOnError))
                goto bail;
        } else {
            LOGV("+++ sha1 digest verified");
        }
    }
    //继续判断dex文件的大小是否合法
    if (pHeader->fileSize != length) {
        LOGE("ERROR: stored file size (%d) != expected (%d)",
            (int) pHeader->fileSize, (int) length);
        if (!(flags & kDexParseContinueOnError))
            goto bail;
    }
    //判断dex文件中累定义的个数
    if (pHeader->classDefsSize == 0) {
        LOGE("ERROR: DEX file has no classes in it, failing");
        goto bail;
    }

    /*
     * Success!
     */
    result = 0;

bail:
    if (result != 0 && pDexFile != NULL) {
        dexFileFree(pDexFile);
        pDexFile = NULL;
    }
    return pDexFile;
}

***********************************************************************************************************************************
承接上部分对dexFileSetupBasicPointers(pDexFile, data)的分析:
void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data) {
    DexHeader *pHeader = (DexHeader*) data;

    pDexFile->baseAddr = data;
    pDexFile->pHeader = pHeader;
    pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff);
    pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);
    pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff);
    pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->methodIdsOff);
    pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);
    pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff);
    pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff);
}
可以看到如此简单的几条语句,难道就是核心部分嘛,是的,可以很开心地告诉你,这是非常核心的部分:
Dex总共可以分八个部分,Header,StringID,TypeID,FieldID,MethodID,ProtoID,ClassDefID,CODE,这八个部分本来就是连在一块的大肉饼,
千里迢迢地从java层一直跑到C++层,但是从这一刻起,就在调用完dexFileSetupBasicPointers函数地这一刻起,Dex的整个结构一下子被“大卸八块”了,完全充斥着
pDexFile这个结构体了,也可以很光荣地告诉你要是再想访问Dex中地任何内容,你只需访问pDexFile这个结构体对象即可!!!,好了,我们继续回到上面的下一条语句开始
分析,不要让兴奋冲昏了头脑,毕竟这还没有大获全胜阿,加油,Comn on!!!
***********************************************************************************************************************************

分析完了这个dexFileParse((u1*)addr, len, parseFlags)函数过后,我们可以返回上一层的dvmDexFileOpenPartial这个函数的下一条语句了,
从这里开始:

if (pDexFile == NULL) {
        LOGE("DEX parse failed");
        goto bail;
    }
    pDvmDex = allocateAuxStructures(pDexFile);
    if (pDvmDex == NULL) {
        dexFileFree(pDexFile);
        goto bail;
    }

    pDvmDex->isMappedReadOnly = false;
    *ppDvmDex = pDvmDex;
    result = 0;

bail:
    return result;
}

好了,这里我们又要分析一个关键的函数了,刚才把pDexFile喂饱了,那么干脆好人做到底啦,是不是应该要把pDvmDex这个对象也要喂饱呢??
我们这就来分析allocateAuxStructures(pDexFile)这个函数吧,
static DvmDex* allocateAuxStructures(DexFile* pDexFile)
{
    //前三行初始化对象,不用多说了,就等着待会儿被赋值吧
    DvmDex* pDvmDex;
    const DexHeader* pHeader;
    u4 stringCount, classCount, methodCount, fieldCount;

    //给pDvmDex这个对象分配内存,赋值为全1
    pDvmDex = (DvmDex*) calloc(1, sizeof(DvmDex));
    if (pDvmDex == NULL)
        return NULL;
   
    //这两句语开始给pDvmDex这个结构体中的变量赋值
    pDvmDex->pDexFile = pDexFile;
    pDvmDex->pHeader = pDexFile->pHeader;

    //这五条语句主要是给这个函数的5个局部变量赋值
    pHeader = pDvmDex->pHeader;

    stringCount = pHeader->stringIdsSize;
    classCount = pHeader->typeIdsSize;
    methodCount = pHeader->methodIdsSize;
    fieldCount = pHeader->fieldIdsSize;

    //下面这四条语句给pDvmDex这个对象中的关键的四个字段进行分配空间
    pDvmDex->pResStrings = (struct StringObject**)
        calloc(stringCount, sizeof(struct StringObject*));

    pDvmDex->pResClasses = (struct ClassObject**)
        calloc(classCount, sizeof(struct ClassObject*));

    pDvmDex->pResMethods = (struct Method**)
        calloc(methodCount, sizeof(struct Method*));

    pDvmDex->pResFields = (struct Field**)
        calloc(fieldCount, sizeof(struct Field*));

    LOGV("+++ DEX %p: allocateAux %d+%d+%d+%d * 4 = %d bytes",
        pDvmDex, stringCount, classCount, methodCount, fieldCount,
        (stringCount + classCount + methodCount + fieldCount) * 4);
    //这条语句其实也是一样的,也是给pDvmDex中的pInterfaceCache对象分配空间,以及赋值而已
    pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);

    if (pDvmDex->pResStrings == NULL ||
        pDvmDex->pResClasses == NULL ||
        pDvmDex->pResMethods == NULL ||
        pDvmDex->pResFields == NULL ||
        pDvmDex->pInterfaceCache == NULL)
    {
        LOGE("Alloc failure in allocateAuxStructures");
        free(pDvmDex->pResStrings);
        free(pDvmDex->pResClasses);
        free(pDvmDex->pResMethods);
        free(pDvmDex->pResFields);
        free(pDvmDex);
        return NULL;
    }

    return pDvmDex;

}

看到这里,你觉得pDvmDex这个对象被喂饱了吗,有没有喂饱就决定了你到底对这个源代码理解了多少,我们继续向下:
刚才dexFileParse((u1*)addr, len, parseFlags) 剩下的部分是这些:

if (pDvmDex == NULL) {
        dexFileFree(pDexFile);
        goto bail;
    }

    pDvmDex->isMappedReadOnly = false;
    *ppDvmDex = pDvmDex;
    result = 0;

bail:
    return result;
}

显然可以明白是什么意思,不用多说了,那么我们要返回上上个函数没执行完多部分了,就是rewriteDex这个函数,
我们还是从这个函数的上次没执行玩的第一条语句开始吧:
//回顾下,pClassLoopup是DexClassLookup类型的对象,这个对象的主要是用来后续方便查找和加载类的,见下面详细分析dexCreateClassLookup这个函数:

pClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
    if (pClassLookup == NULL)
        goto bail;
    pDvmDex->pDexFile->pClassLookup = pClassLookup;
    //这里的doVeriry和doOpt的值为false,所以可以直接跳进入bail了
    if (!doVerify && !doOpt) {
        result = true;
        goto bail;
    }

    prepWhen = dvmGetRelativeTimeUsec();

    if (!loadAllClasses(pDvmDex))
        goto bail;
    loadWhen = dvmGetRelativeTimeUsec();

    if (!dvmCreateInlineSubsTable())
        goto bail;

    verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt);
    verifyOptWhen = dvmGetRelativeTimeUsec();

    if (doVerify && doOpt)
        msgStr = "verify+opt";
    else if (doVerify)
        msgStr = "verify";
    else if (doOpt)
        msgStr = "opt";
    LOGD("DexOpt: load %dms, %s %dms",
        (int) (loadWhen - prepWhen) / 1000,
        msgStr,
        (int) (verifyOptWhen - loadWhen) / 1000);

    result = true;

bail:
    if (pDvmDex != NULL) {
        pDvmDex->pDexFile->pClassLookup = NULL;
    }

    if (ppDvmDex == NULL || !result) {
        dvmDexFileFree(pDvmDex);
    } else {
        *ppDvmDex = pDvmDex;
    }

    if (ppClassLookup == NULL || !result) {
        free(pClassLookup);
    } else {
        *ppClassLookup = pClassLookup;
    }

    return result;
}

**************************************************************
DexClassLookup* dexCreateClassLookup(DexFile* pDexFile)
{
    DexClassLookup* pLookup;
    int allocSize;
    int i, numEntries;
    int numProbes, totalProbes, maxProbes;

    numProbes = totalProbes = maxProbes = 0;

    assert(pDexFile != NULL);
    //dexRoundUpPower2的函数的作用是把一个整数向上转化为一个2的幂,比如:1就是1,2就是2,3就是4,5就是8等等......
    numEntries = dexRoundUpPower2(pDexFile->pHeader->classDefsSize * 2);
    //整个LoopUp表的大小,这里可以看到,有多少类,就有多大的空间,当然numEnties已经在现有的类的个数上翻了一番
    allocSize = offsetof(DexClassLookup, table)
                    + numEntries * sizeof(pLookup->table[0]);
    //为pLookup这个对象分配存储空间
    pLookup = (DexClassLookup*) calloc(1, allocSize);
    if (pLookup == NULL)
        return NULL;
    //给pLookup结构体内的字段size,numEntries赋值
    pLookup->size = allocSize;
    pLookup->numEntries = numEntries;
    //下面开始给pLookup这个对象中的table字段赋值,循环的对把各个类的信息加入到pLookup这张表中
    for (i = 0; i < (int)pDexFile->pHeader->classDefsSize; i++) {
        const DexClassDef* pClassDef;
        const char* pString;
        //通过索引i来获得一个DexClassDef类型的对象
        pClassDef = dexGetClassDef(pDexFile, i);
        //通过类中的类型索引来找到类类型,返回一个字符串
        pString = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
        //classLookupAdd函数就是把第一个类的加入到pLookup这张表中
        //下面先来分析下这个函数,详细分析见下面
        classLookupAdd(pDexFile, pLookup,
            (u1*)pString - pDexFile->baseAddr,
            (u1*)pClassDef - pDexFile->baseAddr, &numProbes);

        if (numProbes > maxProbes)
            maxProbes = numProbes;
        totalProbes += numProbes;
    }

    LOGV("Class lookup: classes=%d slots=%d (%d%% occ) alloc=%d"
         " total=%d max=%d",
        pDexFile->pHeader->classDefsSize, numEntries,
        (100 * pDexFile->pHeader->classDefsSize) / numEntries,
        allocSize, totalProbes, maxProbes);

    return pLookup;
}
//好了,到这里为止,dexCreateClassLookup这个函数建立pLookup这个表的过程算是结束了,我们开始回溯到上面的未完成的代码中
**************************************************************
//下面来解释classLoopupAdd这个函数
static void classLookupAdd(DexFile* pDexFile, DexClassLookup* pLookup,
    int stringOff, int classDefOff, int* pNumProbes)
{
    //得到类描述的一个字符串,其实就是类的类型
    const char* classDescriptor =
        (const char*) (pDexFile->baseAddr + stringOff);
    //得到类的一个类定义对象
    const DexClassDef* pClassDef =
        (const DexClassDef*) (pDexFile->baseAddr + classDefOff);
    //将类描述符通过这个hash函数进行hash,返回一个无符号整形的hash值
    u4 hash = classDescriptorHash(classDescriptor);
    int mask = pLookup->numEntries-1;
    int idx = hash & mask;

    int probes = 0;
    //这里就开始查找pLookup这张表中空余的位置,来放置上面得到的类的信息
    while (pLookup->table[idx].classDescriptorOffset != 0) {
        idx = (idx + 1) & mask;
        probes++;
    }

    pLookup->table[idx].classDescriptorHash = hash;
    pLookup->table[idx].classDescriptorOffset = stringOff;
    pLookup->table[idx].classDefOffset = classDefOff;
    *pNumProbes = probes;
}
//好了,到这里为止,已经把第一个类的信息放在了pLookup这张表中了,我们继续返回上面的代码
***************************************************************

来来回回地终于把内存加载字节码的功能分析完了,中间可能漏了很多东西没有细讲,还有一些可能讲的不对,还望大牛多多批评指教,第一次分析源码,写的有点乱,
请看不懂的朋友随时联系我,我一定会改正的;
希望通过这个过程能够帮助有需要的人,也是对我自己的一次磨练!谢谢
请各位大牛批评指教!!!


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 3
支持
分享
最新回复 (9)
雪    币: 6976
活跃值: (1482)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
2
可惜了.. 如果搞清楚了这个, 看雪安全挑战赛, 第3 4题肯定没问题.
2015-1-27 17:18
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
不错 先mark一下 清醒了再看
2015-1-27 17:19
0
雪    币: 188
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
看下,学习下,貌似要火。
2015-1-27 17:46
0
雪    币: 18
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
学习了...
2015-1-27 19:32
0
雪    币: 10
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
啥意思?
2015-7-22 17:57
0
雪    币: 7818
活跃值: (1073)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
7
请问,openDexFile这个函数是什么时候调用的?
2015-11-15 14:38
0
雪    币: 47
活跃值: (43)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
动态加载dex文件的时候调用的
2015-11-23 17:42
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
请问是否对android5.0 内存动态加载有研究?
2015-12-29 09:10
0
雪    币: 47
活跃值: (43)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
还没去研究过5.0的,哈哈
2016-1-13 11:51
0
游客
登录 | 注册 方可回帖
返回
//