diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2012-04-01 00:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2012-06-21 10:38:11 -0700 |
commit | 41febc2d70dc46595932f5a383bafc1dae296607 (patch) | |
tree | e2f340784edb0c58bc3bc5bb890dc40254310bdd /vm/RawDexFile.cpp | |
download | dalvik-snapshot-ics-mr1.tar.gz |
Snapshot 1c7e1e149d3dcf3949c76ae594ca9c1ca20392f9ics-mr1
Diffstat (limited to 'vm/RawDexFile.cpp')
-rw-r--r-- | vm/RawDexFile.cpp | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/vm/RawDexFile.cpp b/vm/RawDexFile.cpp new file mode 100644 index 0000000..9fef57c --- /dev/null +++ b/vm/RawDexFile.cpp @@ -0,0 +1,276 @@ +/* + * 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) { + LOGE("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) { + LOGE("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) { + LOGE("Unable to read header: %s", strerror(errno)); + return -1; + } + + if (amt != sizeof(headerStart)) { + LOGE("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) { + LOGE("Error with header for %s", fileName); + goto bail; + } + + if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) { + LOGE("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); + } + + LOGV("dvmRawDexFileOpen: Checking cache for %s (%s)", + fileName, cachedName); + + optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime, + adler32, isBootstrap, &newFile, /*createIfMissing=*/true); + + if (optFd < 0) { + LOGI("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) { + LOGE("Unable to extract+optimize DEX from '%s'", fileName); + goto bail; + } + + endWhen = dvmGetRelativeTimeUsec(); + LOGD("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) { + LOGI("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 */ + LOGE("Unable to unlock DEX file"); + goto bail; + } + locked = false; + } + + LOGV("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)) { + LOGD("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); +} |