/* * 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. */ /* * Open an unoptimized DEX file. */ #include "Dalvik.h" #include "libdex/OptInvocation.h" #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> /* * Copy the given number of bytes from one fd to another, first * seeking the source fd to the start of the file. */ static int copyFileToFile(int destFd, int srcFd, size_t size) { if (lseek(srcFd, 0, SEEK_SET) != 0) { ALOGE("lseek failure: %s", strerror(errno)); return -1; } return sysCopyFileToFile(destFd, srcFd, size); } /* * Get the modification time and size in bytes for the given fd. */ static int getModTimeAndSize(int fd, u4* modTime, size_t* size) { struct stat buf; int result = fstat(fd, &buf); if (result < 0) { ALOGE("Unable to determine mod time: %s", strerror(errno)); return -1; } *modTime = (u4) buf.st_mtime; *size = (size_t) buf.st_size; assert((size_t) buf.st_size == buf.st_size); return 0; } /* * Verify the dex file magic number, and get the adler32 checksum out * of the given fd, which is presumed to be a reference to a dex file * with the cursor at the start of the file. The fd's cursor is * modified by this operation. */ static int verifyMagicAndGetAdler32(int fd, u4 *adler32) { /* * The start of a dex file is eight bytes of magic followed by * four bytes of checksum. */ u1 headerStart[12]; ssize_t amt = read(fd, headerStart, sizeof(headerStart)); if (amt < 0) { ALOGE("Unable to read header: %s", strerror(errno)); return -1; } if (amt != sizeof(headerStart)) { ALOGE("Unable to read full header (only got %d bytes)", (int) amt); return -1; } if (!dexHasValidMagic((DexHeader*) (void*) headerStart)) { return -1; } /* * We can't just cast the data to a u4 and read it, since the * platform might be big-endian (also, because that would make the * compiler complain about type-punned pointers). We assume here * that the dex file is in the standard little-endian format; if * that assumption turns out to be invalid, code that runs later * will notice and complain. */ *adler32 = (u4) headerStart[8] | (((u4) headerStart[9]) << 8) | (((u4) headerStart[10]) << 16) | (((u4) headerStart[11]) << 24); return 0; } /* See documentation comment in header. */ int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName, RawDexFile** ppRawDexFile, bool isBootstrap) { /* * TODO: This duplicates a lot of code from dvmJarFileOpen() in * JarFile.c. This should be refactored. */ DvmDex* pDvmDex = NULL; char* cachedName = NULL; int result = -1; int dexFd = -1; int optFd = -1; u4 modTime = 0; u4 adler32 = 0; size_t fileSize = 0; bool newFile = false; bool locked = false; dexFd = open(fileName, O_RDONLY); if (dexFd < 0) goto bail; /* If we fork/exec into dexopt, don't let it inherit the open fd. */ dvmSetCloseOnExec(dexFd); if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) { ALOGE("Error with header for %s", fileName); goto bail; } if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) { ALOGE("Error with stat for %s", fileName); goto bail; } /* * See if the cached file matches. If so, optFd will become a reference * to the cached file and will have been seeked to just past the "opt" * header. */ if (odexOutputName == NULL) { cachedName = dexOptGenerateCacheFileName(fileName, NULL); if (cachedName == NULL) goto bail; } else { cachedName = strdup(odexOutputName); } ALOGV("dvmRawDexFileOpen: Checking cache for %s (%s)", fileName, cachedName); optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime, adler32, isBootstrap, &newFile, /*createIfMissing=*/true); if (optFd < 0) { ALOGI("Unable to open or create cache for %s (%s)", fileName, cachedName); goto bail; } locked = true; /* * If optFd 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, copyWhen, endWhen; bool result; off_t dexOffset; dexOffset = lseek(optFd, 0, SEEK_CUR); result = (dexOffset > 0); if (result) { startWhen = dvmGetRelativeTimeUsec(); result = copyFileToFile(optFd, dexFd, fileSize) == 0; copyWhen = dvmGetRelativeTimeUsec(); } if (result) { result = dvmOptimizeDexFile(optFd, dexOffset, fileSize, fileName, modTime, adler32, isBootstrap); } if (!result) { ALOGE("Unable to extract+optimize DEX from '%s'", fileName); goto bail; } endWhen = dvmGetRelativeTimeUsec(); ALOGD("DEX prep '%s': copy in %dms, rewrite %dms", fileName, (int) (copyWhen - startWhen) / 1000, (int) (endWhen - copyWhen) / 1000); } /* * Map the cached version. This immediately rewinds the fd, so it * doesn't have to be seeked anywhere in particular. */ if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) { ALOGI("Unable to map cached %s", fileName); goto bail; } if (locked) { /* unlock the fd */ if (!dvmUnlockCachedDexFile(optFd)) { /* 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'", fileName); *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile)); (*ppRawDexFile)->cacheFileName = cachedName; (*ppRawDexFile)->pDvmDex = pDvmDex; cachedName = NULL; // don't free it below result = 0; bail: free(cachedName); if (dexFd >= 0) { close(dexFd); } if (optFd >= 0) { if (locked) (void) dvmUnlockCachedDexFile(optFd); close(optFd); } return result; } /* See documentation comment in header. */ int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppRawDexFile) { DvmDex* pDvmDex = NULL; if (!dvmPrepareDexInMemory(pBytes, length, &pDvmDex)) { ALOGD("Unable to open raw DEX from array"); return -1; } assert(pDvmDex != NULL); *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile)); (*ppRawDexFile)->pDvmDex = pDvmDex; return 0; } /* * Close a RawDexFile and free the struct. */ void dvmRawDexFileFree(RawDexFile* pRawDexFile) { if (pRawDexFile == NULL) return; dvmDexFileFree(pRawDexFile->pDvmDex); free(pRawDexFile->cacheFileName); free(pRawDexFile); }