/* * For some of the reflection stuff we need to un-box primitives, e.g. * convert a java/lang/Integer to int or even a float. We assume that * the first instance field holds the value. * * To verify this, we either need to ensure that the class has only one * instance field, or we need to look up the field by name and verify * that it comes first. The former is simpler, and should work. */ bool dvmValidateBoxClasses() { static const char* classes[] = { "Ljava/lang/Boolean;", "Ljava/lang/Character;", "Ljava/lang/Float;", "Ljava/lang/Double;", "Ljava/lang/Byte;", "Ljava/lang/Short;", "Ljava/lang/Integer;", "Ljava/lang/Long;", NULL }; const char** ccp; for (ccp = classes; *ccp != NULL; ccp++) { ClassObject* clazz; clazz = dvmFindClassNoInit(*ccp, NULL); if (clazz == NULL) { ALOGE("Couldn't find '%s'", *ccp); return false; } if (clazz->ifieldCount != 1) { ALOGE("Found %d instance fields in '%s'", clazz->ifieldCount, *ccp); return false; } } return true; }
/* * Find a class by name, initializing it if requested. */ ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader, bool doInit) { ClassObject* clazz = NULL; char* name = NULL; char* descriptor = NULL; if (nameObj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); goto bail; } name = dvmCreateCstrFromString(nameObj); /* * We need to validate and convert the name (from x.y.z to x/y/z). This * is especially handy for array types, since we want to avoid * auto-generating bogus array classes. */ if (!validateClassName(name)) { LOGW("dvmFindClassByName rejecting '%s'\n", name); dvmThrowException("Ljava/lang/ClassNotFoundException;", name); goto bail; } descriptor = dvmDotToDescriptor(name); if (descriptor == NULL) { goto bail; } if (doInit) clazz = dvmFindClass(descriptor, loader); else clazz = dvmFindClassNoInit(descriptor, loader); if (clazz == NULL) { LOGVV("FAIL: load %s (%d)\n", descriptor, doInit); Thread* self = dvmThreadSelf(); Object* oldExcep = dvmGetException(self); dvmAddTrackedAlloc(oldExcep, self); /* don't let this be GCed */ dvmClearException(self); dvmThrowChainedException("Ljava/lang/ClassNotFoundException;", name, oldExcep); dvmReleaseTrackedAlloc(oldExcep, self); } else { LOGVV("GOOD: load %s (%d) --> %p ldr=%p\n", descriptor, doInit, clazz, clazz->classLoader); } bail: free(name); free(descriptor); return clazz; }
/* * Determine if "method" is a "privileged" invocation, i.e. is it one * of the variations of AccessController.doPrivileged(). * * Because the security stuff pulls in a pile of stuff that we may not * want or need, we don't do the class/method lookups at init time, but * instead on first use. */ bool dvmIsPrivilegedMethod(const Method* method) { int i; assert(method != NULL); if (!gDvm.javaSecurityAccessControllerReady) { /* * Populate on first use. No concurrency risk since we're just * finding pointers to fixed structures. */ static const char* kSignatures[NUM_DOPRIV_FUNCS] = { "(Ljava/security/PrivilegedAction;)Ljava/lang/Object;", "(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;", "(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;", "(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;", }; ClassObject* clazz; clazz = dvmFindClassNoInit("Ljava/security/AccessController;", NULL); if (clazz == NULL) { LOGW("Couldn't find java/security/AccessController\n"); return false; } assert(NELEM(gDvm.methJavaSecurityAccessController_doPrivileged) == NELEM(kSignatures)); /* verify init */ for (i = 0; i < NUM_DOPRIV_FUNCS; i++) { gDvm.methJavaSecurityAccessController_doPrivileged[i] = dvmFindDirectMethodByDescriptor(clazz, "doPrivileged", kSignatures[i]); if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == NULL) { LOGW("Warning: couldn't find java/security/AccessController" ".doPrivileged %s\n", kSignatures[i]); return false; } } /* all good, raise volatile readiness flag */ android_atomic_release_store(true, &gDvm.javaSecurityAccessControllerReady); } for (i = 0; i < NUM_DOPRIV_FUNCS; i++) { if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == method) { //LOGI("+++ doPriv match\n"); return true; } } return false; }
/* * Make an inline call for the "debug" interpreter, used when the debugger * or profiler is active. */ bool dvmPerformInlineOp4Dbg(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult, int opIndex) { Thread* self = dvmThreadSelf(); bool result; assert(opIndex >= 0 && opIndex < NELEM(gDvmInlineOpsTable)); #ifdef WITH_PROFILER /* * Populate the methods table on first use. It's possible the class * hasn't been resolved yet, so we need to do the full "calling the * method for the first time" routine. (It's probably okay to skip * the access checks.) * * Currently assuming that we're only inlining stuff loaded by the * bootstrap class loader. This is a safe assumption for many reasons. */ Method* method = gDvm.inlinedMethods[opIndex]; if (method == NULL) { ClassObject* clazz; clazz = dvmFindClassNoInit( gDvmInlineOpsTable[opIndex].classDescriptor, NULL); if (clazz == NULL) { LOGW("Warning: can't find class '%s'\n", clazz->descriptor); goto skip_prof; } method = dvmFindDirectMethodByDescriptor(clazz, gDvmInlineOpsTable[opIndex].methodName, gDvmInlineOpsTable[opIndex].methodSignature); if (method == NULL) method = dvmFindVirtualMethodByDescriptor(clazz, gDvmInlineOpsTable[opIndex].methodName, gDvmInlineOpsTable[opIndex].methodSignature); if (method == NULL) { LOGW("Warning: can't find method %s.%s %s\n", clazz->descriptor, gDvmInlineOpsTable[opIndex].methodName, gDvmInlineOpsTable[opIndex].methodSignature); goto skip_prof; } gDvm.inlinedMethods[opIndex] = method; IF_LOGV() { char* desc = dexProtoCopyMethodDescriptor(&method->prototype); LOGV("Registered for profile: %s.%s %s\n", method->clazz->descriptor, method->name, desc); free(desc); } }
Method* dvmFindInlinableMethod(const char* classDescriptor, const char* methodName, const char* methodSignature) { /* * Find the class. */ ClassObject* clazz = dvmFindClassNoInit(classDescriptor, NULL); if (clazz == NULL) { ALOGE("dvmFindInlinableMethod: can't find class '%s'", classDescriptor); dvmClearException(dvmThreadSelf()); return NULL; } /* * Method could be virtual or direct. Try both. Don't use * the "hier" versions. */ Method* method = dvmFindDirectMethodByDescriptor(clazz, methodName, methodSignature); if (method == NULL) { method = dvmFindVirtualMethodByDescriptor(clazz, methodName, methodSignature); } if (method == NULL) { ALOGE("dvmFindInlinableMethod: can't find method %s.%s %s", clazz->descriptor, methodName, methodSignature); return NULL; } /* * Check that the method is appropriate for inlining. */ if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) { ALOGE("dvmFindInlinableMethod: can't inline non-final method %s.%s", clazz->descriptor, method->name); return NULL; } if (dvmIsSynchronizedMethod(method) || dvmIsDeclaredSynchronizedMethod(method)) { ALOGE("dvmFindInlinableMethod: can't inline synchronized method %s.%s", clazz->descriptor, method->name); return NULL; } return method; }
/* * Find the named class object. We have to trim "*pSignature" down to just * the first token, do the lookup, and then restore anything important * that we've stomped on. * * "pSig" will be advanced to the start of the next token. */ static ClassObject* convertSignaturePartToClass(char** pSignature, const ClassObject* defClass) { ClassObject* clazz = NULL; char* signature = *pSignature; if (*signature == '[') { /* looks like "[[[Landroid/debug/Stuff;"; we want the whole thing */ char savedChar; while (*++signature == '[') ; if (*signature == 'L') { while (*++signature != ';') ; } /* advance past ';', and stomp on whatever comes next */ savedChar = *++signature; *signature = '\0'; clazz = dvmFindArrayClass(*pSignature, defClass->classLoader); *signature = savedChar; } else if (*signature == 'L') { /* looks like 'Landroid/debug/Stuff;"; we want the whole thing */ char savedChar; while (*++signature != ';') ; savedChar = *++signature; *signature = '\0'; clazz = dvmFindClassNoInit(*pSignature, defClass->classLoader); *signature = savedChar; } else { clazz = dvmFindPrimitiveClass(*signature++); } if (clazz == NULL) { ALOGW("Unable to match class for part: '%s'", *pSignature); } *pSignature = signature; return clazz; }
/* * Find the class corresponding to "classIdx", which maps to a class name * string. It might be in the same DEX file as "referrer", in a different * DEX file, generated by a class loader, or generated by the VM (e.g. * array classes). * * Because the DexTypeId is associated with the referring class' DEX file, * we may have to resolve the same class more than once if it's referred * to from classes in multiple DEX files. This is a necessary property for * DEX files associated with different class loaders. * * We cache a copy of the lookup in the DexFile's "resolved class" table, * so future references to "classIdx" are faster. * * Note that "referrer" may be in the process of being linked. * * Traditional VMs might do access checks here, but in Dalvik the class * "constant pool" is shared between all classes in the DEX file. We rely * on the verifier to do the checks for us. * * Does not initialize the class. * * "fromUnverifiedConstant" should only be set if this call is the direct * result of executing a "const-class" or "instance-of" instruction, which * use class constants not resolved by the bytecode verifier. * * Returns NULL with an exception raised on failure. */ ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx, bool fromUnverifiedConstant) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const char* className; /* * Check the table first -- this gets called from the other "resolve" * methods. */ resClass = dvmDexGetResolvedClass(pDvmDex, classIdx); if (resClass != NULL) return resClass; LOGVV("--- resolving class %u (referrer=%s cl=%p)\n", classIdx, referrer->descriptor, referrer->classLoader); /* * Class hasn't been loaded yet, or is in the process of being loaded * and initialized now. Try to get a copy. If we find one, put the * pointer in the DexTypeId. There isn't a race condition here -- * 32-bit writes are guaranteed atomic on all target platforms. Worst * case we have two threads storing the same value. * * If this is an array class, we'll generate it here. */ className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx); if (className[0] != '\0' && className[1] == '\0') { /* primitive type */ resClass = dvmFindPrimitiveClass(className[0]); } else { resClass = dvmFindClassNoInit(className, referrer->classLoader); } if (resClass != NULL) { /* * If the referrer was pre-verified, the resolved class must come * from the same DEX or from a bootstrap class. The pre-verifier * makes assumptions that could be invalidated by a wacky class * loader. (See the notes at the top of oo/Class.c.) * * The verifier does *not* fail a class for using a const-class * or instance-of instruction referring to an unresolveable class, * because the result of the instruction is simply a Class object * or boolean -- there's no need to resolve the class object during * verification. Instance field and virtual method accesses can * break dangerously if we get the wrong class, but const-class and * instance-of are only interesting at execution time. So, if we * we got here as part of executing one of the "unverified class" * instructions, we skip the additional check. * * Ditto for class references from annotations and exception * handler lists. */ if (!fromUnverifiedConstant && IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED)) { ClassObject* resClassCheck = resClass; if (dvmIsArrayClass(resClassCheck)) resClassCheck = resClassCheck->elementClass; if (referrer->pDvmDex != resClassCheck->pDvmDex && resClassCheck->classLoader != NULL) { LOGW("Class resolved by unexpected DEX:" " %s(%p):%p ref [%s] %s(%p):%p\n", referrer->descriptor, referrer->classLoader, referrer->pDvmDex, resClass->descriptor, resClassCheck->descriptor, resClassCheck->classLoader, resClassCheck->pDvmDex); LOGW("(%s had used a different %s during pre-verification)\n", referrer->descriptor, resClass->descriptor); dvmThrowException("Ljava/lang/IllegalAccessError;", "Class ref in pre-verified class resolved to unexpected " "implementation"); return NULL; } } LOGVV("##### +ResolveClass(%s): referrer=%s dex=%p ldr=%p ref=%d\n", resClass->descriptor, referrer->descriptor, referrer->pDvmDex, referrer->classLoader, classIdx); /* * Add what we found to the list so we can skip the class search * next time through. * * TODO: should we be doing this when fromUnverifiedConstant==true? * (see comments at top of oo/Class.c) */ dvmDexSetResolvedClass(pDvmDex, classIdx, resClass); } else { /* not found, exception should be raised */ LOGVV("Class not found: %s\n", dexStringByTypeIdx(pDvmDex->pDexFile, classIdx)); assert(dvmCheckException(dvmThreadSelf())); } return resClass; }
/* * Alternate version of dvmResolveClass for use with verification and * optimization. Performs access checks on every resolve, and refuses * to acknowledge the existence of classes defined in more than one DEX * file. * * Exceptions caused by failures are cleared before returning. * * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. */ ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx, VerifyError* pFailure) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; /* * Check the table first. If not there, do the lookup by name. */ resClass = dvmDexGetResolvedClass(pDvmDex, classIdx); if (resClass == NULL) { const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx); if (className[0] != '\0' && className[1] == '\0') { /* primitive type */ resClass = dvmFindPrimitiveClass(className[0]); } else { resClass = dvmFindClassNoInit(className, referrer->classLoader); } if (resClass == NULL) { /* not found, exception should be raised */ ALOGV("DexOpt: class %d (%s) not found", classIdx, dexStringByTypeIdx(pDvmDex->pDexFile, classIdx)); if (pFailure != NULL) { /* dig through the wrappers to find the original failure */ Object* excep = dvmGetException(dvmThreadSelf()); while (true) { Object* cause = dvmGetExceptionCause(excep); if (cause == NULL) break; excep = cause; } if (strcmp(excep->clazz->descriptor, "Ljava/lang/IncompatibleClassChangeError;") == 0) { *pFailure = VERIFY_ERROR_CLASS_CHANGE; } else { *pFailure = VERIFY_ERROR_NO_CLASS; } } dvmClearOptException(dvmThreadSelf()); return NULL; } /* * Add it to the resolved table so we're faster on the next lookup. */ dvmDexSetResolvedClass(pDvmDex, classIdx, resClass); } /* multiple definitions? */ if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) { ALOGI("DexOpt: not resolving ambiguous class '%s'", resClass->descriptor); if (pFailure != NULL) *pFailure = VERIFY_ERROR_NO_CLASS; return NULL; } /* access allowed? */ tweakLoader(referrer, resClass); bool allowed = dvmCheckClassAccess(referrer, resClass); untweakLoader(referrer, resClass); if (!allowed) { ALOGW("DexOpt: resolve class illegal access: %s -> %s", referrer->descriptor, resClass->descriptor); if (pFailure != NULL) *pFailure = VERIFY_ERROR_ACCESS_CLASS; return NULL; } return resClass; }