| /* | 
 |  * Copyright (C) 2008 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | /* | 
 |  * Internal-native initialization and some common utility functions. | 
 |  */ | 
 | #include "Dalvik.h" | 
 | #include "native/InternalNativePriv.h" | 
 |  | 
 | /* | 
 |  * Set of classes for which we provide methods. | 
 |  * | 
 |  * The last field, classNameHash, is filled in at startup. | 
 |  */ | 
 | static DalvikNativeClass gDvmNativeMethodSet[] = { | 
 |     { "Ljava/lang/Object;",               dvm_java_lang_Object, 0 }, | 
 |     { "Ljava/lang/Class;",                dvm_java_lang_Class, 0 }, | 
 |     { "Ljava/lang/Runtime;",              dvm_java_lang_Runtime, 0 }, | 
 |     { "Ljava/lang/String;",               dvm_java_lang_String, 0 }, | 
 |     { "Ljava/lang/System;",               dvm_java_lang_System, 0 }, | 
 |     { "Ljava/lang/SystemProperties;",     dvm_java_lang_SystemProperties, 0 }, | 
 |     { "Ljava/lang/Throwable;",            dvm_java_lang_Throwable, 0 }, | 
 |     { "Ljava/lang/VMClassLoader;",        dvm_java_lang_VMClassLoader, 0 }, | 
 |     { "Ljava/lang/VMThread;",             dvm_java_lang_VMThread, 0 }, | 
 |     { "Ljava/lang/reflect/AccessibleObject;", | 
 |             dvm_java_lang_reflect_AccessibleObject, 0 }, | 
 |     { "Ljava/lang/reflect/Array;",        dvm_java_lang_reflect_Array, 0 }, | 
 |     { "Ljava/lang/reflect/Constructor;", | 
 |             dvm_java_lang_reflect_Constructor, 0 }, | 
 |     { "Ljava/lang/reflect/Field;",        dvm_java_lang_reflect_Field, 0 }, | 
 |     { "Ljava/lang/reflect/Method;",       dvm_java_lang_reflect_Method, 0 }, | 
 |     { "Ljava/lang/reflect/Proxy;",        dvm_java_lang_reflect_Proxy, 0 }, | 
 |     { "Ljava/security/AccessController;", | 
 |             dvm_java_security_AccessController, 0 }, | 
 |     { "Ljava/util/concurrent/atomic/AtomicLong;", | 
 |             dvm_java_util_concurrent_atomic_AtomicLong, 0 }, | 
 |     { "Ldalvik/system/SamplingProfiler;", | 
 |             dvm_dalvik_system_SamplingProfiler, 0 }, | 
 |     { "Ldalvik/system/VMDebug;",          dvm_dalvik_system_VMDebug, 0 }, | 
 |     { "Ldalvik/system/DexFile;",          dvm_dalvik_system_DexFile, 0 }, | 
 |     { "Ldalvik/system/VMRuntime;",        dvm_dalvik_system_VMRuntime, 0 }, | 
 |     { "Ldalvik/system/Zygote;",           dvm_dalvik_system_Zygote, 0 }, | 
 |     { "Ldalvik/system/VMStack;",          dvm_dalvik_system_VMStack, 0 }, | 
 |     { "Lorg/apache/harmony/dalvik/ddmc/DdmServer;", | 
 |             dvm_org_apache_harmony_dalvik_ddmc_DdmServer, 0 }, | 
 |     { "Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;",  | 
 |             dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal, 0 }, | 
 |     { "Lorg/apache/harmony/dalvik/NativeTestTarget;", | 
 |             dvm_org_apache_harmony_dalvik_NativeTestTarget, 0 }, | 
 |     { "Lsun/misc/Unsafe;",                dvm_sun_misc_Unsafe, 0 }, | 
 |     { NULL, NULL, 0 }, | 
 | }; | 
 |  | 
 |  | 
 | /* | 
 |  * Set up hash values on the class names. | 
 |  */ | 
 | bool dvmInternalNativeStartup(void) | 
 | { | 
 |     DalvikNativeClass* classPtr = gDvmNativeMethodSet; | 
 |  | 
 |     while (classPtr->classDescriptor != NULL) { | 
 |         classPtr->classDescriptorHash = | 
 |             dvmComputeUtf8Hash(classPtr->classDescriptor); | 
 |         classPtr++; | 
 |     } | 
 |  | 
 |     gDvm.userDexFiles = dvmHashTableCreate(2, dvmFreeDexOrJar); | 
 |     if (gDvm.userDexFiles == NULL) | 
 |         return false; | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | /* | 
 |  * Clean up. | 
 |  */ | 
 | void dvmInternalNativeShutdown(void) | 
 | { | 
 |     dvmHashTableFree(gDvm.userDexFiles); | 
 | } | 
 |  | 
 | /* | 
 |  * Search the internal native set for a match. | 
 |  */ | 
 | DalvikNativeFunc dvmLookupInternalNativeMethod(const Method* method) | 
 | { | 
 |     const char* classDescriptor = method->clazz->descriptor; | 
 |     const DalvikNativeClass* pClass; | 
 |     u4 hash; | 
 |  | 
 |     hash = dvmComputeUtf8Hash(classDescriptor); | 
 |     pClass = gDvmNativeMethodSet; | 
 |     while (true) { | 
 |         if (pClass->classDescriptor == NULL) | 
 |             break; | 
 |         if (pClass->classDescriptorHash == hash && | 
 |             strcmp(pClass->classDescriptor, classDescriptor) == 0) | 
 |         { | 
 |             const DalvikNativeMethod* pMeth = pClass->methodInfo; | 
 |             while (true) { | 
 |                 if (pMeth->name == NULL) | 
 |                     break; | 
 |  | 
 |                 if (dvmCompareNameDescriptorAndMethod(pMeth->name, | 
 |                     pMeth->signature, method) == 0) | 
 |                 { | 
 |                     /* match */ | 
 |                     //LOGV("+++  match on %s.%s %s at %p\n", | 
 |                     //    className, methodName, methodSignature, pMeth->fnPtr); | 
 |                     return pMeth->fnPtr; | 
 |                 } | 
 |  | 
 |                 pMeth++; | 
 |             } | 
 |         } | 
 |  | 
 |         pClass++; | 
 |     } | 
 |  | 
 |     return NULL; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Magic "internal native" code stub, inserted into abstract method | 
 |  * definitions when a class is first loaded.  This throws the expected | 
 |  * exception so we don't have to explicitly check for it in the interpreter. | 
 |  */ | 
 | void dvmAbstractMethodStub(const u4* args, JValue* pResult) | 
 | { | 
 |     LOGD("--- called into dvmAbstractMethodStub\n"); | 
 |     dvmThrowException("Ljava/lang/AbstractMethodError;", | 
 |         "abstract method not implemented"); | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Verify that "obj" is non-null and is an instance of "clazz". | 
 |  * | 
 |  * Returns "false" and throws an exception if not. | 
 |  */ | 
 | bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz) | 
 | { | 
 |     if (obj == NULL) { | 
 |         dvmThrowException("Ljava/lang/NullPointerException;", NULL); | 
 |         return false; | 
 |     } | 
 |     if (!dvmInstanceof(obj->clazz, clazz)) { | 
 |         dvmThrowException("Ljava/lang/IllegalArgumentException;", | 
 |             "object is not an instance of the class"); | 
 |         return false; | 
 |     } | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | /* | 
 |  * Validate a "binary" class name, e.g. "java.lang.String" or "[I". | 
 |  */ | 
 | static bool validateClassName(const char* name) | 
 | { | 
 |     int len = strlen(name); | 
 |     int i = 0; | 
 |  | 
 |     /* check for reasonable array types */ | 
 |     if (name[0] == '[') { | 
 |         while (name[i] == '[') | 
 |             i++; | 
 |  | 
 |         if (name[i] == 'L') { | 
 |             /* array of objects, make sure it ends well */ | 
 |             if (name[len-1] != ';') | 
 |                 return false; | 
 |         } else if (strchr(PRIM_TYPE_TO_LETTER, name[i]) != NULL) { | 
 |             if (i != len-1) | 
 |                 return false; | 
 |         } else { | 
 |             return false; | 
 |         } | 
 |     } | 
 |  | 
 |     /* quick check for illegal chars */ | 
 |     for ( ; i < len; i++) { | 
 |         if (name[i] == '/') | 
 |             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; | 
 | } | 
 |  | 
 | /* | 
 |  * We insert native method stubs for abstract methods so we don't have to | 
 |  * check the access flags at the time of the method call.  This results in | 
 |  * "native abstract" methods, which can't exist.  If we see the "abstract" | 
 |  * flag set, clear the "native" flag. | 
 |  *  | 
 |  * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED | 
 |  * position, because the callers of this function are trying to convey | 
 |  * the "traditional" meaning of the flags to their callers. | 
 |  */ | 
 | u4 dvmFixMethodFlags(u4 flags) | 
 | { | 
 |     if ((flags & ACC_ABSTRACT) != 0) { | 
 |         flags &= ~ACC_NATIVE; | 
 |     } | 
 |  | 
 |     flags &= ~ACC_SYNCHRONIZED; | 
 |      | 
 |     if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) { | 
 |         flags |= ACC_SYNCHRONIZED; | 
 |     } | 
 |      | 
 |     return flags & JAVA_FLAGS_MASK; | 
 | } | 
 |  | 
 |  | 
 | #define NUM_DOPRIV_FUNCS    4 | 
 |  | 
 | /* | 
 |  * 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 */ | 
 |         gDvm.javaSecurityAccessControllerReady = true; | 
 |     } | 
 |  | 
 |     for (i = 0; i < NUM_DOPRIV_FUNCS; i++) { | 
 |         if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == method) { | 
 |             //LOGI("+++ doPriv match\n"); | 
 |             return true; | 
 |         } | 
 |     } | 
 |     return false; | 
 | } | 
 |  |