diff options
-rw-r--r-- | vm/JarFile.c | 9 | ||||
-rw-r--r-- | vm/RawDexFile.c | 262 | ||||
-rw-r--r-- | vm/native/dalvik_system_DexFile.c | 18 |
3 files changed, 283 insertions, 6 deletions
diff --git a/vm/JarFile.c b/vm/JarFile.c index 919787629..8b13e6a4e 100644 --- a/vm/JarFile.c +++ b/vm/JarFile.c @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Access the contents of a Jar file. * @@ -20,6 +21,7 @@ * just wants a zip archive with "classes.dex" inside. In Android the * most common example is ".apk". */ + #include "Dalvik.h" #include "libdex/OptInvocation.h" @@ -186,6 +188,11 @@ bail: 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; @@ -254,7 +261,7 @@ tryArchive: } else { cachedName = strdup(odexOutputName); } - LOGV("dvmDexCacheStatus: Checking cache for %s (%s)\n", + LOGV("dvmJarFileOpen: Checking cache for %s (%s)\n", fileName, cachedName); fd = dvmOpenCachedDexFile(fileName, cachedName, dexGetZipEntryModTime(&archive, entry), diff --git a/vm/RawDexFile.c b/vm/RawDexFile.c index 62e67100e..1d8784ca0 100644 --- a/vm/RawDexFile.c +++ b/vm/RawDexFile.c @@ -17,14 +17,272 @@ /* * 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, u4 size) +{ + u1* buf = malloc(size); + int result = -1; + ssize_t amt; + + if (buf == NULL) { + LOGE("malloc failure (errno %d)\n", errno); + goto bail; + } + + if (lseek(srcFd, 0, SEEK_SET) != 0) { + LOGE("lseek failure (errno %d)\n", errno); + goto bail; + } + + amt = read(srcFd, buf, size); + + if (amt < 0) { + LOGE("read failure (errno %d)\n", errno); + goto bail; + } + + if (amt != (ssize_t) size) { + LOGE("short read (%d < %ud)\n", (int) amt, size); + goto bail; + } + + amt = write(destFd, buf, size); + + if (amt < 0) { + LOGE("write failure (errno %d)\n", errno); + goto bail; + } + + if (amt != (ssize_t) size) { + LOGE("short write (%d < %ud)\n", (int) amt, size); + goto bail; + } + + result = 0; // Success! + +bail: + free(buf); + return result; +} + +/* + * Get the modification time and size in bytes for the given fd. + */ +static int getModTimeAndSize(int fd, u4* modTime, u4* size) +{ + struct stat buf; + int result = fstat(fd, &buf); + + if (result < 0) { + LOGE("Unable to determing mod time (errno %d)\n", errno); + return -1; + } + + *modTime = (u4) buf.st_mtime; + *size = (u4) 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 (errno %d)\n", errno); + return -1; + } + + if (amt != sizeof(headerStart)) { + LOGE("Unable to read full header (only got %d bytes)\n", (int) amt); + return -1; + } + + if (memcmp(headerStart, DEX_MAGIC DEX_MAGIC_VERS, 8) != 0) { + LOGE("Unexpected dex magic (0x%02x%02x%02x%02x%02x%02x%02x%02x)\n", + headerStart[0], headerStart[1], headerStart[2], + headerStart[3], headerStart[4], headerStart[5], + headerStart[6], headerStart[7]); + 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 - should be very similar to what JarFile does. - return -1; + /* + * 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; + u4 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\n", fileName); + goto bail; + } + + if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) { + LOGE("Error with stat for %s\n", 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)\n", + 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)\n", + 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'\n", fileName); + goto bail; + } + + endWhen = dvmGetRelativeTimeUsec(); + LOGD("DEX prep '%s': copy in %dms, rewrite %dms\n", + 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\n", 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\n"); + goto bail; + } + locked = false; + } + + LOGV("Successfully opened '%s'\n", 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. */ diff --git a/vm/native/dalvik_system_DexFile.c b/vm/native/dalvik_system_DexFile.c index 59f5d57fc..7c601d841 100644 --- a/vm/native/dalvik_system_DexFile.c +++ b/vm/native/dalvik_system_DexFile.c @@ -20,6 +20,16 @@ #include "Dalvik.h" #include "native/InternalNativePriv.h" +/* + * Return true if the given name ends with ".dex". + */ +static bool hasDexExtension(const char* name) { + size_t len = strlen(name); + + return (len >= 5) + && (name[len - 5] != '/') + && (strcmp(&name[len - 4], ".dex") == 0); +} /* * Internal struct for managing DexFile. @@ -191,10 +201,12 @@ static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args, } /* - * Try to open it directly as a DEX. If that fails, try it as a Zip - * with a "classes.dex" inside. + * 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. */ - if (dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) { + if (hasDexExtension(sourceName) + && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) { LOGV("Opening DEX file '%s' (DEX)\n", sourceName); pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); |