/* * static Class findLoadedClass(ClassLoader cl, String name) */ static void Dalvik_java_lang_VMClassLoader_findLoadedClass(const u4* args, JValue* pResult) { Object* loader = (Object*) args[0]; StringObject* nameObj = (StringObject*) args[1]; ClassObject* clazz = NULL; char* name = NULL; char* descriptor = NULL; if (nameObj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); goto bail; } /* * Get a UTF-8 copy of the string, and convert dots to slashes. */ name = dvmCreateCstrFromString(nameObj); if (name == NULL) goto bail; descriptor = dvmDotToDescriptor(name); if (descriptor == NULL) goto bail; clazz = dvmLookupClass(descriptor, loader, false); LOGVV("look: %s ldr=%p --> %p\n", descriptor, loader, clazz); bail: free(name); free(descriptor); RETURN_PTR(clazz); }
java_lang_String_p fastiva_Dalvik_java_lang_System_mapLibraryName(java_lang_String_p nameObj) { #endif StringObject* result = NULL; char* name; char* mappedName; if (nameObj == NULL) { dvmThrowNullPointerException("userLibName == null"); THROW_V(); } name = dvmCreateCstrFromString(nameObj); #ifdef _DEBUG assert(strstr(name, "cfb") == NULL); #endif mappedName = dvmCreateSystemLibraryName(name); if (mappedName != NULL) { result = dvmCreateStringFromCstr(mappedName); dvmReleaseTrackedAlloc((Object*) result, NULL); } free(name); free(mappedName); RETURN_PTR((java_lang_String_p)result); }
/* * Get the named method. */ Object* dvmGetDeclaredConstructorOrMethod(ClassObject* clazz, StringObject* nameObj, ArrayObject* args) { Object* result = NULL; DexStringCache targetDescriptorCache; char* name; const char* targetDescriptor; dexStringCacheInit(&targetDescriptorCache); name = dvmCreateCstrFromString(nameObj); createTargetDescriptor(args, &targetDescriptorCache); targetDescriptor = targetDescriptorCache.value; result = findConstructorOrMethodInArray(clazz->directMethodCount, clazz->directMethods, name, targetDescriptor); if (result == NULL) { result = findConstructorOrMethodInArray(clazz->virtualMethodCount, clazz->virtualMethods, name, targetDescriptor); } free(name); dexStringCacheRelease(&targetDescriptorCache); return result; }
/* * Get the named field. */ Object* dvmGetDeclaredField(ClassObject* clazz, StringObject* nameObj) { int i; Object* fieldObj = NULL; char* name = dvmCreateCstrFromString(nameObj); if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField)) dvmInitClass(gDvm.classJavaLangReflectField); for (i = 0; i < clazz->sfieldCount; i++) { Field* field = &clazz->sfields[i]; if (strcmp(name, field->name) == 0) { fieldObj = createFieldObject(field, clazz); break; } } if (fieldObj == NULL) { for (i = 0; i < clazz->ifieldCount; i++) { Field* field = &clazz->ifields[i]; if (strcmp(name, field->name) == 0) { fieldObj = createFieldObject(field, clazz); break; } } } free(name); return fieldObj; }
/* * static void startMethodTracingFd(String traceFileName, FileDescriptor fd, * int bufferSize, int flags, boolean samplingEnabled, int intervalUs) * * Start method trace profiling, sending results to a file descriptor. */ static void Dalvik_dalvik_system_VMDebug_startMethodTracingFd(const u4* args, JValue* pResult) { StringObject* traceFileStr = (StringObject*) args[0]; Object* traceFd = (Object*) args[1]; int bufferSize = args[2]; int flags = args[3]; bool samplingEnabled = args[4]; int intervalUs = args[5]; int origFd = getFileDescriptor(traceFd); if (origFd < 0) RETURN_VOID(); int fd = dup(origFd); if (fd < 0) { dvmThrowExceptionFmt(gDvm.exRuntimeException, "dup(%d) failed: %s", origFd, strerror(errno)); RETURN_VOID(); } char* traceFileName = dvmCreateCstrFromString(traceFileStr); if (traceFileName == NULL) { RETURN_VOID(); } dvmMethodTraceStart(traceFileName, fd, bufferSize, flags, false, samplingEnabled, intervalUs); free(traceFileName); RETURN_VOID(); }
/* * Print the direct stack trace of the given exception to the log. */ static void logStackTraceOf(Object* exception) { const ArrayObject* stackData; StringObject* messageStr; int stackSize; const int* intVals; messageStr = (StringObject*) dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_message); if (messageStr != NULL) { char* cp = dvmCreateCstrFromString(messageStr); LOGI("%s: %s\n", exception->clazz->descriptor, cp); free(cp); } else { LOGI("%s:\n", exception->clazz->descriptor); } stackData = (const ArrayObject*) dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_stackState); if (stackData == NULL) { LOGI(" (no stack trace data found)\n"); return; } stackSize = stackData->length / 2; intVals = (const int*) stackData->contents; dvmLogRawStackTrace(intVals, stackSize); }
static void preloadDexCachesStringsVisitor(void* addr, u4 threadId, RootType type, void* arg) { StringTable& table = *(StringTable*) arg; StringObject* strObj = *(StringObject**) addr; LOG_FATAL_IF(strObj->clazz != gDvm.classJavaLangString, "Unknown class for supposed string"); char* newStr = dvmCreateCstrFromString(strObj); // ALOGI("VMRuntime.preloadDexCaches interned=%s", newStr); table[newStr] = strObj; free(newStr); }
/* * 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; }
/* * 从指定的DEX文件中加载一个类,使用类加载器去初始化一个类对象 */ 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]; ClassObject* clazz = NULL; DexOrJar* pDexOrJar = (DexOrJar*) cookie; DvmDex* pDvmDex; char* name; char* descriptor; name = dvmCreateCstrFromString(nameObj); descriptor = dvmDotToDescriptor(name); ALOGV("--- Explicit class load '%s' l=%p c=0x%08x", descriptor, loader, 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); Thread* self = dvmThreadSelf(); if (dvmCheckException(self)) { /* * If we threw a "class not found" exception, stifle it, since the * contract in the higher method says we simply return null if * the class is not found. */ Object* excep = dvmGetException(self); if (strcmp(excep->clazz->descriptor, "Ljava/lang/ClassNotFoundException;") == 0 || strcmp(excep->clazz->descriptor, "Ljava/lang/NoClassDefFoundError;") == 0) { dvmClearException(self); } clazz = NULL; } free(descriptor); RETURN_PTR(clazz); }
/* * static void dumpHprofData(String fileName, FileDescriptor fd) * * Cause "hprof" data to be dumped. We can throw an IOException if an * error occurs during file handling. */ static void Dalvik_dalvik_system_VMDebug_dumpHprofData(const u4* args, JValue* pResult) { #ifdef WITH_HPROF StringObject* fileNameStr = (StringObject*) args[0]; Object* fileDescriptor = (Object*) args[1]; char* fileName; int result; /* * Only one of these may be NULL. */ if (fileNameStr == NULL && fileDescriptor == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); RETURN_VOID(); } if (fileNameStr != NULL) { fileName = dvmCreateCstrFromString(fileNameStr); if (fileName == NULL) { /* unexpected -- malloc failure? */ dvmThrowException("Ljava/lang/RuntimeException;", "malloc failure?"); RETURN_VOID(); } } else { fileName = strdup("[fd]"); } int fd = -1; if (fileDescriptor != NULL) { fd = getFileDescriptor(fileDescriptor); if (fd < 0) RETURN_VOID(); } result = hprofDumpHeap(fileName, fd, false); free(fileName); if (result != 0) { /* ideally we'd throw something more specific based on actual failure */ dvmThrowException("Ljava/lang/RuntimeException;", "Failure during heap dump -- check log output for details"); RETURN_VOID(); } #else dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); #endif RETURN_VOID(); }
/* * Utility function when we're evaluating alternative implementations. */ static void badMatch(StringObject* thisStrObj, StringObject* compStrObj, int expectResult, int newResult, const char* compareType) { ArrayObject* thisArray; ArrayObject* compArray; const char* thisStr; const char* compStr; int thisOffset, compOffset, thisCount, compCount; thisCount = dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_COUNT); compCount = dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_COUNT); thisOffset = dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_OFFSET); compOffset = dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_OFFSET); thisArray = (ArrayObject*) dvmGetFieldObject((Object*) thisStrObj, STRING_FIELDOFF_VALUE); compArray = (ArrayObject*) dvmGetFieldObject((Object*) compStrObj, STRING_FIELDOFF_VALUE); thisStr = dvmCreateCstrFromString(thisStrObj); compStr = dvmCreateCstrFromString(compStrObj); ALOGE("%s expected %d got %d", compareType, expectResult, newResult); ALOGE(" this (o=%d l=%d) '%s'", thisOffset, thisCount, thisStr); ALOGE(" comp (o=%d l=%d) '%s'", compOffset, compCount, compStr); dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG, ((const u2*) thisArray->contents) + thisOffset, thisCount*2, kHexDumpLocal); dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG, ((const u2*) compArray->contents) + compOffset, compCount*2, kHexDumpLocal); dvmAbort(); }
/* * public static boolean isDexOptNeeded(String apkName) * throws FileNotFoundException, IOException * * Returns true if the VM believes that the apk/jar file is out of date * and should be passed through "dexopt" again. * * @param fileName the absolute path to the apk/jar file to examine. * @return true if dexopt should be called on the file, false otherwise. * @throws java.io.FileNotFoundException if fileName is not readable, * not a file, or not present. * @throws java.io.IOException if fileName is not a valid apk/jar file or * if problems occur while parsing it. * @throws java.lang.NullPointerException if fileName is null. * @throws dalvik.system.StaleDexCacheError if the optimized dex file * is stale but exists on a read-only partition. */ static void Dalvik_dalvik_system_DexFile_isDexOptNeeded(const u4* args, JValue* pResult) { StringObject* nameObj = (StringObject*) args[0]; char* name; DexCacheStatus status; int result; name = dvmCreateCstrFromString(nameObj); if (name == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); RETURN_VOID(); } if (access(name, R_OK) != 0) { dvmThrowException("Ljava/io/FileNotFoundException;", name); free(name); RETURN_VOID(); } status = dvmDexCacheStatus(name); LOGV("dvmDexCacheStatus(%s) returned %d\n", name, status); result = true; switch (status) { default: //FALLTHROUGH case DEX_CACHE_BAD_ARCHIVE: dvmThrowException("Ljava/io/IOException;", name); result = -1; break; case DEX_CACHE_OK: result = false; break; case DEX_CACHE_STALE: result = true; break; case DEX_CACHE_STALE_ODEX: dvmThrowException("Ldalvik/system/StaleDexCacheError;", name); result = -1; break; } free(name); if (result >= 0) { RETURN_BOOLEAN(result); } else { RETURN_VOID(); } }
/* * static boolean nativeLoad(String filename, ClassLoader loader) * * Load the specified full path as a dynamic library filled with * JNI-compatible methods. */ static void Dalvik_java_lang_Runtime_nativeLoad(const u4* args, JValue* pResult) { StringObject* fileNameObj = (StringObject*) args[0]; Object* classLoader = (Object*) args[1]; char* fileName; int result; if (fileNameObj == NULL) RETURN_INT(false); fileName = dvmCreateCstrFromString(fileNameObj); result = dvmLoadNativeCode(fileName, classLoader); free(fileName); RETURN_INT(result); }
/* * private static String getBootClassPathResource(String name, int index) * * Find a resource with a matching name in a boot class path entry. * * This mimics the previous VM interface, since we're sharing class libraries. */ static void Dalvik_java_lang_VMClassLoader_getBootClassPathResource( const u4* args, JValue* pResult) { StringObject* nameObj = (StringObject*) args[0]; StringObject* result; int idx = args[1]; char* name; name = dvmCreateCstrFromString(nameObj); if (name == NULL) RETURN_PTR(NULL); result = dvmGetBootPathResource(name, idx); free(name); dvmReleaseTrackedAlloc((Object*)result, NULL); RETURN_PTR(result); }
/* * static void startMethodTracingFilename(String traceFileName, int bufferSize, * int flags, boolean samplingEnabled, int intervalUs) * * Start method trace profiling, sending results to a file. */ static void Dalvik_dalvik_system_VMDebug_startMethodTracingFilename(const u4* args, JValue* pResult) { StringObject* traceFileStr = (StringObject*) args[0]; int bufferSize = args[1]; int flags = args[2]; bool samplingEnabled = args[3]; int intervalUs = args[4]; char* traceFileName = dvmCreateCstrFromString(traceFileStr); if (traceFileName == NULL) { RETURN_VOID(); } dvmMethodTraceStart(traceFileName, -1, bufferSize, flags, false, samplingEnabled, intervalUs); free(traceFileName); RETURN_VOID(); }
/* * static Class defineClass(ClassLoader cl, String name, * byte[] data, int offset, int len) * throws ClassFormatError * * Convert an array of bytes to a Class object. */ static void Dalvik_java_lang_VMClassLoader_defineClass(const u4* args, JValue* pResult) { Object* loader = (Object*) args[0]; StringObject* nameObj = (StringObject*) args[1]; const u1* data = (const u1*) args[2]; int offset = args[3]; int len = args[4]; char* name = NULL; name = dvmCreateCstrFromString(nameObj); ALOGE("ERROR: defineClass(%p, %s, %p, %d, %d)", loader, name, data, offset, len); dvmThrowUnsupportedOperationException( "can't load this type of class file"); free(name); RETURN_VOID(); }
/* * static void startMethodTracingNative(String traceFileName, * FileDescriptor fd, int bufferSize, int flags) * * Start method trace profiling. * * If both "traceFileName" and "fd" are null, the result will be sent * directly to DDMS. (The non-DDMS versions of the calls are expected * to enforce non-NULL filenames.) */ static void Dalvik_dalvik_system_VMDebug_startMethodTracingNative(const u4* args, JValue* pResult) { StringObject* traceFileStr = (StringObject*) args[0]; Object* traceFd = (Object*) args[1]; int bufferSize = args[2]; int flags = args[3]; if (bufferSize == 0) { // Default to 8MB per the documentation. bufferSize = 8 * 1024 * 1024; } if (bufferSize < 1024) { dvmThrowException("Ljava/lang/IllegalArgumentException;", NULL); RETURN_VOID(); } char* traceFileName = NULL; if (traceFileStr != NULL) traceFileName = dvmCreateCstrFromString(traceFileStr); int fd = -1; if (traceFd != NULL) { int origFd = getFileDescriptor(traceFd); if (origFd < 0) RETURN_VOID(); fd = dup(origFd); if (fd < 0) { dvmThrowExceptionFmt("Ljava/lang/RuntimeException;", "dup(%d) failed: %s", origFd, strerror(errno)); RETURN_VOID(); } } dvmMethodTraceStart(traceFileName != NULL ? traceFileName : "[DDMS]", fd, bufferSize, flags, (traceFileName == NULL && fd == -1)); free(traceFileName); RETURN_VOID(); }
/* * public static String mapLibraryName(String libname) */ static void Dalvik_java_lang_System_mapLibraryName(const u4* args, JValue* pResult) { StringObject* nameObj = (StringObject*) args[0]; StringObject* result = NULL; char* name; char* mappedName; if (nameObj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); RETURN_VOID(); } name = dvmCreateCstrFromString(nameObj); mappedName = dvmCreateSystemLibraryName(name); if (mappedName != NULL) { result = dvmCreateStringFromCstr(mappedName); dvmReleaseTrackedAlloc((Object*) result, NULL); } free(name); free(mappedName); RETURN_PTR(result); }
/** * 使用jni GetMethodID 方法获取jmethodID 强制转为 Method 的hook 方法 示例 */ static void newTestMethod(const u4* args, JValue* pResult, const Method* method, struct Thread* self) { // args 是原来函数的参数数组, 原来test函数只有一个String型参数 // 并且要注意, 如果是不是static函数, 下标0 是函数所在类的实例obj // 在dvm中Object, jni 中的jobject 和 java 中的 Object类 都不是同一个东西 // String类对应StringObject // 取出参数打印出来看看 StringObject* param1 = NULL; if(dvmIsStaticMethod(method)) param1 = (StringObject*)args[0]; else param1 = (StringObject*)args[1]; LOGD("param1:%s",dvmCreateCstrFromString(param1)); //JValue 是个union ,要返回int 就 pResult->i=1; 返回Object对象就 pResult->l = ojb; // 但是, 在dvm中的Object, jni 中的jobject 和 java 中的 Object类 都不是同一个东西 // 所以, 我们这里使用dvm的函数来创建一个StringObject* pResult->l = dvmCreateStringFromCstr("newTestMethod"); // 一般情况应该使用宏 : RETURN_XXX(result); return; }
/* * Generate a proxy class with the specified name, interfaces, and loader. * "interfaces" is an array of class objects. * * The interpreted code has done all of the necessary checks, e.g. we know * that "interfaces" contains only interface classes. * * On failure we leave a partially-created class object sitting around, * but the garbage collector will take care of it. */ ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces, Object* loader) { int result = -1; char* nameStr = NULL; Method** methods = NULL; ClassObject* newClass = NULL; int i; nameStr = dvmCreateCstrFromString(str); if (nameStr == NULL) { dvmThrowException("Ljava/lang/IllegalArgumentException;", "missing name"); goto bail; } LOGV("+++ Generate proxy class '%s' %p from %d interface classes\n", nameStr, loader, interfaces->length); /* * Characteristics of a Proxy class: * - concrete class, public and final * - superclass is java.lang.reflect.Proxy * - implements all listed interfaces (req'd for instanceof) * - has one method for each method in the interfaces (barring duplicates) * - has one constructor (takes an InvocationHandler arg) * - has overrides for hashCode, equals, and toString (these come first) * - has one field, a reference to the InvocationHandler object * * The idea here is to create a class object and fill in the details * as we would in loadClassFromDex(), and then call dvmLinkClass() to do * all the heavy lifting (notably populating the virtual and interface * method tables). */ /* * Generate a temporary list of virtual methods. */ int methodCount; if (!gatherMethods(interfaces, &methods, &methodCount)) goto bail; /* * Allocate storage for the class object and set some basic fields. */ newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT); if (newClass == NULL) return NULL; DVM_OBJECT_INIT(&newClass->obj, gDvm.unlinkedJavaLangClass); newClass->descriptorAlloc = dvmNameToDescriptor(nameStr); newClass->descriptor = newClass->descriptorAlloc; newClass->accessFlags = ACC_PUBLIC | ACC_FINAL; newClass->super = gDvm.classJavaLangReflectProxy; newClass->primitiveType = PRIM_NOT; newClass->classLoader = loader; #if WITH_HPROF && WITH_HPROF_STACK newClass->hprofSerialNumber = 0; hprofFillInStackTrace(newClass); #endif /* * Add direct method definitions. We have one (the constructor). */ newClass->directMethodCount = 1; newClass->directMethods = (Method*) dvmLinearAlloc(newClass->classLoader, 1 * sizeof(Method)); createConstructor(newClass, &newClass->directMethods[0]); dvmLinearReadOnly(newClass->classLoader, newClass->directMethods); /* * Add virtual method definitions. */ newClass->virtualMethodCount = methodCount; newClass->virtualMethods = (Method*) dvmLinearAlloc(newClass->classLoader, newClass->virtualMethodCount * sizeof(Method)); for (i = 0; i < newClass->virtualMethodCount; i++) { createHandlerMethod(newClass, &newClass->virtualMethods[i],methods[i]); } dvmLinearReadOnly(newClass->classLoader, newClass->virtualMethods); /* * Add interface list. */ int interfaceCount = interfaces->length; ClassObject** ifArray = (ClassObject**) interfaces->contents; newClass->interfaceCount = interfaceCount; newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader, sizeof(ClassObject*) * interfaceCount); for (i = 0; i < interfaceCount; i++) newClass->interfaces[i] = ifArray[i]; dvmLinearReadOnly(newClass->classLoader, newClass->interfaces); /* * The class has one instance field, "protected InvocationHandler h", * which is filled in by the constructor. */ newClass->ifieldCount = 1; newClass->ifields = (InstField*) dvmLinearAlloc(newClass->classLoader, 1 * sizeof(InstField)); InstField* ifield = &newClass->ifields[0]; ifield->field.clazz = newClass; ifield->field.name = "h"; ifield->field.signature = "Ljava/lang/reflect/InvocationHandler;"; ifield->field.accessFlags = ACC_PROTECTED; ifield->byteOffset = -1; /* set later */ dvmLinearReadOnly(newClass->classLoader, newClass->ifields); /* * Everything is ready. See if the linker will lap it up. */ newClass->status = CLASS_LOADED; if (!dvmLinkClass(newClass, true)) { LOGI("Proxy class link failed\n"); goto bail; } /* * All good. Add it to the hash table. We should NOT see a collision * here; if we do, it means the caller has screwed up and provided us * with a duplicate name. */ if (!dvmAddClassToHash(newClass)) { LOGE("ERROR: attempted to generate %s more than once\n", newClass->descriptor); goto bail; } result = 0; bail: free(nameStr); free(methods); if (result != 0) { /* must free innards explicitly if we didn't finish linking */ dvmFreeClassInnards(newClass); newClass = NULL; dvmThrowException("Ljava/lang/RuntimeException;", NULL); } /* this allows the GC to free it */ dvmReleaseTrackedAlloc((Object*) newClass, NULL); return newClass; }
/* * static boolean cacheRegisterMap(String classAndMethodDescr) * * If the specified class is loaded, and the named method exists, ensure * that the method's register map is ready for use. If the class/method * cannot be found, nothing happens. * * This can improve the zygote's sharing of compressed register maps. Do * this after class preloading. * * Returns true if the register map is cached and ready, either as a result * of this call or earlier activity. Returns false if the class isn't loaded, * if the method couldn't be found, or if the method has no register map. * * (Uncomment logs in dvmGetExpandedRegisterMap0() to gather stats.) */ static void Dalvik_dalvik_system_VMDebug_cacheRegisterMap(const u4* args, JValue* pResult) { StringObject* classAndMethodDescStr = (StringObject*) args[0]; ClassObject* clazz; bool result = false; if (classAndMethodDescStr == NULL) { dvmThrowNullPointerException("classAndMethodDesc == null"); RETURN_VOID(); } char* classAndMethodDesc = NULL; /* * Pick the string apart. We have a local copy, so just modify it * in place. */ classAndMethodDesc = dvmCreateCstrFromString(classAndMethodDescStr); char* methodName = strchr(classAndMethodDesc, '.'); if (methodName == NULL) { dvmThrowRuntimeException("method name not found in string"); RETURN_VOID(); } *methodName++ = '\0'; char* methodDescr = strchr(methodName, ':'); if (methodDescr == NULL) { dvmThrowRuntimeException("method descriptor not found in string"); RETURN_VOID(); } *methodDescr++ = '\0'; //ALOGD("GOT: %s %s %s", classAndMethodDesc, methodName, methodDescr); /* * Find the class, but only if it's already loaded. */ clazz = dvmLookupClass(classAndMethodDesc, NULL, false); if (clazz == NULL) { ALOGD("Class %s not found in bootstrap loader", classAndMethodDesc); goto bail; } Method* method; /* * Find the method, which could be virtual or direct, defined directly * or inherited. */ if (methodName[0] == '<') { /* * Constructor or class initializer. Only need to examine the * "direct" list, and don't need to search up the class hierarchy. */ method = dvmFindDirectMethodByDescriptor(clazz, methodName, methodDescr); } else { /* * Try both lists, and scan up the tree. */ method = dvmFindVirtualMethodHierByDescriptor(clazz, methodName, methodDescr); if (method == NULL) { method = dvmFindDirectMethodHierByDescriptor(clazz, methodName, methodDescr); } } if (method != NULL) { /* * Got it. See if there's a register map here. */ const RegisterMap* pMap; pMap = dvmGetExpandedRegisterMap(method); if (pMap == NULL) { ALOGV("No map for %s.%s %s", classAndMethodDesc, methodName, methodDescr); } else { ALOGV("Found map %s.%s %s", classAndMethodDesc, methodName, methodDescr); result = true; } } else { ALOGV("Unable to find %s.%s %s", classAndMethodDesc, methodName, methodDescr); } bail: free(classAndMethodDesc); RETURN_BOOLEAN(result); }
/* * private static int openDexFile(String sourceName, String outputName, * int flags) throws IOException * * Open a DEX file, returning a pointer to our internal data structure. * * "sourceName" should point to the "source" jar or DEX file. * * If "outputName" is NULL, the DEX code will automatically find the * "optimized" version in the cache directory, creating it if necessary. * If it's non-NULL, the specified file will be used instead. * * TODO: at present we will happily open the same file more than once. * To optimize this away we could search for existing entries in the hash * table and refCount them. Requires atomic ops or adding "synchronized" * to the non-native code that calls here. */ static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args, JValue* pResult) { StringObject* sourceNameObj = (StringObject*) args[0]; StringObject* outputNameObj = (StringObject*) args[1]; int flags = args[2]; DexOrJar* pDexOrJar = NULL; JarFile* pJarFile; RawDexFile* pRawDexFile; char* sourceName; char* outputName; if (sourceNameObj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); RETURN_VOID(); } sourceName = dvmCreateCstrFromString(sourceNameObj); if (outputNameObj != NULL) outputName = dvmCreateCstrFromString(outputNameObj); else outputName = NULL; /* * We have to deal with the possibility that somebody might try to * open one of our bootstrap class DEX files. The set of dependencies * will be different, and hence the results of optimization might be * different, which means we'd actually need to have two versions of * the optimized DEX: one that only knows about part of the boot class * path, and one that knows about everything in it. The latter might * optimize field/method accesses based on a class that appeared later * in the class path. * * We can't let the user-defined class loader open it and start using * the classes, since the optimized form of the code skips some of * the method and field resolution that we would ordinarily do, and * we'd have the wrong semantics. * * We have to reject attempts to manually open a DEX file from the boot * class path. The easiest way to do this is by filename, which works * out because variations in name (e.g. "/system/framework/./ext.jar") * result in us hitting a different dalvik-cache entry. It's also fine * if the caller specifies their own output file. */ if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) { LOGW("Refusing to reopen boot DEX '%s'\n", sourceName); dvmThrowException("Ljava/io/IOException;", "Re-opening BOOTCLASSPATH DEX files is not allowed"); free(sourceName); RETURN_VOID(); } /* * Try to open it directly as a DEX. If that fails, try it as a Zip * with a "classes.dex" inside. */ if (dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) { LOGV("Opening DEX file '%s' (DEX)\n", sourceName); pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); pDexOrJar->isDex = true; pDexOrJar->pRawDexFile = pRawDexFile; } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) { LOGV("Opening DEX file '%s' (Jar)\n", sourceName); pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); pDexOrJar->isDex = false; pDexOrJar->pJarFile = pJarFile; } else { LOGV("Unable to open DEX file '%s'\n", sourceName); dvmThrowException("Ljava/io/IOException;", "unable to open DEX file"); } if (pDexOrJar != NULL) { pDexOrJar->fileName = sourceName; /* add to hash table */ u4 hash = dvmComputeUtf8Hash(sourceName); void* result; dvmHashTableLock(gDvm.userDexFiles); result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, hashcmpDexOrJar, true); dvmHashTableUnlock(gDvm.userDexFiles); if (result != pDexOrJar) { LOGE("Pointer has already been added?\n"); dvmAbort(); } pDexOrJar->okayToFree = true; } else free(sourceName); RETURN_PTR(pDexOrJar); }
/* * private static Class defineClass(String name, ClassLoader loader, * int cookie, ProtectionDomain pd) * * Load a class from a DEX file. This is roughly equivalent to defineClass() * in a regular VM -- it's invoked by the class loader to cause the * creation of a specific class. The difference is that the search for and * reading of the bytes is done within the VM. * * The class name is a "binary name", e.g. "java.lang.String". * * Returns a null pointer with no exception if the class was not found. * Throws an exception on other failures. */ 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); Thread* self = dvmThreadSelf(); if (dvmCheckException(self)) { /* * If we threw a "class not found" exception, stifle it, since the * contract in the higher method says we simply return null if * the class is not found. */ Object* excep = dvmGetException(self); if (strcmp(excep->clazz->descriptor, "Ljava/lang/ClassNotFoundException;") == 0 || strcmp(excep->clazz->descriptor, "Ljava/lang/NoClassDefFoundError;") == 0) { dvmClearException(self); } clazz = NULL; } /* * Set the ProtectionDomain -- do we need this to happen before we * link the class and make it available? If so, we need to pass it * through dvmDefineClass (and figure out some other * stuff, like where it comes from for bootstrap classes). */ if (clazz != NULL) { //LOGI("SETTING pd '%s' to %p\n", clazz->descriptor, pd); dvmSetFieldObject((Object*) clazz, gDvm.offJavaLangClass_pd, pd); } free(descriptor); RETURN_PTR(clazz); }
/* * private static int openDexFile(String sourceName, String outputName, * int flags) throws IOException * * Open a DEX file, returning a pointer to our internal data structure. * 打开一个DEX文件,返回一个指向我们内部数据结构体 * "sourceName" should point to the "source" jar or DEX file. * 'sourceName'应该是指jar或dex文件 * * If "outputName" is NULL, the DEX code will automatically find the * "optimized" version in the cache directory, creating it if necessary. * If it's non-NULL, the specified file will be used instead. * * TODO: at present we will happily open the same file more than once. * To optimize this away we could search for existing entries in the hash * table and refCount them. Requires atomic ops or adding "synchronized" * to the non-native code that calls here. * * TODO: should be using "long" for a pointer. */ static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args, JValue* pResult) { StringObject* sourceNameObj = (StringObject*) args[0]; StringObject* outputNameObj = (StringObject*) args[1]; DexOrJar* pDexOrJar = NULL; JarFile* pJarFile; RawDexFile* pRawDexFile; char* sourceName; char* outputName; if (sourceNameObj == NULL) { dvmThrowNullPointerException("sourceName == null"); RETURN_VOID(); } /*转换java数据类型成为c数据类型*/ sourceName = dvmCreateCstrFromString(sourceNameObj); if (outputNameObj != NULL) outputName = dvmCreateCstrFromString(outputNameObj); else outputName = NULL; /* * We have to deal with the possibility that somebody might try to * open one of our bootstrap class DEX files. The set of dependencies * will be different, and hence the results of optimization might be * different, which means we'd actually need to have two versions of * the optimized DEX: one that only knows about part of the boot class * path, and one that knows about everything in it. The latter might * optimize field/method accesses based on a class that appeared later * in the class path. * * We can't let the user-defined class loader open it and start using * the classes, since the optimized form of the code skips some of * the method and field resolution that we would ordinarily do, and * we'd have the wrong semantics. * * We have to reject attempts to manually open a DEX file from the boot * class path. The easiest way to do this is by filename, which works * out because variations in name (e.g. "/system/framework/./ext.jar") * result in us hitting a different dalvik-cache entry. It's also fine * if the caller specifies their own output file. */ if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) { ALOGW("Refusing to reopen boot DEX '%s'", sourceName); dvmThrowIOException( "Re-opening BOOTCLASSPATH DEX files is not allowed"); free(sourceName); free(outputName); RETURN_VOID(); } /* * Try to open it directly as a DEX if the name ends with ".dex". * If that fails (or isn't tried in the first place), try it as a * Zip with a "classes.dex" inside. */ /*判断扩展后缀名.eg .dex或.jar*/ if (hasDexExtension(sourceName) && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) { ALOGV("Opening DEX file '%s' (DEX)", sourceName); pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); pDexOrJar->isDex = true; pDexOrJar->pRawDexFile = pRawDexFile; pDexOrJar->pDexMemory = NULL; } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) { ALOGV("Opening DEX file '%s' (Jar)", sourceName); pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); pDexOrJar->isDex = false; pDexOrJar->pJarFile = pJarFile; pDexOrJar->pDexMemory = NULL; } else { ALOGV("Unable to open DEX file '%s'", sourceName); dvmThrowIOException("unable to open DEX file"); } if (pDexOrJar != NULL) { pDexOrJar->fileName = sourceName; addToDexFileTable(pDexOrJar); } else { free(sourceName); } RETURN_PTR(pDexOrJar); }
/* * Dump a summary of an array of references to the log file. * * This is used to dump the contents of ReferenceTable and IndirectRefTable * structs. */ void dvmDumpReferenceTableContents(Object* const* refs, size_t count, const char* descr) { LOGW("%s reference table (%p) dump:", descr, refs); if (count == 0) { LOGW(" (empty)"); return; } // Dump the most recent N entries. const size_t kLast = 10; int first = count - kLast; if (first < 0) { first = 0; } LOGW(" Last %d entries (of %d):", (count - first), count); for (int idx = count - 1; idx >= first; --idx) { const Object* ref = refs[idx]; if (ref == NULL) { continue; } if (ref == kClearedJniWeakGlobal) { LOGW(" %5d: cleared jweak", idx); continue; } if (ref->clazz == NULL) { // should only be possible right after a plain dvmMalloc(). size_t size = dvmObjectSizeInHeap(ref); LOGW(" %5d: %p (raw) (%zd bytes)", idx, ref, size); continue; } std::string className(dvmHumanReadableType(ref)); std::string extras; size_t elems = getElementCount(ref); if (elems != 0) { StringAppendF(&extras, " (%zd elements)", elems); } else if (ref->clazz == gDvm.classJavaLangString) { const StringObject* str = reinterpret_cast<const StringObject*>(ref); extras += " \""; size_t count = 0; char* s = dvmCreateCstrFromString(str); char* p = s; for (; *p && count < 16; ++p, ++count) { extras += *p; } if (*p == 0) { extras += "\""; } else { StringAppendF(&extras, "... (%d chars)", str->length()); } free(s); } LOGW(" %5d: %p %s%s", idx, ref, className.c_str(), extras.c_str()); } // Make a copy of the table, and sort it. Object** tableCopy = (Object**)malloc(sizeof(Object*) * count); if (tableCopy == NULL) { LOGE("Unable to copy table with %d elements", count); return; } memcpy(tableCopy, refs, sizeof(Object*) * count); qsort(tableCopy, count, sizeof(Object*), compareObject); refs = tableCopy; // use sorted list // Remove any uninteresting stuff from the list. The sort moved them all to the end. while (count > 0 && refs[count-1] == NULL) { --count; } while (count > 0 && refs[count-1] == kClearedJniWeakGlobal) { --count; } if (count == 0) { return; } // Dump a summary of the whole table. LOGW(" Summary:"); size_t equiv, identical; equiv = identical = 0; size_t idx; size_t elems; for (idx = 1; idx < count; idx++) { elems = getElementCount(refs[idx-1]); if (refs[idx] == refs[idx-1]) { // same reference, added more than once. identical++; } else if (refs[idx]->clazz == refs[idx-1]->clazz && getElementCount(refs[idx]) == elems) { // same class / element count, different object. equiv++; } else { // different class. logSummaryLine(refs[idx-1], elems, identical, equiv); equiv = identical = 0; } } // Handle the last entry (everything above outputs refs[i-1]). elems = getElementCount(refs[idx-1]); logSummaryLine(refs[count-1], elems, identical, equiv); free(tableCopy); }