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.blogfshare.com/defineclassnative.html

猜你喜欢

转载自blog.csdn.net/justFWD/article/details/50915725