/* * Close a Jar file and free the struct. */ void dvmJarFileFree(JarFile* pJarFile) { if (pJarFile == NULL) return; dvmDexFileFree(pJarFile->pDvmDex); dexZipCloseArchive(&pJarFile->archive); free(pJarFile->cacheFileName); free(pJarFile); }
/* * Extract "classes.dex" from archive file. * * If "quiet" is set, don't report common errors. */ UnzipToFileResult dexUnzipToFile(const char* zipFileName, const char* outFileName, bool quiet) { UnzipToFileResult result = kUTFRSuccess; static const char* kFileToExtract = "classes.dex"; ZipArchive archive; ZipEntry entry; bool unlinkOnFailure = false; int fd = -1; if (dexZipOpenArchive(zipFileName, &archive) != 0) { if (!quiet) { fprintf(stderr, "Unable to open '%s' as zip archive\n", zipFileName); } result = kUTFRNotZip; goto bail; } fd = open(outFileName, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd < 0) { fprintf(stderr, "Unable to create output file '%s': %s\n", outFileName, strerror(errno)); result = kUTFROutputFileProblem; goto bail; } unlinkOnFailure = true; entry = dexZipFindEntry(&archive, kFileToExtract); if (entry == NULL) { if (!quiet) { fprintf(stderr, "Unable to find '%s' in '%s'\n", kFileToExtract, zipFileName); } result = kUTFRNoClassesDex; goto bail; } if (dexZipExtractEntryToFile(&archive, entry, fd) != 0) { fprintf(stderr, "Extract of '%s' from '%s' failed\n", kFileToExtract, zipFileName); result = kUTFRBadZip; goto bail; } bail: if (fd >= 0) close(fd); if (unlinkOnFailure && result != kUTFRSuccess) unlink(outFileName); dexZipCloseArchive(&archive); return result; }
/* * Prepare to access a ZipArchive in an open file descriptor. */ int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive) { MemMapping map; int err; map.addr = NULL; memset(pArchive, 0, sizeof(*pArchive)); pArchive->mFd = fd; if (sysMapFileInShmem(pArchive->mFd, &map) != 0) { err = -1; LOGW("Map of '%s' failed\n", debugFileName); goto bail; } if (map.length < kEOCDLen) { err = -1; LOGV("File '%s' too small to be zip (%zd)\n", debugFileName,map.length); goto bail; } if (!parseZipArchive(pArchive, &map)) { err = -1; LOGV("Parsing '%s' failed\n", debugFileName); goto bail; } /* success */ err = 0; sysCopyMap(&pArchive->mMap, &map); map.addr = NULL; bail: if (err != 0) dexZipCloseArchive(pArchive); if (map.addr != NULL) sysReleaseShmem(&map); return err; }
/* * Prepare to access a ZipArchive through an open file descriptor. * * On success, we fill out the contents of "pArchive" and return 0. */ int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive) { int result = -1; memset(pArchive, 0, sizeof(*pArchive)); pArchive->mFd = fd; if (mapCentralDirectory(fd, debugFileName, pArchive) != 0) goto bail; if (parseZipArchive(pArchive) != 0) { LOGV("Zip: parsing '%s' failed\n", debugFileName); goto bail; } /* success */ result = 0; bail: if (result != 0) dexZipCloseArchive(pArchive); return result; }
/* * 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; }
/* * Open a Jar file. It's okay if it's just a Zip archive without all of * the Jar trimmings, but we do insist on finding "classes.dex" inside * or an appropriately-named ".odex" file alongside. * * If "isBootstrap" is not set, the optimizer/verifier regards this DEX as * being part of a different class loader. */ int dvmJarFileOpen(const char* fileName, const char* odexOutputName, JarFile** ppJarFile, bool isBootstrap) { /* * TODO: This function has been duplicated and modified to become * dvmRawDexFileOpen() in RawDexFile.c. This should be refactored. */ ZipArchive archive; DvmDex* pDvmDex = NULL; char* cachedName = NULL; bool archiveOpen = false; bool locked = false; int fd = -1; int result = -1; /* Even if we're not going to look at the archive, we need to * open it so we can stuff it into ppJarFile. */ if (dexZipOpenArchive(fileName, &archive) != 0) goto bail; archiveOpen = true; /* If we fork/exec into dexopt, don't let it inherit the archive's fd. */ dvmSetCloseOnExec(dexZipGetArchiveFd(&archive)); /* First, look for a ".odex" alongside the jar file. It will * have the same name/path except for the extension. */ fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); if (fd >= 0) { ALOGV("Using alternate file (odex) for %s ...", fileName); if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { ALOGE("%s odex has stale dependencies", fileName); free(cachedName); cachedName = NULL; close(fd); fd = -1; goto tryArchive; } else { ALOGV("%s odex has good dependencies", fileName); //TODO: make sure that the .odex actually corresponds // to the classes.dex inside the archive (if present). // For typical use there will be no classes.dex. } } else { ZipEntry entry; tryArchive: /* * Pre-created .odex absent or stale. Look inside the jar for a * "classes.dex". */ entry = dexZipFindEntry(&archive, kDexInJarName); if (entry != NULL) { bool newFile = false; /* * We've found the one we want. See if there's an up-to-date copy * in the cache. * * On return, "fd" will be seeked just past the "opt" header. * * If a stale .odex file is present and classes.dex exists in * the archive, this will *not* return an fd pointing to the * .odex file; the fd will point into dalvik-cache like any * other jar. */ if (odexOutputName == NULL) { cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName); if (cachedName == NULL) goto bail; } else { cachedName = strdup(odexOutputName); } ALOGV("dvmJarFileOpen: Checking cache for %s (%s)", fileName, cachedName); fd = dvmOpenCachedDexFile(fileName, cachedName, dexGetZipEntryModTime(&archive, entry), dexGetZipEntryCrc32(&archive, entry), isBootstrap, &newFile, /*createIfMissing=*/true); if (fd < 0) { ALOGI("Unable to open or create cache for %s (%s)", fileName, cachedName); goto bail; } locked = true; /* * If fd points to a new file (because there was no cached version, * or the cached version was stale), generate the optimized DEX. * The file descriptor returned is still locked, and is positioned * just past the optimization header. */ if (newFile) { u8 startWhen, extractWhen, endWhen; bool result; off_t dexOffset; dexOffset = lseek(fd, 0, SEEK_CUR); result = (dexOffset > 0); if (result) { startWhen = dvmGetRelativeTimeUsec(); result = dexZipExtractEntryToFile(&archive, entry, fd) == 0; extractWhen = dvmGetRelativeTimeUsec(); } if (result) { result = dvmOptimizeDexFile(fd, dexOffset, dexGetZipEntryUncompLen(&archive, entry), fileName, dexGetZipEntryModTime(&archive, entry), dexGetZipEntryCrc32(&archive, entry), isBootstrap); } if (!result) { ALOGE("Unable to extract+optimize DEX from '%s'", fileName); goto bail; } endWhen = dvmGetRelativeTimeUsec(); ALOGD("DEX prep '%s': unzip in %dms, rewrite %dms", fileName, (int) (extractWhen - startWhen) / 1000, (int) (endWhen - extractWhen) / 1000); } } else { ALOGI("Zip is good, but no %s inside, and no valid .odex " "file in the same directory", kDexInJarName); goto bail; } } /* * Map the cached version. This immediately rewinds the fd, so it * doesn't have to be seeked anywhere in particular. */ if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) { ALOGI("Unable to map %s in %s", kDexInJarName, fileName); goto bail; } if (locked) { /* unlock the fd */ if (!dvmUnlockCachedDexFile(fd)) { /* uh oh -- this process needs to exit or we'll wedge the system */ ALOGE("Unable to unlock DEX file"); goto bail; } locked = false; } ALOGV("Successfully opened '%s' in '%s'", kDexInJarName, fileName); *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile)); (*ppJarFile)->archive = archive; (*ppJarFile)->cacheFileName = cachedName; (*ppJarFile)->pDvmDex = pDvmDex; cachedName = NULL; // don't free it below result = 0; bail: /* clean up, closing the open file */ if (archiveOpen && result != 0) dexZipCloseArchive(&archive); free(cachedName); if (fd >= 0) { if (locked) (void) dvmUnlockCachedDexFile(fd); close(fd); } return result; }
/* * Extract "classes.dex" from zipFd into "cacheFd", leaving a little space * up front for the DEX optimization header. */ static int extractAndProcessZip(int zipFd, int cacheFd, const char* debugFileName, bool isBootstrap, const char* bootClassPath, const char* dexoptFlagStr) { ZipArchive zippy; // 用于描述ZIP压缩文件的数据结构 ZipEntry zipEntry; // 用于表示一个ZIP入口 size_t uncompLen; long modWhen, crc32; off_t dexOffset; // 用于表示在Odex文件中,原Dex文件的起始地址 int err; int result = -1; int dexoptFlags = 0; /* bit flags, from enum DexoptFlags */ // 设置默认的优化模式 DexClassVerifyMode verifyMode = VERIFY_MODE_ALL; DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED; memset(&zippy, 0, sizeof(zippy)); /* make sure we're still at the start of an empty file */ if (lseek(cacheFd, 0, SEEK_END) != 0) { ALOGE("DexOptZ: new cache file '%s' is not empty", debugFileName); goto bail; } /* * Write a skeletal DEX optimization header. We want the classes.dex * to come just after it. */ err = dexOptCreateEmptyHeader(cacheFd); if (err != 0) goto bail; /* record the file position so we can get back here later */ // 取得Odex文件中原Dex文件的起始位置,实际就是一个Odex文件头部的长度 dexOffset = lseek(cacheFd, 0, SEEK_CUR); if (dexOffset < 0) goto bail; /* * Open the zip archive, find the DEX entry. */ if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) { ALOGW("DexOptZ: unable to open zip archive '%s'", debugFileName); goto bail; } // 获取目标Dex文件的解压入口 zipEntry = dexZipFindEntry(&zippy, kClassesDex); if (zipEntry == NULL) { ALOGW("DexOptZ: zip archive '%s' does not include %s", debugFileName, kClassesDex); goto bail; } /* * Extract some info about the zip entry. */ if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL, &modWhen, &crc32) != 0) { ALOGW("DexOptZ: zip archive GetEntryInfo failed on %s", debugFileName); goto bail; } uncompLen = uncompLen; modWhen = modWhen; crc32 = crc32; /* * Extract the DEX data into the cache file at the current offset. * 从ZIP文件将目标Dex文件解压出来,并写入cacheFd所指文件,此时cacheFd所指文件非空, * 包括一个Odex文件头部加上一个原始的Dex文件 */ if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0) { ALOGW("DexOptZ: extraction of %s from %s failed", kClassesDex, debugFileName); goto bail; } /* Parse the options. */ /* * 入口参数dexoptFlagStr,对验证优化需求进行分析,dexoptFlagStr实际上是一个字符串, * 记录了验证优化选项 */ if (dexoptFlagStr[0] != '\0') { const char* opc; const char* val; // 设置验证模式 opc = strstr(dexoptFlagStr, "v="); /* verification */ if (opc != NULL) { switch (*(opc+2)) { case 'n': verifyMode = VERIFY_MODE_NONE; break; case 'r': verifyMode = VERIFY_MODE_REMOTE; break; case 'a': verifyMode = VERIFY_MODE_ALL; break; default: break; } } // 设置优化模式 opc = strstr(dexoptFlagStr, "o="); /* optimization */ if (opc != NULL) { switch (*(opc+2)) { case 'n': dexOptMode = OPTIMIZE_MODE_NONE; break; case 'v': dexOptMode = OPTIMIZE_MODE_VERIFIED; break; case 'a': dexOptMode = OPTIMIZE_MODE_ALL; break; case 'f': dexOptMode = OPTIMIZE_MODE_FULL; break; default: break; } } opc = strstr(dexoptFlagStr, "m=y"); /* register map */ if (opc != NULL) { dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS; } opc = strstr(dexoptFlagStr, "u="); /* uniprocessor target */ if (opc != NULL) { switch (*(opc+2)) { case 'y': dexoptFlags |= DEXOPT_UNIPROCESSOR; break; case 'n': dexoptFlags |= DEXOPT_SMP; break; default: break; } } } /* * Prep the VM and perform the optimization. * 完成了原Dex文件的提取以及验证优化选项的设置,即可以开始真正的优化工作,需要 * 初始化一个虚拟机专门用于验证优化工作 */ if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, dexoptFlags) != 0) { ALOGE("DexOptZ: VM init failed"); goto bail; } //vmStarted = 1; /* do the optimization */ if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName, modWhen, crc32, isBootstrap)) { ALOGE("Optimization failed"); goto bail; } /* we don't shut the VM down -- process is about to exit */ result = 0; bail: dexZipCloseArchive(&zippy); 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; }
int dvmJarFileOpen(const char* fileName, const char* odexOutputName, JarFile** ppJarFile, bool isBootstrap) { ZipArchive archive; DvmDex* pDvmDex = NULL; char* cachedName = NULL; bool archiveOpen = false; bool locked = false; int fd = -1; int result = -1; if (dexZipOpenArchive(fileName, &archive) != 0) goto bail; archiveOpen = true; dvmSetCloseOnExec(dexZipGetArchiveFd(&archive)); fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); if (fd >= 0) { ALOGV("Using alternate file (odex) for %s ...", fileName); if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) { ALOGE("%s odex has stale dependencies", fileName); free(cachedName); cachedName = NULL; close(fd); fd = -1; goto tryArchive; } else { ALOGV("%s odex has good dependencies", fileName); } } else { ZipEntry entry; tryArchive: entry = dexZipFindEntry(&archive, kDexInJarName); if (entry != NULL) { bool newFile = false; if (odexOutputName == NULL) { cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName); if (cachedName == NULL) goto bail; } else { cachedName = strdup(odexOutputName); } ALOGV("dvmJarFileOpen: Checking cache for %s (%s)", fileName, cachedName); fd = dvmOpenCachedDexFile(fileName, cachedName, dexGetZipEntryModTime(&archive, entry), dexGetZipEntryCrc32(&archive, entry), isBootstrap, &newFile, /*createIfMissing=*/true); if (fd < 0) { ALOGI("Unable to open or create cache for %s (%s)", fileName, cachedName); goto bail; } locked = true; /* Generate the optimized DEX. */ if (newFile) { u8 startWhen, extractWhen, endWhen; bool result; off_t dexOffset; dexOffset = lseek(fd, 0, SEEK_CUR); result = (dexOffset > 0); if (result) { startWhen = dvmGetRelativeTimeUsec(); result = dexZipExtractEntryToFile(&archive, entry, fd) == 0; extractWhen = dvmGetRelativeTimeUsec(); } if (result) { result = dvmOptimizeDexFile(fd, dexOffset, dexGetZipEntryUncompLen(&archive, entry), fileName, dexGetZipEntryModTime(&archive, entry), dexGetZipEntryCrc32(&archive, entry), isBootstrap); } if (!result) { ALOGE("Unable to extract+optimize DEX from '%s'", fileName); goto bail; } endWhen = dvmGetRelativeTimeUsec(); ALOGD("DEX prep '%s': unzip in %dms, rewrite %dms", fileName, (int) (extractWhen - startWhen) / 1000, (int) (endWhen - extractWhen) / 1000); } } else { ALOGI("Zip is good, but no %s inside, and no valid .odex " "file in the same directory", kDexInJarName); goto bail; } } if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) { ALOGI("Unable to map %s in %s", kDexInJarName, fileName); goto bail; } if (locked) { /* unlock the fd */ if (!dvmUnlockCachedDexFile(fd)) { /* This process needs to exit or we'll wedge the system O_O */ ALOGE("Unable to unlock DEX file"); goto bail; } locked = false; } ALOGV("Successfully opened '%s' in '%s'", kDexInJarName, fileName); *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile)); (*ppJarFile)->archive = archive; (*ppJarFile)->cacheFileName = cachedName; (*ppJarFile)->pDvmDex = pDvmDex; cachedName = NULL; // don't free it below result = 0; bail: if (archiveOpen && result != 0) dexZipCloseArchive(&archive); free(cachedName); if (fd >= 0) { if (locked) (void) dvmUnlockCachedDexFile(fd); close(fd); } return result; }