Home | History | Annotate | Download | only in vm
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * Open an unoptimized DEX file.
     19  */
     20 
     21 #include "Dalvik.h"
     22 #include "libdex/OptInvocation.h"
     23 
     24 #include <fcntl.h>
     25 #include <sys/stat.h>
     26 #include <sys/types.h>
     27 #include <unistd.h>
     28 
     29 /*
     30  * Copy the given number of bytes from one fd to another, first
     31  * seeking the source fd to the start of the file.
     32  */
     33 static int copyFileToFile(int destFd, int srcFd, size_t size)
     34 {
     35     if (lseek(srcFd, 0, SEEK_SET) != 0) {
     36         ALOGE("lseek failure: %s", strerror(errno));
     37         return -1;
     38     }
     39 
     40     return sysCopyFileToFile(destFd, srcFd, size);
     41 }
     42 
     43 /*
     44  * Get the modification time and size in bytes for the given fd.
     45  */
     46 static int getModTimeAndSize(int fd, u4* modTime, size_t* size)
     47 {
     48     struct stat buf;
     49     int result = fstat(fd, &buf);
     50 
     51     if (result < 0) {
     52         ALOGE("Unable to determine mod time: %s", strerror(errno));
     53         return -1;
     54     }
     55 
     56     *modTime = (u4) buf.st_mtime;
     57     *size = (size_t) buf.st_size;
     58     assert((size_t) buf.st_size == buf.st_size);
     59 
     60     return 0;
     61 }
     62 
     63 /*
     64  * Verify the dex file magic number, and get the adler32 checksum out
     65  * of the given fd, which is presumed to be a reference to a dex file
     66  * with the cursor at the start of the file. The fd's cursor is
     67  * modified by this operation.
     68  */
     69 static int verifyMagicAndGetAdler32(int fd, u4 *adler32)
     70 {
     71     /*
     72      * The start of a dex file is eight bytes of magic followed by
     73      * four bytes of checksum.
     74      */
     75     u1 headerStart[12];
     76     ssize_t amt = read(fd, headerStart, sizeof(headerStart));
     77 
     78     if (amt < 0) {
     79         ALOGE("Unable to read header: %s", strerror(errno));
     80         return -1;
     81     }
     82 
     83     if (amt != sizeof(headerStart)) {
     84         ALOGE("Unable to read full header (only got %d bytes)", (int) amt);
     85         return -1;
     86     }
     87 
     88     if (!dexHasValidMagic((DexHeader*) (void*) headerStart)) {
     89         return -1;
     90     }
     91 
     92     /*
     93      * We can't just cast the data to a u4 and read it, since the
     94      * platform might be big-endian (also, because that would make the
     95      * compiler complain about type-punned pointers). We assume here
     96      * that the dex file is in the standard little-endian format; if
     97      * that assumption turns out to be invalid, code that runs later
     98      * will notice and complain.
     99      */
    100     *adler32 = (u4) headerStart[8]
    101         | (((u4) headerStart[9]) << 8)
    102         | (((u4) headerStart[10]) << 16)
    103         | (((u4) headerStart[11]) << 24);
    104 
    105     return 0;
    106 }
    107 
    108 /* See documentation comment in header. */
    109 int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
    110     RawDexFile** ppRawDexFile, bool isBootstrap)
    111 {
    112     /*
    113      * TODO: This duplicates a lot of code from dvmJarFileOpen() in
    114      * JarFile.c. This should be refactored.
    115      */
    116 
    117     DvmDex* pDvmDex = NULL;
    118     char* cachedName = NULL;
    119     int result = -1;
    120     int dexFd = -1;
    121     int optFd = -1;
    122     u4 modTime = 0;
    123     u4 adler32 = 0;
    124     size_t fileSize = 0;
    125     bool newFile = false;
    126     bool locked = false;
    127 
    128     dexFd = open(fileName, O_RDONLY);
    129     if (dexFd < 0) goto bail;
    130 
    131     /* If we fork/exec into dexopt, don't let it inherit the open fd. */
    132     dvmSetCloseOnExec(dexFd);
    133 
    134     if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) {
    135         ALOGE("Error with header for %s", fileName);
    136         goto bail;
    137     }
    138 
    139     if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) {
    140         ALOGE("Error with stat for %s", fileName);
    141         goto bail;
    142     }
    143 
    144     /*
    145      * See if the cached file matches. If so, optFd will become a reference
    146      * to the cached file and will have been seeked to just past the "opt"
    147      * header.
    148      */
    149 
    150     if (odexOutputName == NULL) {
    151         cachedName = dexOptGenerateCacheFileName(fileName, NULL);
    152         if (cachedName == NULL)
    153             goto bail;
    154     } else {
    155         cachedName = strdup(odexOutputName);
    156     }
    157 
    158     ALOGV("dvmRawDexFileOpen: Checking cache for %s (%s)",
    159             fileName, cachedName);
    160 
    161     optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime,
    162         adler32, isBootstrap, &newFile, /*createIfMissing=*/true);
    163 
    164     if (optFd < 0) {
    165         ALOGI("Unable to open or create cache for %s (%s)",
    166                 fileName, cachedName);
    167         goto bail;
    168     }
    169     locked = true;
    170 
    171     /*
    172      * If optFd points to a new file (because there was no cached
    173      * version, or the cached version was stale), generate the
    174      * optimized DEX. The file descriptor returned is still locked,
    175      * and is positioned just past the optimization header.
    176      */
    177     if (newFile) {
    178         u8 startWhen, copyWhen, endWhen;
    179         bool result;
    180         off_t dexOffset;
    181 
    182         dexOffset = lseek(optFd, 0, SEEK_CUR);
    183         result = (dexOffset > 0);
    184 
    185         if (result) {
    186             startWhen = dvmGetRelativeTimeUsec();
    187             result = copyFileToFile(optFd, dexFd, fileSize) == 0;
    188             copyWhen = dvmGetRelativeTimeUsec();
    189         }
    190 
    191         if (result) {
    192             result = dvmOptimizeDexFile(optFd, dexOffset, fileSize,
    193                 fileName, modTime, adler32, isBootstrap);
    194         }
    195 
    196         if (!result) {
    197             ALOGE("Unable to extract+optimize DEX from '%s'", fileName);
    198             goto bail;
    199         }
    200 
    201         endWhen = dvmGetRelativeTimeUsec();
    202         ALOGD("DEX prep '%s': copy in %dms, rewrite %dms",
    203             fileName,
    204             (int) (copyWhen - startWhen) / 1000,
    205             (int) (endWhen - copyWhen) / 1000);
    206     }
    207 
    208     /*
    209      * Map the cached version.  This immediately rewinds the fd, so it
    210      * doesn't have to be seeked anywhere in particular.
    211      */
    212     if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) {
    213         ALOGI("Unable to map cached %s", fileName);
    214         goto bail;
    215     }
    216 
    217     if (locked) {
    218         /* unlock the fd */
    219         if (!dvmUnlockCachedDexFile(optFd)) {
    220             /* uh oh -- this process needs to exit or we'll wedge the system */
    221             ALOGE("Unable to unlock DEX file");
    222             goto bail;
    223         }
    224         locked = false;
    225     }
    226 
    227     ALOGV("Successfully opened '%s'", fileName);
    228 
    229     *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
    230     (*ppRawDexFile)->cacheFileName = cachedName;
    231     (*ppRawDexFile)->pDvmDex = pDvmDex;
    232     cachedName = NULL;      // don't free it below
    233     result = 0;
    234 
    235 bail:
    236     free(cachedName);
    237     if (dexFd >= 0) {
    238         close(dexFd);
    239     }
    240     if (optFd >= 0) {
    241         if (locked)
    242             (void) dvmUnlockCachedDexFile(optFd);
    243         close(optFd);
    244     }
    245     return result;
    246 }
    247 
    248 /* See documentation comment in header. */
    249 int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppRawDexFile)
    250 {
    251     DvmDex* pDvmDex = NULL;
    252 
    253     if (!dvmPrepareDexInMemory(pBytes, length, &pDvmDex)) {
    254         ALOGD("Unable to open raw DEX from array");
    255         return -1;
    256     }
    257     assert(pDvmDex != NULL);
    258 
    259     *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile));
    260     (*ppRawDexFile)->pDvmDex = pDvmDex;
    261 
    262     return 0;
    263 }
    264 
    265 /*
    266  * Close a RawDexFile and free the struct.
    267  */
    268 void dvmRawDexFileFree(RawDexFile* pRawDexFile)
    269 {
    270     if (pRawDexFile == NULL)
    271         return;
    272 
    273     dvmDexFileFree(pRawDexFile->pDvmDex);
    274     free(pRawDexFile->cacheFileName);
    275     free(pRawDexFile);
    276 }
    277