Android安全–从defineClassNative看类的加载过程

前面一篇文章中讲到Dex的加载过程,其中涉及到的结构体有:

struct DexOrJar {
    char*           fileName;
    bool            isDex;
    bool            okayToFree;
    RawDexFile*     pRawDexFile;
    JarFile*        pJarFile;
    u1*             pDexMemory; // malloc()ed memory, if any
};

如果是dex文件就会调用dvmRawDexFileOpen填充RawDexFile结构,如果是jar文件就会调用dvmJarFileOpen填充JarFile。结构如下:

struct RawDexFile {
    char*           cacheFileName;
    DvmDex*         pDvmDex;
};

struct JarFile {
    ZipArchive      archive;
    char*           cacheFileName;
    DvmDex*         pDvmDex;
};

其中最重要的就是pDvmDex这个结构体。

struct DvmDex {
    DexFile*                    pDexFile;            //odex的信息
    const DexHeader*           pHeader;             //dex文件头相关信息
    struct StringObject**     pResStrings;         //字符串
    struct ClassObject**     pResClasses;         //通过DexFile里的ClassDefItem构造出来的一个结构体(类信息)
    struct Method**         pResMethods;         //通过Method_Item构造出来的结构体(方法信息)
    struct Field**          pResFields;
    struct AtomicCache*     pInterfaceCache;
    bool                    isMappedReadOnly;
    MemMapping                     memMap;
    jobject             dex_object;
    pthread_mutex_t         modLock;
};

DexFile结构体存储了odex文件的一些信息。

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;
    const DexClassLookup*         pClassLookup;
    const void*                 pRegisterMapPool;       // RegisterMapClassPool
    const u1*                   baseAddr;
    int                         overhead;
};

其中DexClassLookup结构体是用来方便快速查找某个类的一个hash表。

struct DexClassLookup {
    int     size;                       // total size, including “size”
    int     numEntries;                 // size of table[]; always power of 2
    struct {
        u4      classDescriptorHash;    // class descriptor hash code
        int     classDescriptorOffset;  // in bytes, from start of DEX
        int     classDefOffset;         // in bytes, from start of DEX
    } table[1];
};

关于DexClassLookup结构 可以参考:

http://blog.csdn.net/roland_sun/article/details/46877563

这篇文章。

然后再来看看Dalvik_dalvik_system_DexFile_defineClassNative方法是怎么找到类信息的。

代码地址:http://androidxref.com/4.4_r1/xref/dalvik/vm/native/dalvik_system_DexFile.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
static void Dalvik_dalvik_system_DexFile_defineClassNative(const u4* args,
    JValue* pResult)
{
    StringObject* nameObj = (StringObject*) args[0];
    Object* loader = (Object*) args[1];
    int cookie = args[2];
    ClassObject* clazz = NULL;
    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
    DvmDex* pDvmDex;
    char* name;
    char* descriptor;

    name = dvmCreateCstrFromString(nameObj);
    //把com.monkey.test 转成Lcom/monkey/test;
    descriptor = dvmDotToDescriptor(name);
    ALOGV("--- Explicit class load '%s' l=%p c=0x%08x",
        descriptor, loader, cookie);
    free(name);

    if (!validateCookie(cookie))
        RETURN_VOID();

    //获取pDvmDex
    if (pDexOrJar->isDex)
        pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
    else
        pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);

    /* once we load something, we can't unmap the storage */
    pDexOrJar->okayToFree = false;
   
    //获取ClassObject
    clazz = dvmDefineClass(pDvmDex, descriptor, loader);
    Thread* self = dvmThreadSelf();
    if (dvmCheckException(self)) {
        /*
         * If we threw a "class not found" exception, stifle it, since the
         * contract in the higher method says we simply return null if
         * the class is not found.
         */

        Object* excep = dvmGetException(self);
        if (strcmp(excep->clazz->descriptor,
                   "Ljava/lang/ClassNotFoundException;") == 0 ||
            strcmp(excep->clazz->descriptor,
                   "Ljava/lang/NoClassDefFoundError;") == 0)
        {
            dvmClearException(self);
        }
        clazz = NULL;
    }

    free(descriptor);
    RETURN_PTR(clazz);
}

然后看里面的dvmDefineClass方法:

1
2
3
4
5
6
7
ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
    Object* classLoader)
{
    assert(pDvmDex != NULL);

    return findClassNoInit(descriptor, classLoader, pDvmDex);
}

这里调用了findClassNoInit函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
    DvmDex* pDvmDex)
{
    Thread* self = dvmThreadSelf();
    ClassObject* clazz;
    ..............
    clazz = dvmLookupClass(descriptor, loader, true);  //判断当前的类是否已经加载
    if (clazz == NULL) {
        const DexClassDef* pClassDef;
        if (pDvmDex == NULL) {
            assert(loader == NULL);     /* shouldn't be here otherwise */
            pDvmDex = searchBootPathForClass(descriptor, &pClassDef);//如果pDvmDex为NULL,从系统缺省的库目录里加载
        } else {
            pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor); //从dex文件中找到目标类的classDefItem数据
        }

    ..............
        /* found a match, try to load it */
        clazz = loadClassFromDex(pDvmDex, pClassDef, loader);  //通过dex文件对象,目标类的ClassDefItem,classloader来加载目标类到内存中,最后会构造一个ClassObject结构
    ..............
        dvmLockObject(self, (Object*) clazz);
        if (!dvmAddClassToHash(clazz)) {
            //加到hash_table的时候出错的一些处理
        }

        }
}

struct ClassObject : Object {

    u4                          instanceData[CLASS_FIELD_SLOTS];
    const char*             descriptor;
    char*                   descriptorAlloc;
    u4                          accessFlags;
    u4                          serialNumber;
    DvmDex*                     pDvmDex;
    ClassStatus             status;
    ClassObject*            verifyErrorClass;
    u4                          initThreadId;
    size_t                  objectSize;
    ClassObject*            elementClass;
    int                     arrayDim;
    PrimitiveType           primitiveType;
    ClassObject*            super;
    Object*                 classLoader;
    InitiatingLoaderList    initiatingLoaderList;
    int                     interfaceCount;
    ClassObject**           interfaces;
    int                     directMethodCount;
    Method*                     directMethods;
    int                     virtualMethodCount;
    Method*                     virtualMethods;
    int                     vtableCount;
    Method**                    vtable;
    int                     iftableCount;
    InterfaceEntry*         iftable;
    int                     ifviPoolCount;
    int*                    ifviPool;
    int                     ifieldCount;
    int                     ifieldRefCount;
    InstField*              ifields;
    u4              refOffsets;
    const char*             sourceFile;
    int                     sfieldCount;
    StaticField             sfields[0];
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static ClassObject* loadClassFromDex(DvmDex* pDvmDex,
    const DexClassDef* pClassDef, Object* classLoader)
{
    ClassObject* result;
    DexClassDataHeader header;
    const u1* pEncodedData;
    const DexFile* pDexFile;

    assert((pDvmDex != NULL) && (pClassDef != NULL));
    pDexFile = pDvmDex->pDexFile;

    if (gDvm.verboseClass) {
        ALOGV("CLASS: loading '%s'...",
            dexGetClassDescriptor(pDexFile, pClassDef));
    }

    pEncodedData = dexGetClassData(pDexFile, pClassDef);  //得到classDefItem里的ClassData

    if (pEncodedData != NULL) {
        dexReadClassDataHeader(&pEncodedData, &header);  //读取classDefItem的头信息
    } else {
        // Provide an all-zeroes header for the rest of the loading.
        memset(&header, 0, sizeof(header));
    }

    result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData,
            classLoader);                                 //根据一系列类的数据,对ClassObject这个结构体的字段进行填充处理

    if (gDvm.verboseClass && (result != NULL)) {
        ALOGI("[Loaded %s from DEX %p (cl=%p)]",
            result->descriptor, pDvmDex, classLoader);
    }

    return result;
}

最后调用loadClassFromDex0填充ClassObject结构。

 

类加载完了,还有类的方法的加载:

上面的loadClassFromDex0函数里面会调用dexReadClassDataMethod来读取Method信息,

DEX_INLINE void dexReadClassDataMethod(const u1** pData, DexMethod* pMethod,
        u4* lastIndex) {
    u4 index = *lastIndex + readUnsignedLeb128(pData);

    pMethod->accessFlags = readUnsignedLeb128(pData);
    pMethod->codeOff = readUnsignedLeb128(pData);
    pMethod->methodIdx = index;
    *lastIndex = index;
}

然后通过函数

static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod, Method* meth)来根据methodid找到method信息。

pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);

meth->name = dexStringById(pDexFile, pMethodId->nameIdx);
dexProtoSetFromMethodId(&meth->prototype, pDexFile, pMethodId);
meth->shorty = dexProtoGetShorty(&meth->prototype);
meth->accessFlags = pDexMethod->accessFlags;
meth->clazz = clazz;
meth->jniArgInfo = 0;
meth->registersSize = pDexCode->registersSize;
meth->insSize = pDexCode->insSize;
meth->outsSize = pDexCode->outsSize;
meth->insns = pDexCode->insns;
if (dvmIsNativeMethod(meth)) {
    meth->nativeFunc = dvmResolveNativeMethod;
    meth->jniArgInfo = computeJniArgInfo(&meth->prototype);
}

本文链接:http://www.alonemonkey.com/defineclassnative.html

一条评论

Comments are closed.