/* * Try to load all classes in the specified DEX. If they have some sort * of broken dependency, e.g. their superclass lives in a different DEX * that wasn't previously loaded into the bootstrap class path, loading * will fail. This is the desired behavior. * * We have no notion of class loader at this point, so we load all of * the classes with the bootstrap class loader. It turns out this has * exactly the behavior we want, and has no ill side effects because we're * running in a separate process and anything we load here will be forgotten. * * We set the CLASS_MULTIPLE_DEFS flag here if we see multiple definitions. * This works because we only call here as part of optimization / pre-verify, * not during verification as part of loading a class into a running VM. * * This returns "false" if the world is too screwed up to do anything * useful at all. */ static bool loadAllClasses(DvmDex* pDvmDex) { u4 count = pDvmDex->pDexFile->pHeader->classDefsSize; u4 idx; int loaded = 0; LOGV("DexOpt: +++ trying to load %d classes\n", count); dvmSetBootPathExtraDex(pDvmDex); /* * We have some circularity issues with Class and Object that are most * easily avoided by ensuring that Object is never the first thing we * try to find. Take care of that here. (We only need to do this when * loading classes from the DEX file that contains Object, and only * when Object comes first in the list, but it costs very little to * do it in all cases.) */ if (dvmFindSystemClass("Ljava/lang/Class;") == NULL) { LOGE("ERROR: java.lang.Class does not exist!\n"); return false; } for (idx = 0; idx < count; idx++) { const DexClassDef* pClassDef; const char* classDescriptor; ClassObject* newClass; pClassDef = dexGetClassDef(pDvmDex->pDexFile, idx); classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, pClassDef->classIdx); LOGV("+++ loading '%s'", classDescriptor); //newClass = dvmDefineClass(pDexFile, classDescriptor, // NULL); newClass = dvmFindSystemClassNoInit(classDescriptor); if (newClass == NULL) { LOGV("DexOpt: failed loading '%s'\n", classDescriptor); dvmClearOptException(dvmThreadSelf()); } else if (newClass->pDvmDex != pDvmDex) { /* * We don't load the new one, and we tag the first one found * with the "multiple def" flag so the resolver doesn't try * to make it available. */ LOGD("DexOpt: '%s' has an earlier definition; blocking out\n", classDescriptor); SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS); } else { loaded++; } } LOGV("DexOpt: +++ successfully loaded %d classes\n", loaded); dvmSetBootPathExtraDex(NULL); return true; }
static bool initDirectMethodReference(Method** pMethod, const char* className, const char* name, const char* descriptor) { ClassObject* clazz = dvmFindSystemClassNoInit(className); if (clazz == NULL) { ALOGE("Could not find essential class %s for direct method lookup", className); return false; } return initDirectMethodReferenceByClass(pMethod, clazz, name, descriptor); }
/* * Perform Proxy setup. */ bool dvmReflectProxyStartup() { /* * Standard methods we must provide in our proxy. */ Method* methE; Method* methH; Method* methT; Method* methF; methE = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "equals", "(Ljava/lang/Object;)Z"); methH = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "hashCode", "()I"); methT = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "toString", "()Ljava/lang/String;"); methF = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "finalize", "()V"); if (methE == NULL || methH == NULL || methT == NULL || methF == NULL) { LOGE("Could not find equals/hashCode/toString/finalize in Object\n"); return false; } gDvm.voffJavaLangObject_equals = methE->methodIndex; gDvm.voffJavaLangObject_hashCode = methH->methodIndex; gDvm.voffJavaLangObject_toString = methT->methodIndex; gDvm.voffJavaLangObject_finalize = methF->methodIndex; /* * The prototype signature needs to be cloned from a method in a * "real" DEX file. We declared this otherwise unused method just * for this purpose. */ ClassObject* proxyClass; Method* meth; proxyClass = dvmFindSystemClassNoInit("Ljava/lang/reflect/Proxy;"); if (proxyClass == NULL) { LOGE("No java.lang.reflect.Proxy\n"); return false; } meth = dvmFindDirectMethodByDescriptor(proxyClass, "constructorPrototype", "(Ljava/lang/reflect/InvocationHandler;)V"); if (meth == NULL) { LOGE("Could not find java.lang.Proxy.constructorPrototype()\n"); return false; } gDvm.methJavaLangReflectProxy_constructorPrototype = meth; return true; }
static bool initClassReference(ClassObject** pClass, const char* name) { ClassObject* result; assert(*pClass == NULL); if (name[0] == '[') { result = dvmFindArrayClass(name, NULL); } else { result = dvmFindSystemClassNoInit(name); } if (result == NULL) { ALOGE("Could not find essential class %s", name); return false; } *pClass = result; return true; }
static bool initVirtualMethodOffset(int* pOffset, const char* className, const char* name, const char* descriptor) { ClassObject* clazz = dvmFindSystemClassNoInit(className); if (clazz == NULL) { ALOGE("Could not find essential class %s for virtual method lookup", className); return false; } Method* method = dvmFindVirtualMethodByDescriptor(clazz, name, descriptor); if (method == NULL) { ALOGE("Could not find essential virtual method %s.%s with descriptor %s", clazz->descriptor, name, descriptor); return false; } *pOffset = method->methodIndex; return true; }
/* * Initialize string globals. * * This isn't part of the VM init sequence because it's hard to get the * timing right -- we need it to happen after java/lang/String has been * loaded, but before anybody wants to use a string. It's easiest to * just initialize it on first use. * * In some unusual circumstances (e.g. trying to throw an exception because * String implements java/lang/CharSequence, but CharSequence doesn't exist) * we can try to create an exception string internally before anything has * really tried to use String. In that case we basically self-destruct. */ static bool stringStartup() { if (gDvm.javaLangStringReady < 0) { LOGE("ERROR: reentrant string initialization\n"); assert(false); return false; } assert(gDvm.javaLangStringReady == 0); gDvm.javaLangStringReady = -1; if (gDvm.classJavaLangString == NULL) gDvm.classJavaLangString = dvmFindSystemClassNoInit("Ljava/lang/String;"); gDvm.offJavaLangString_value = dvmFindFieldOffset(gDvm.classJavaLangString, "value", "[C"); gDvm.offJavaLangString_count = dvmFindFieldOffset(gDvm.classJavaLangString, "count", "I"); gDvm.offJavaLangString_offset = dvmFindFieldOffset(gDvm.classJavaLangString, "offset", "I"); gDvm.offJavaLangString_hashCode = dvmFindFieldOffset(gDvm.classJavaLangString, "hashCode", "I"); if (gDvm.offJavaLangString_value < 0 || gDvm.offJavaLangString_count < 0 || gDvm.offJavaLangString_offset < 0 || gDvm.offJavaLangString_hashCode < 0) { LOGE("VM-required field missing from java/lang/String\n"); return false; } gDvm.javaLangStringReady = 1; return true; }
static bool initFieldOffsets() { struct FieldInfo { int* offset; const char* name; const char* type; }; static struct FieldInfo infoDdmcChunk[] = { { &gDvm.offDalvikDdmcChunk_type, "type", "I" }, { &gDvm.offDalvikDdmcChunk_data, "data", "[B" }, { &gDvm.offDalvikDdmcChunk_offset, "offset", "I" }, { &gDvm.offDalvikDdmcChunk_length, "length", "I" }, { NULL, NULL, NULL } }; static struct FieldInfo infoFileDescriptor[] = { { &gDvm.offJavaIoFileDescriptor_descriptor, "descriptor", "I" }, { NULL, NULL, NULL } }; static struct FieldInfo infoString[] = { { &gDvm.offJavaLangString_value, "value", "[C" }, { &gDvm.offJavaLangString_count, "count", "I" }, { &gDvm.offJavaLangString_offset, "offset", "I" }, { &gDvm.offJavaLangString_hashCode, "hashCode", "I" }, { NULL, NULL, NULL } }; static struct FieldInfo infoThread[] = { { &gDvm.offJavaLangThread_vmThread, "vmThread", "Ljava/lang/VMThread;" }, { &gDvm.offJavaLangThread_group, "group", "Ljava/lang/ThreadGroup;" }, { &gDvm.offJavaLangThread_daemon, "daemon", "Z" }, { &gDvm.offJavaLangThread_name, "name", "Ljava/lang/String;" }, { &gDvm.offJavaLangThread_priority, "priority", "I" }, { &gDvm.offJavaLangThread_uncaughtHandler, "uncaughtHandler", "Ljava/lang/Thread$UncaughtExceptionHandler;" }, { &gDvm.offJavaLangThread_contextClassLoader, "contextClassLoader", "Ljava/lang/ClassLoader;" }, { NULL, NULL, NULL } }; static struct FieldInfo infoThreadGroup[] = { { &gDvm.offJavaLangThreadGroup_name, "name", "Ljava/lang/String;" }, { &gDvm.offJavaLangThreadGroup_parent, "parent", "Ljava/lang/ThreadGroup;" }, { NULL, NULL, NULL } }; static struct FieldInfo infoThrowable[] = { { &gDvm.offJavaLangThrowable_stackState, "stackState", "Ljava/lang/Object;" }, { &gDvm.offJavaLangThrowable_cause, "cause", "Ljava/lang/Throwable;" }, { NULL, NULL, NULL } }; static struct FieldInfo infoVMThread[] = { { &gDvm.offJavaLangVMThread_thread, "thread", "Ljava/lang/Thread;" }, { &gDvm.offJavaLangVMThread_vmData, "vmData", "I" }, { NULL, NULL, NULL } }; static struct FieldInfo infoFinalizerReference[] = { { &gDvm.offJavaLangRefFinalizerReference_zombie, "zombie", "Ljava/lang/Object;" }, { NULL, NULL, NULL } }; static struct FieldInfo infoConstructor[] = { { &gDvm.offJavaLangReflectConstructor_slot, "slot", "I" }, { &gDvm.offJavaLangReflectConstructor_declClass, "declaringClass", "Ljava/lang/Class;" }, { NULL, NULL, NULL } }; static struct FieldInfo infoField[] = { { &gDvm.offJavaLangReflectField_slot, "slot", "I" }, { &gDvm.offJavaLangReflectField_declClass, "declaringClass", "Ljava/lang/Class;" }, { NULL, NULL, NULL } }; static struct FieldInfo infoMethod[] = { { &gDvm.offJavaLangReflectMethod_slot, "slot", "I" }, { &gDvm.offJavaLangReflectMethod_declClass, "declaringClass", "Ljava/lang/Class;" }, { NULL, NULL, NULL } }; static struct FieldInfo infoProxy[] = { { &gDvm.offJavaLangReflectProxy_h, "h", "Ljava/lang/reflect/InvocationHandler;" }, { NULL, NULL, NULL } }; static struct FieldInfo infoBuffer[] = { { &gDvm.offJavaNioBuffer_capacity, "capacity", "I" }, { &gDvm.offJavaNioBuffer_effectiveDirectAddress, "effectiveDirectAddress", "J" }, { NULL, NULL, NULL } }; static struct { const char* name; const struct FieldInfo* fields; } classes[] = { { "Lorg/apache/harmony/dalvik/ddmc/Chunk;", infoDdmcChunk }, { "Ljava/io/FileDescriptor;", infoFileDescriptor }, { "Ljava/lang/String;", infoString }, { "Ljava/lang/Thread;", infoThread }, { "Ljava/lang/ThreadGroup;", infoThreadGroup }, { "Ljava/lang/Throwable;", infoThrowable }, { "Ljava/lang/VMThread;", infoVMThread }, { "Ljava/lang/ref/FinalizerReference;", infoFinalizerReference }, { "Ljava/lang/reflect/Constructor;", infoConstructor }, { "Ljava/lang/reflect/Field;", infoField }, { "Ljava/lang/reflect/Method;", infoMethod }, { "Ljava/lang/reflect/Proxy;", infoProxy }, { "Ljava/nio/Buffer;", infoBuffer }, { NULL, NULL } }; int i; for (i = 0; classes[i].name != NULL; i++) { const char* className = classes[i].name; ClassObject* clazz = dvmFindSystemClassNoInit(className); const struct FieldInfo* fields = classes[i].fields; if (clazz == NULL) { ALOGE("Could not find essential class %s for field lookup", className); return false; } int j; for (j = 0; fields[j].offset != NULL; j++) { if (!initFieldOffset(clazz, fields[j].offset, fields[j].name, fields[j].type)) { return false; } } } return true; }
/* * Cache pointers to some of the exception classes we use locally. * * Note this is NOT called during dexopt optimization. Some of the fields * are initialized by the verifier (dvmVerifyCodeFlow). */ bool dvmExceptionStartup(void) { gDvm.classJavaLangThrowable = dvmFindSystemClassNoInit("Ljava/lang/Throwable;"); gDvm.classJavaLangRuntimeException = dvmFindSystemClassNoInit("Ljava/lang/RuntimeException;"); gDvm.classJavaLangStackOverflowError = dvmFindSystemClassNoInit("Ljava/lang/StackOverflowError;"); gDvm.classJavaLangError = dvmFindSystemClassNoInit("Ljava/lang/Error;"); gDvm.classJavaLangStackTraceElement = dvmFindSystemClassNoInit("Ljava/lang/StackTraceElement;"); gDvm.classJavaLangStackTraceElementArray = dvmFindArrayClass("[Ljava/lang/StackTraceElement;", NULL); if (gDvm.classJavaLangThrowable == NULL || gDvm.classJavaLangStackTraceElement == NULL || gDvm.classJavaLangStackTraceElementArray == NULL) { LOGE("Could not find one or more essential exception classes\n"); return false; } /* * Find the constructor. Note that, unlike other saved method lookups, * we're using a Method* instead of a vtable offset. This is because * constructors don't have vtable offsets. (Also, since we're creating * the object in question, it's impossible for anyone to sub-class it.) */ Method* meth; meth = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangStackTraceElement, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V"); if (meth == NULL) { LOGE("Unable to find constructor for StackTraceElement\n"); return false; } gDvm.methJavaLangStackTraceElement_init = meth; /* grab an offset for the stackData field */ gDvm.offJavaLangThrowable_stackState = dvmFindFieldOffset(gDvm.classJavaLangThrowable, "stackState", "Ljava/lang/Object;"); if (gDvm.offJavaLangThrowable_stackState < 0) { LOGE("Unable to find Throwable.stackState\n"); return false; } /* and one for the message field, in case we want to show it */ gDvm.offJavaLangThrowable_message = dvmFindFieldOffset(gDvm.classJavaLangThrowable, "detailMessage", "Ljava/lang/String;"); if (gDvm.offJavaLangThrowable_message < 0) { LOGE("Unable to find Throwable.detailMessage\n"); return false; } /* and one for the cause field, just 'cause */ gDvm.offJavaLangThrowable_cause = dvmFindFieldOffset(gDvm.classJavaLangThrowable, "cause", "Ljava/lang/Throwable;"); if (gDvm.offJavaLangThrowable_cause < 0) { LOGE("Unable to find Throwable.cause\n"); return false; } return true; }
/* * Initialize string globals. * * This isn't part of the VM init sequence because it's hard to get the * timing right -- we need it to happen after java/lang/String has been * loaded, but before anybody wants to use a string. It's easiest to * just initialize it on first use. * * In some unusual circumstances (e.g. trying to throw an exception because * String implements java/lang/CharSequence, but CharSequence doesn't exist) * we can try to create an exception string internally before anything has * really tried to use String. In that case we basically self-destruct. */ static bool stringStartup() { if (gDvm.javaLangStringReady < 0) { LOGE("ERROR: reentrant string initialization\n"); assert(false); return false; } assert(gDvm.javaLangStringReady == 0); gDvm.javaLangStringReady = -1; if (gDvm.classJavaLangString == NULL) gDvm.classJavaLangString = dvmFindSystemClassNoInit("Ljava/lang/String;"); gDvm.offJavaLangString_value = dvmFindFieldOffset(gDvm.classJavaLangString, "value", "[C"); gDvm.offJavaLangString_count = dvmFindFieldOffset(gDvm.classJavaLangString, "count", "I"); gDvm.offJavaLangString_offset = dvmFindFieldOffset(gDvm.classJavaLangString, "offset", "I"); gDvm.offJavaLangString_hashCode = dvmFindFieldOffset(gDvm.classJavaLangString, "hashCode", "I"); if (gDvm.offJavaLangString_value < 0 || gDvm.offJavaLangString_count < 0 || gDvm.offJavaLangString_offset < 0 || gDvm.offJavaLangString_hashCode < 0) { LOGE("VM-required field missing from java/lang/String\n"); return false; } bool badValue = false; if (gDvm.offJavaLangString_value != STRING_FIELDOFF_VALUE) { LOGE("InlineNative: String.value offset = %d, expected %d\n", gDvm.offJavaLangString_value, STRING_FIELDOFF_VALUE); badValue = true; } if (gDvm.offJavaLangString_count != STRING_FIELDOFF_COUNT) { LOGE("InlineNative: String.count offset = %d, expected %d\n", gDvm.offJavaLangString_count, STRING_FIELDOFF_COUNT); badValue = true; } if (gDvm.offJavaLangString_offset != STRING_FIELDOFF_OFFSET) { LOGE("InlineNative: String.offset offset = %d, expected %d\n", gDvm.offJavaLangString_offset, STRING_FIELDOFF_OFFSET); badValue = true; } if (gDvm.offJavaLangString_hashCode != STRING_FIELDOFF_HASHCODE) { LOGE("InlineNative: String.hashCode offset = %d, expected %d\n", gDvm.offJavaLangString_hashCode, STRING_FIELDOFF_HASHCODE); badValue = true; } if (badValue) return false; gDvm.javaLangStringReady = 1; return true; }