/* * Checks the dependencies of the dex cache file corresponding * to the jar file at the absolute path "fileName". */ DexCacheStatus dvmDexCacheStatus(const char *fileName) { ZipArchive archive; char* cachedName = NULL; int fd; DexCacheStatus result = DEX_CACHE_ERROR; ZipEntry entry; /* Always treat elements of the bootclasspath as up-to-date. * The fact that interpreted code is running at all means that this * should be true. */ if (dvmClassPathContains(gDvm.bootClassPath, fileName)) { return DEX_CACHE_OK; } //TODO: match dvmJarFileOpen()'s logic. Not super-important // (the odex-first logic is only necessary for dexpreopt) // but it would be nice to be consistent. /* Try to find the dex file inside of the archive. */ if (dexZipOpenArchive(fileName, &archive) != 0) { return DEX_CACHE_BAD_ARCHIVE; } entry = dexZipFindEntry(&archive, kDexInJarName); if (entry != NULL) { bool newFile = false; /* * See if there's an up-to-date copy of the optimized dex * in the cache, but don't create one if there isn't. */ ALOGV("dvmDexCacheStatus: Checking cache for %s", fileName); cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName); if (cachedName == NULL) return DEX_CACHE_BAD_ARCHIVE; fd = dvmOpenCachedDexFile(fileName, cachedName, dexGetZipEntryModTime(&archive, entry), dexGetZipEntryCrc32(&archive, entry), /*isBootstrap=*/false, &newFile, /*createIfMissing=*/false); ALOGV("dvmOpenCachedDexFile returned fd %d", fd); if (fd < 0) { result = DEX_CACHE_STALE; goto bail; } /* dvmOpenCachedDexFile locks the file as a side-effect. * Unlock and close it. */ if (!dvmUnlockCachedDexFile(fd)) { /* uh oh -- this process needs to exit or we'll wedge the system */ ALOGE("Unable to unlock DEX file"); goto bail; } /* When createIfMissing is false, dvmOpenCachedDexFile() only * returns a valid fd if the cache file is up-to-date. */ } else { /* * There's no dex file in the jar file. See if there's an * optimized dex file living alongside the jar. */ fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); if (fd < 0) { ALOGI("Zip is good, but no %s inside, and no .odex " "file in the same directory", kDexInJarName); result = DEX_CACHE_BAD_ARCHIVE; goto bail; } ALOGV("Using alternate file (odex) for %s ...", fileName); if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { ALOGE("%s odex has stale dependencies", fileName); ALOGE("odex source not available -- failing"); result = DEX_CACHE_STALE_ODEX; goto bail; } else { ALOGV("%s odex has good dependencies", fileName); } } result = DEX_CACHE_OK; bail: dexZipCloseArchive(&archive); free(cachedName); if (fd >= 0) { close(fd); } return result; }
DexCacheStatus dvmDexCacheStatus(const char *fileName) { ZipArchive archive; char* cachedName = NULL; int fd; DexCacheStatus result = DEX_CACHE_ERROR; ZipEntry entry; if (dvmClassPathContains(gDvm.bootClassPath, fileName)) { return DEX_CACHE_OK; } if (dexZipOpenArchive(fileName, &archive) != 0) { return DEX_CACHE_BAD_ARCHIVE; } entry = dexZipFindEntry(&archive, kDexInJarName); if (entry != NULL) { bool newFile = false; ALOGV("dvmDexCacheStatus: Checking cache for %s", fileName); cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName); if (cachedName == NULL) return DEX_CACHE_BAD_ARCHIVE; fd = dvmOpenCachedDexFile(fileName, cachedName, dexGetZipEntryModTime(&archive, entry), dexGetZipEntryCrc32(&archive, entry), /*isBootstrap=*/false, &newFile, /*createIfMissing=*/false); ALOGV("dvmOpenCachedDexFile returned fd %d", fd); if (fd < 0) { result = DEX_CACHE_STALE; goto bail; } if (!dvmUnlockCachedDexFile(fd)) { /* uh oh -- this process needs to exit or we'll wedge the system */ ALOGE("Unable to unlock DEX file"); goto bail; } } else { fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); if (fd < 0) { ALOGI("Zip is good, but no %s inside, and no .odex " "file in the same directory", kDexInJarName); result = DEX_CACHE_BAD_ARCHIVE; goto bail; } ALOGV("Using alternate file (odex) for %s ...", fileName); if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { ALOGE("%s odex has stale dependencies", fileName); ALOGE("odex source not available -- failing"); result = DEX_CACHE_STALE_ODEX; goto bail; } else { ALOGV("%s odex has good dependencies", fileName); } } result = DEX_CACHE_OK; bail: dexZipCloseArchive(&archive); free(cachedName); if (fd >= 0) { close(fd); } return 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 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); }