博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DexClassLoader和PathClassLoader类载入机制
阅读量:5141 次
发布时间:2019-06-13

本文共 13093 字,大约阅读时间需要 43 分钟。

    0x00

    在一文中,我们分析了dex文件怎样形成了DexFile结构体。本文中解说类载入机制,实际上就是生成ClassObject对象。

    我们以DexClassLoader为例。解说类载入机制,PathClassLoader是一样的。

    我们在载入类时一般会调用loadClass,那么我们就从loadClass来開始分析。

    

    0x01

    DexClassLoader类没有loadClass方法。所以调用的是父类ClassLoader类的loadClass方法,ClassLoader类的loadClass方法位于libcore\luni\src\main\java\java\lang\ClassLoader.java中。

protected Class
loadClass(String className, boolean resolve) throws ClassNotFoundException { Class
clazz = findLoadedClass(className); if (clazz == null) { try { clazz = parent.loadClass(className, false); } catch (ClassNotFoundException e) { // Don't want to see this. } if (clazz == null) { clazz = findClass(className); } } return clazz; }
    DexClassLoader复写了父类ClassLoader的findClass方法。所以调用子类DexClassLoader类的方法findClass。代码位于libcore\dalvik\src\main\java\dalvik\system\DexClassLoader.java。
@Override    protected Class
findClass(String name) throws ClassNotFoundException { if (VERBOSE_DEBUG) System.out.println("DexClassLoader " + this + ": findClass '" + name + "'"); int length = mFiles.length; for (int i = 0; i < length; i++) { if (VERBOSE_DEBUG) System.out.println(" Now searching: " + mFiles[i].getPath()); if (mDexs[i] != null) { String slashName = name.replace('.', '/'); Class clazz = mDexs[i].loadClass(slashName, this); if (clazz != null) { if (VERBOSE_DEBUG) System.out.println(" found"); return clazz; } } } throw new ClassNotFoundException(name + " in loader " + this); }
    这里调用的是DexFile类的loadClass方法,代码位于libcore\dalvik\src\main\java\dalvik\system\DexFile.java。

public Class loadClass(String name, ClassLoader loader) {        String slashName = name.replace('.', '/');        return loadClassBinaryName(slashName, loader);    }
public Class loadClassBinaryName(String name, ClassLoader loader) {        return defineClass(name, loader, mCookie,            null);            //new ProtectionDomain(name) /*DEBUG ONLY*/);    }
    defineClass相应的是JNI方法,例如以下:

native private static Class defineClass(String name, ClassLoader loader,        int cookie, ProtectionDomain pd);
    还记得
一文中,openDexFile也是JNI方法。
相应的native方法位于dalvik\vm\native\dalvik_system_DexFile.c。

const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {    { "openDexFile",        "(Ljava/lang/String;Ljava/lang/String;I)I",        Dalvik_dalvik_system_DexFile_openDexFile },    { "closeDexFile",       "(I)V",        Dalvik_dalvik_system_DexFile_closeDexFile },    { "defineClass",        "(Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/security/ProtectionDomain;)Ljava/lang/Class;",        Dalvik_dalvik_system_DexFile_defineClass },    { "getClassNameList",   "(I)[Ljava/lang/String;",        Dalvik_dalvik_system_DexFile_getClassNameList },    { "isDexOptNeeded",     "(Ljava/lang/String;)Z",        Dalvik_dalvik_system_DexFile_isDexOptNeeded },    { NULL, NULL, NULL },};

    defineClass相应的是Dalvik_dalvik_system_DexFile_defineClass方法。注意defineClass函数传递进来的參数有一个是mCookie,就是在一文中。openDexFile生成的,利用这个mCookie能够在native层找到openDexFile生成的DexFile结构体。

    0x02

    Dalvik_dalvik_system_DexFile_defineClass代码位于dalvik\vm\native\dalvik_system_DexFile.c

static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args,    JValue* pResult){    StringObject* nameObj = (StringObject*) args[0];    Object* loader = (Object*) args[1];    int cookie = args[2];    Object* pd = (Object*) args[3];    ClassObject* clazz = NULL;    DexOrJar* pDexOrJar = (DexOrJar*) cookie;    DvmDex* pDvmDex;    char* name;    char* descriptor;    name = dvmCreateCstrFromString(nameObj);    descriptor = dvmDotToDescriptor(name);    LOGV("--- Explicit class load '%s' 0x%08x\n", descriptor, cookie);    free(name);    if (!validateCookie(cookie))        RETURN_VOID();    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;    clazz = dvmDefineClass(pDvmDex, descriptor, loader);    ......    ......    free(descriptor);    RETURN_PTR(clazz);}
    首先通过cookie找到DexOrJar结构体pDexOrJar,然后依据pDexOrJar找到DvmDex结构体pDvmDex。

    以下我们来分析核心函数dvmDefineClass,这个用来生成ClassObject。dvmDefineClass。findClassNoInit 方法都位于dalvik\vm\oo\Class.c。

ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,    Object* classLoader){    assert(pDvmDex != NULL);    return findClassNoInit(descriptor, classLoader, pDvmDex);}
static ClassObject* findClassNoInit(const char* descriptor, Object* loader,    DvmDex* pDvmDex){    Thread* self = dvmThreadSelf();    ClassObject* clazz;    bool profilerNotified = false;    ......    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);        } else {            pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);        }        ......        /* found a match, try to load it */        clazz = loadClassFromDex(pDvmDex, pClassDef, loader);        ......        if (!dvmAddClassToHash(clazz)) {            ......        }		......	}    return clazz;}
    首先调用dvmLookupClass方法,依据目标类的描写叙述符descriptor在系统已载入类中进行查找,如果已对其载入,则返回目标类的ClassObject对象;否则,将对目标类进行载入。

    我们如果没有对其载入过,然后调用dexFindClass方法找到DexClassDef结构体。我们首先来看下DexClassDef结构体,代码位于dalvik\vm\oo\Class.c。

typedef struct DexClassDef {    u4  classIdx;           /* index into typeIds for this class */    u4  accessFlags;    u4  superclassIdx;      /* index into typeIds for superclass */    u4  interfacesOff;      /* file offset to DexTypeList */    u4  sourceFileIdx;      /* index into stringIds for source file name */    u4  annotationsOff;     /* file offset to annotations_directory_item */    u4  classDataOff;       /* file offset to class_data_item */    u4  staticValuesOff;    /* file offset to DexEncodedArray */} DexClassDef;
    为了方便理解以后的代码,我这里先附上一张图。DexClassDef就是图中最左边的部分class_def_item。

    dexFindClass方法也位于dalvik\vm\oo\Class.c。

const DexClassDef* dexFindClass(const DexFile* pDexFile,    const char* descriptor){    const DexClassLookup* pLookup = pDexFile->pClassLookup;    u4 hash;    int idx, mask;    hash = classDescriptorHash(descriptor);    mask = pLookup->numEntries - 1;    idx = hash & mask;    /*     * Search until we find a matching entry or an empty slot.     */    while (true) {        int offset;        offset = pLookup->table[idx].classDescriptorOffset;        if (offset == 0)            return NULL;        if (pLookup->table[idx].classDescriptorHash == hash) {            const char* str;            str = (const char*) (pDexFile->baseAddr + offset);            if (strcmp(str, descriptor) == 0) {                return (const DexClassDef*)                    (pDexFile->baseAddr + pLookup->table[idx].classDefOffset);            }        }        idx = (idx + 1) & mask;    }}
    最后返回值的地方解释下。pDexFile->baseAddr指向dex文件头部。后面加上的是
DexClassDef结构体距离dex文件头部的偏移。

    

    返回到findClassNoInit,继续运行loadClassFromDex方法。这是真正生成ClassObject对象的地方。

代码位于dalvik\vm\oo\Class.c。

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) {        LOGV("CLASS: loading '%s'...\n",            dexGetClassDescriptor(pDexFile, pClassDef));    }    pEncodedData = dexGetClassData(pDexFile, pClassDef);    if (pEncodedData != NULL) {        dexReadClassDataHeader(&pEncodedData, &header);    } else {        // Provide an all-zeroes header for the rest of the loading.        memset(&header, 0, sizeof(header));    }    result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData,            classLoader);    if (gDvm.verboseClass && (result != NULL)) {        LOGI("[Loaded %s from DEX %p (cl=%p)]\n",            result->descriptor, pDvmDex, classLoader);    }    return result;}
    dexGetClassData方法用来获取上图中的第二部分class_data_item。

代码位于dalvik\libdex\DexFile.h。

DEX_INLINE const u1* dexGetClassData(const DexFile* pDexFile,    const DexClassDef* pClassDef){    if (pClassDef->classDataOff == 0)        return NULL;    return (const u1*) (pDexFile->baseAddr + pClassDef->classDataOff);}
    loadClassFromDex0用于生成终于的ClassObject对象。
代码位于
dalvik\libdex\
DexFile.h。

static ClassObject* loadClassFromDex0(DvmDex* pDvmDex,    const DexClassDef* pClassDef, const DexClassDataHeader* pHeader,    const u1* pEncodedData, Object* classLoader){    ClassObject* newClass = NULL;    const DexFile* pDexFile;    const char* descriptor;    int i;    pDexFile = pDvmDex->pDexFile;    descriptor = dexGetClassDescriptor(pDexFile, pClassDef);    /*     * Make sure the aren't any "bonus" flags set, since we use them for     * runtime state.     */    if ((pClassDef->accessFlags & ~EXPECTED_FILE_FLAGS) != 0) {        LOGW("Invalid file flags in class %s: %04x\n",            descriptor, pClassDef->accessFlags);        return NULL;    }    /*     * Allocate storage for the class object on the GC heap, so that other     * objects can have references to it.  We bypass the usual mechanism     * (allocObject), because we don't have all the bits and pieces yet.     *     * Note that we assume that java.lang.Class does not override     * finalize().     */    /* TODO: Can there be fewer special checks in the usual path?

*/ assert(descriptor != NULL); if (classLoader == NULL && strcmp(descriptor, "Ljava/lang/Class;") == 0) { assert(gDvm.classJavaLangClass != NULL); newClass = gDvm.classJavaLangClass; } else { size_t size = classObjectSize(pHeader->staticFieldsSize); newClass = (ClassObject*) dvmMalloc(size, ALLOC_DEFAULT); } if (newClass == NULL) return NULL; DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass); dvmSetClassSerialNumber(newClass); newClass->descriptor = descriptor; assert(newClass->descriptorAlloc == NULL); newClass->accessFlags = pClassDef->accessFlags; dvmSetFieldObject((Object *)newClass, offsetof(ClassObject, classLoader), (Object *)classLoader); newClass->pDvmDex = pDvmDex; newClass->primitiveType = PRIM_NOT; newClass->status = CLASS_IDX; /* * Stuff the superclass index into the object pointer field. The linker * pulls it out and replaces it with a resolved ClassObject pointer. * I'm doing it this way (rather than having a dedicated superclassIdx * field) to save a few bytes of overhead per class. * * newClass->super is not traversed or freed by dvmFreeClassInnards, so * this is safe. */ assert(sizeof(u4) == sizeof(ClassObject*)); /* 32-bit check */ newClass->super = (ClassObject*) pClassDef->superclassIdx; /* * Stuff class reference indices into the pointer fields. * * The elements of newClass->interfaces are not traversed or freed by * dvmFreeClassInnards, so this is GC-safe. */ const DexTypeList* pInterfacesList; pInterfacesList = dexGetInterfacesList(pDexFile, pClassDef); if (pInterfacesList != NULL) { newClass->interfaceCount = pInterfacesList->size; newClass->interfaces = (ClassObject**) dvmLinearAlloc(classLoader, newClass->interfaceCount * sizeof(ClassObject*)); for (i = 0; i < newClass->interfaceCount; i++) { const DexTypeItem* pType = dexGetTypeItem(pInterfacesList, i); newClass->interfaces[i] = (ClassObject*)(u4) pType->typeIdx; } dvmLinearReadOnly(classLoader, newClass->interfaces); } /* load field definitions */ /* * Over-allocate the class object and append static field info * onto the end. It's fixed-size and known at alloc time. This * seems to increase zygote sharing. Heap compaction will have to * be careful if it ever tries to move ClassObject instances, * because we pass Field pointers around internally. But at least * now these Field pointers are in the object heap. */ if (pHeader->staticFieldsSize != 0) { /* static fields stay on system heap; field data isn't "write once" */ int count = (int) pHeader->staticFieldsSize; u4 lastIndex = 0; DexField field; newClass->sfieldCount = count; for (i = 0; i < count; i++) { dexReadClassDataField(&pEncodedData, &field, &lastIndex); loadSFieldFromDex(newClass, &field, &newClass->sfields[i]); } } if (pHeader->instanceFieldsSize != 0) { int count = (int) pHeader->instanceFieldsSize; u4 lastIndex = 0; DexField field; newClass->ifieldCount = count; newClass->ifields = (InstField*) dvmLinearAlloc(classLoader, count * sizeof(InstField)); for (i = 0; i < count; i++) { dexReadClassDataField(&pEncodedData, &field, &lastIndex); loadIFieldFromDex(newClass, &field, &newClass->ifields[i]); } dvmLinearReadOnly(classLoader, newClass->ifields); } /* * Load method definitions. We do this in two batches, direct then * virtual. * * If register maps ha

转载于:https://www.cnblogs.com/wzzkaifa/p/6984776.html

你可能感兴趣的文章
django实现分页功能
查看>>
CSS伪类与CSS伪元素的区别
查看>>
如何:修改字符串内容
查看>>
Tomcat Server之启动---Bootstrap类
查看>>
经典问题-生产者和消费者问题
查看>>
Hadoop Distributed File System 简介
查看>>
文档通信(跨域-不跨域)、时时通信(websocket)、离线存储(applicationCache)、开启多线程(web worker)...
查看>>
常用正则表达式
查看>>
队列的基本使用方法
查看>>
解题:USACO18FEB Taming the Herd
查看>>
ACM-括号匹配问题
查看>>
使用Python中的urlparse、urllib抓取和解析网页(一)(转)
查看>>
Linux_屏蔽360、scanv、QQ管家等IP扫描
查看>>
LeetCode 538. Convert BST to Greater Tree
查看>>
@JoinColumn
查看>>
22_传智播客iOS视频教程_类的定义
查看>>
HDU 1856
查看>>
[HDU 2102] A计划(搜索题,典型dfs or bfs)
查看>>
推荐给4.3.3越狱用户的安全漏洞修复工
查看>>
用HTML的select+option标签实现下拉框
查看>>