1. 程式人生 > >DexClassLoader和PathClassLoader類載入機制

DexClassLoader和PathClassLoader類載入機制

分析 sbin 分享 return bject _id ise 否則 nts

0x00

在DexClassLoader和PathClassLoader載入Dex流程一文中,我們分析了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);
還記得DexClassLoader和PathClassLoader載入Dex流程一文中,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,就是在DexClassLoader和PathClassLoader載入Dex流程一文中。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

DexClassLoader和PathClassLoader類載入機制