Home | History | Annotate | Download | only in native
      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  * dalvik.system.DexFile
     19  */
     20 #include "Dalvik.h"
     21 #include "native/InternalNativePriv.h"
     22 
     23 /*
     24  * Return true if the given name ends with ".dex".
     25  */
     26 static bool hasDexExtension(const char* name) {
     27     size_t len = strlen(name);
     28 
     29     return (len >= 5)
     30         && (name[len - 5] != '/')
     31         && (strcmp(&name[len - 4], ".dex") == 0);
     32 }
     33 
     34 /*
     35  * Internal struct for managing DexFile.
     36  */
     37 struct DexOrJar {
     38     char*       fileName;
     39     bool        isDex;
     40     bool        okayToFree;
     41     RawDexFile* pRawDexFile;
     42     JarFile*    pJarFile;
     43     u1*         pDexMemory; // malloc()ed memory, if any
     44 };
     45 
     46 /*
     47  * (This is a dvmHashTableFree callback.)
     48  */
     49 void dvmFreeDexOrJar(void* vptr)
     50 {
     51     DexOrJar* pDexOrJar = (DexOrJar*) vptr;
     52 
     53     ALOGV("Freeing DexOrJar '%s'", pDexOrJar->fileName);
     54 
     55     if (pDexOrJar->isDex)
     56         dvmRawDexFileFree(pDexOrJar->pRawDexFile);
     57     else
     58         dvmJarFileFree(pDexOrJar->pJarFile);
     59     free(pDexOrJar->fileName);
     60     free(pDexOrJar->pDexMemory);
     61     free(pDexOrJar);
     62 }
     63 
     64 /*
     65  * (This is a dvmHashTableLookup compare func.)
     66  *
     67  * Args are DexOrJar*.
     68  */
     69 static int hashcmpDexOrJar(const void* tableVal, const void* newVal)
     70 {
     71     return (int) newVal - (int) tableVal;
     72 }
     73 
     74 /*
     75  * Verify that the "cookie" is a DEX file we opened.
     76  *
     77  * Expects that the hash table will be *unlocked* here.
     78  *
     79  * If the cookie is invalid, we throw an exception and return "false".
     80  */
     81 static bool validateCookie(int cookie)
     82 {
     83     DexOrJar* pDexOrJar = (DexOrJar*) cookie;
     84 
     85     LOGVV("+++ dex verifying cookie %p", pDexOrJar);
     86 
     87     if (pDexOrJar == NULL)
     88         return false;
     89 
     90     u4 hash = cookie;
     91     dvmHashTableLock(gDvm.userDexFiles);
     92     void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
     93                 hashcmpDexOrJar, false);
     94     dvmHashTableUnlock(gDvm.userDexFiles);
     95     if (result == NULL) {
     96         dvmThrowRuntimeException("invalid DexFile cookie");
     97         return false;
     98     }
     99 
    100     return true;
    101 }
    102 
    103 
    104 /*
    105  * Add given DexOrJar to the hash table of user-loaded dex files.
    106  */
    107 static void addToDexFileTable(DexOrJar* pDexOrJar) {
    108     /*
    109      * Later on, we will receive this pointer as an argument and need
    110      * to find it in the hash table without knowing if it's valid or
    111      * not, which means we can't compute a hash value from anything
    112      * inside DexOrJar. We don't share DexOrJar structs when the same
    113      * file is opened multiple times, so we can just use the low 32
    114      * bits of the pointer as the hash.
    115      */
    116     u4 hash = (u4) pDexOrJar;
    117     void* result;
    118 
    119     dvmHashTableLock(gDvm.userDexFiles);
    120     result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
    121             hashcmpDexOrJar, true);
    122     dvmHashTableUnlock(gDvm.userDexFiles);
    123 
    124     if (result != pDexOrJar) {
    125         ALOGE("Pointer has already been added?");
    126         dvmAbort();
    127     }
    128 
    129     pDexOrJar->okayToFree = true;
    130 }
    131 
    132 /*
    133  * private static int openDexFileNative(String sourceName, String outputName,
    134  *     int flags) throws IOException
    135  *
    136  * Open a DEX file, returning a pointer to our internal data structure.
    137  *
    138  * "sourceName" should point to the "source" jar or DEX file.
    139  *
    140  * If "outputName" is NULL, the DEX code will automatically find the
    141  * "optimized" version in the cache directory, creating it if necessary.
    142  * If it's non-NULL, the specified file will be used instead.
    143  *
    144  * TODO: at present we will happily open the same file more than once.
    145  * To optimize this away we could search for existing entries in the hash
    146  * table and refCount them.  Requires atomic ops or adding "synchronized"
    147  * to the non-native code that calls here.
    148  *
    149  * TODO: should be using "long" for a pointer.
    150  */
    151 static void Dalvik_dalvik_system_DexFile_openDexFileNative(const u4* args,
    152     JValue* pResult)
    153 {
    154     StringObject* sourceNameObj = (StringObject*) args[0];
    155     StringObject* outputNameObj = (StringObject*) args[1];
    156     DexOrJar* pDexOrJar = NULL;
    157     JarFile* pJarFile;
    158     RawDexFile* pRawDexFile;
    159     char* sourceName;
    160     char* outputName;
    161 
    162     if (sourceNameObj == NULL) {
    163         dvmThrowNullPointerException("sourceName == null");
    164         RETURN_VOID();
    165     }
    166 
    167     sourceName = dvmCreateCstrFromString(sourceNameObj);
    168     if (outputNameObj != NULL)
    169         outputName = dvmCreateCstrFromString(outputNameObj);
    170     else
    171         outputName = NULL;
    172 
    173     /*
    174      * We have to deal with the possibility that somebody might try to
    175      * open one of our bootstrap class DEX files.  The set of dependencies
    176      * will be different, and hence the results of optimization might be
    177      * different, which means we'd actually need to have two versions of
    178      * the optimized DEX: one that only knows about part of the boot class
    179      * path, and one that knows about everything in it.  The latter might
    180      * optimize field/method accesses based on a class that appeared later
    181      * in the class path.
    182      *
    183      * We can't let the user-defined class loader open it and start using
    184      * the classes, since the optimized form of the code skips some of
    185      * the method and field resolution that we would ordinarily do, and
    186      * we'd have the wrong semantics.
    187      *
    188      * We have to reject attempts to manually open a DEX file from the boot
    189      * class path.  The easiest way to do this is by filename, which works
    190      * out because variations in name (e.g. "/system/framework/./ext.jar")
    191      * result in us hitting a different dalvik-cache entry.  It's also fine
    192      * if the caller specifies their own output file.
    193      */
    194     if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) {
    195         ALOGW("Refusing to reopen boot DEX '%s'", sourceName);
    196         dvmThrowIOException(
    197             "Re-opening BOOTCLASSPATH DEX files is not allowed");
    198         free(sourceName);
    199         free(outputName);
    200         RETURN_VOID();
    201     }
    202 
    203     /*
    204      * Try to open it directly as a DEX if the name ends with ".dex".
    205      * If that fails (or isn't tried in the first place), try it as a
    206      * Zip with a "classes.dex" inside.
    207      */
    208     if (hasDexExtension(sourceName)
    209             && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
    210         ALOGV("Opening DEX file '%s' (DEX)", sourceName);
    211 
    212         pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
    213         pDexOrJar->isDex = true;
    214         pDexOrJar->pRawDexFile = pRawDexFile;
    215         pDexOrJar->pDexMemory = NULL;
    216     } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
    217         ALOGV("Opening DEX file '%s' (Jar)", sourceName);
    218 
    219         pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
    220         pDexOrJar->isDex = false;
    221         pDexOrJar->pJarFile = pJarFile;
    222         pDexOrJar->pDexMemory = NULL;
    223     } else {
    224         ALOGV("Unable to open DEX file '%s'", sourceName);
    225         dvmThrowIOException("unable to open DEX file");
    226     }
    227 
    228     if (pDexOrJar != NULL) {
    229         pDexOrJar->fileName = sourceName;
    230         addToDexFileTable(pDexOrJar);
    231     } else {
    232         free(sourceName);
    233     }
    234 
    235     free(outputName);
    236     RETURN_PTR(pDexOrJar);
    237 }
    238 
    239 /*
    240  * private static int openDexFile(byte[] fileContents) throws IOException
    241  *
    242  * Open a DEX file represented in a byte[], returning a pointer to our
    243  * internal data structure.
    244  *
    245  * The system will only perform "essential" optimizations on the given file.
    246  *
    247  * TODO: should be using "long" for a pointer.
    248  */
    249 static void Dalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4* args,
    250     JValue* pResult)
    251 {
    252     ArrayObject* fileContentsObj = (ArrayObject*) args[0];
    253     u4 length;
    254     u1* pBytes;
    255     RawDexFile* pRawDexFile;
    256     DexOrJar* pDexOrJar = NULL;
    257 
    258     if (fileContentsObj == NULL) {
    259         dvmThrowNullPointerException("fileContents == null");
    260         RETURN_VOID();
    261     }
    262 
    263     /* TODO: Avoid making a copy of the array. (note array *is* modified) */
    264     length = fileContentsObj->length;
    265     pBytes = (u1*) malloc(length);
    266 
    267     if (pBytes == NULL) {
    268         dvmThrowRuntimeException("unable to allocate DEX memory");
    269         RETURN_VOID();
    270     }
    271 
    272     memcpy(pBytes, fileContentsObj->contents, length);
    273 
    274     if (dvmRawDexFileOpenArray(pBytes, length, &pRawDexFile) != 0) {
    275         ALOGV("Unable to open in-memory DEX file");
    276         free(pBytes);
    277         dvmThrowRuntimeException("unable to open in-memory DEX file");
    278         RETURN_VOID();
    279     }
    280 
    281     ALOGV("Opening in-memory DEX");
    282     pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
    283     pDexOrJar->isDex = true;
    284     pDexOrJar->pRawDexFile = pRawDexFile;
    285     pDexOrJar->pDexMemory = pBytes;
    286     pDexOrJar->fileName = strdup("<memory>"); // Needs to be free()able.
    287     addToDexFileTable(pDexOrJar);
    288 
    289     RETURN_PTR(pDexOrJar);
    290 }
    291 
    292 /*
    293  * private static void closeDexFile(int cookie)
    294  *
    295  * Release resources associated with a user-loaded DEX file.
    296  */
    297 static void Dalvik_dalvik_system_DexFile_closeDexFile(const u4* args,
    298     JValue* pResult)
    299 {
    300     int cookie = args[0];
    301     DexOrJar* pDexOrJar = (DexOrJar*) cookie;
    302 
    303     if (pDexOrJar == NULL)
    304         RETURN_VOID();
    305     if (!validateCookie(cookie))
    306         RETURN_VOID();
    307 
    308     ALOGV("Closing DEX file %p (%s)", pDexOrJar, pDexOrJar->fileName);
    309 
    310     /*
    311      * We can't just free arbitrary DEX files because they have bits and
    312      * pieces of loaded classes.  The only exception to this rule is if
    313      * they were never used to load classes.
    314      *
    315      * If we can't free them here, dvmInternalNativeShutdown() will free
    316      * them when the VM shuts down.
    317      */
    318     if (pDexOrJar->okayToFree) {
    319         u4 hash = (u4) pDexOrJar;
    320         dvmHashTableLock(gDvm.userDexFiles);
    321         if (!dvmHashTableRemove(gDvm.userDexFiles, hash, pDexOrJar)) {
    322             ALOGW("WARNING: could not remove '%s' from DEX hash table",
    323                 pDexOrJar->fileName);
    324         }
    325         dvmHashTableUnlock(gDvm.userDexFiles);
    326         ALOGV("+++ freeing DexFile '%s' resources", pDexOrJar->fileName);
    327         dvmFreeDexOrJar(pDexOrJar);
    328     } else {
    329         ALOGV("+++ NOT freeing DexFile '%s' resources", pDexOrJar->fileName);
    330     }
    331 
    332     RETURN_VOID();
    333 }
    334 
    335 /*
    336  * private static Class defineClassNative(String name, ClassLoader loader,
    337  *      int cookie)
    338  *
    339  * Load a class from a DEX file.  This is roughly equivalent to defineClass()
    340  * in a regular VM -- it's invoked by the class loader to cause the
    341  * creation of a specific class.  The difference is that the search for and
    342  * reading of the bytes is done within the VM.
    343  *
    344  * The class name is a "binary name", e.g. "java.lang.String".
    345  *
    346  * Returns a null pointer with no exception if the class was not found.
    347  * Throws an exception on other failures.
    348  */
    349 static void Dalvik_dalvik_system_DexFile_defineClassNative(const u4* args,
    350     JValue* pResult)
    351 {
    352     StringObject* nameObj = (StringObject*) args[0];
    353     Object* loader = (Object*) args[1];
    354     int cookie = args[2];
    355     ClassObject* clazz = NULL;
    356     DexOrJar* pDexOrJar = (DexOrJar*) cookie;
    357     DvmDex* pDvmDex;
    358     char* name;
    359     char* descriptor;
    360 
    361     name = dvmCreateCstrFromString(nameObj);
    362     descriptor = dvmDotToDescriptor(name);
    363     ALOGV("--- Explicit class load '%s' l=%p c=0x%08x",
    364         descriptor, loader, cookie);
    365     free(name);
    366 
    367     if (!validateCookie(cookie))
    368         RETURN_VOID();
    369 
    370     if (pDexOrJar->isDex)
    371         pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
    372     else
    373         pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
    374 
    375     /* once we load something, we can't unmap the storage */
    376     pDexOrJar->okayToFree = false;
    377 
    378     clazz = dvmDefineClass(pDvmDex, descriptor, loader);
    379     Thread* self = dvmThreadSelf();
    380     if (dvmCheckException(self)) {
    381         /*
    382          * If we threw a "class not found" exception, stifle it, since the
    383          * contract in the higher method says we simply return null if
    384          * the class is not found.
    385          */
    386         Object* excep = dvmGetException(self);
    387         if (strcmp(excep->clazz->descriptor,
    388                    "Ljava/lang/ClassNotFoundException;") == 0 ||
    389             strcmp(excep->clazz->descriptor,
    390                    "Ljava/lang/NoClassDefFoundError;") == 0)
    391         {
    392             dvmClearException(self);
    393         }
    394         clazz = NULL;
    395     }
    396 
    397     free(descriptor);
    398     RETURN_PTR(clazz);
    399 }
    400 
    401 /*
    402  * private static String[] getClassNameList(int cookie)
    403  *
    404  * Returns a String array that holds the names of all classes in the
    405  * specified DEX file.
    406  */
    407 static void Dalvik_dalvik_system_DexFile_getClassNameList(const u4* args,
    408     JValue* pResult)
    409 {
    410     int cookie = args[0];
    411     DexOrJar* pDexOrJar = (DexOrJar*) cookie;
    412     Thread* self = dvmThreadSelf();
    413 
    414     if (!validateCookie(cookie))
    415         RETURN_VOID();
    416 
    417     DvmDex* pDvmDex;
    418     if (pDexOrJar->isDex)
    419         pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
    420     else
    421         pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
    422     assert(pDvmDex != NULL);
    423     DexFile* pDexFile = pDvmDex->pDexFile;
    424 
    425     int count = pDexFile->pHeader->classDefsSize;
    426     ClassObject* arrayClass =
    427         dvmFindArrayClassForElement(gDvm.classJavaLangString);
    428     ArrayObject* stringArray =
    429         dvmAllocArrayByClass(arrayClass, count, ALLOC_DEFAULT);
    430     if (stringArray == NULL) {
    431         /* probably OOM */
    432         ALOGD("Failed allocating array of %d strings", count);
    433         assert(dvmCheckException(self));
    434         RETURN_VOID();
    435     }
    436 
    437     int i;
    438     for (i = 0; i < count; i++) {
    439         const DexClassDef* pClassDef = dexGetClassDef(pDexFile, i);
    440         const char* descriptor =
    441             dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
    442 
    443         char* className = dvmDescriptorToDot(descriptor);
    444         StringObject* str = dvmCreateStringFromCstr(className);
    445         dvmSetObjectArrayElement(stringArray, i, (Object *)str);
    446         dvmReleaseTrackedAlloc((Object *)str, self);
    447         free(className);
    448     }
    449 
    450     dvmReleaseTrackedAlloc((Object*)stringArray, self);
    451     RETURN_PTR(stringArray);
    452 }
    453 
    454 /*
    455  * public static boolean isDexOptNeeded(String fileName)
    456  *         throws FileNotFoundException, IOException
    457  *
    458  * Returns true if the VM believes that the apk/jar file is out of date
    459  * and should be passed through "dexopt" again.
    460  *
    461  * @param fileName the absolute path to the apk/jar file to examine.
    462  * @return true if dexopt should be called on the file, false otherwise.
    463  * @throws java.io.FileNotFoundException if fileName is not readable,
    464  *         not a file, or not present.
    465  * @throws java.io.IOException if fileName is not a valid apk/jar file or
    466  *         if problems occur while parsing it.
    467  * @throws java.lang.NullPointerException if fileName is null.
    468  * @throws dalvik.system.StaleDexCacheError if the optimized dex file
    469  *         is stale but exists on a read-only partition.
    470  */
    471 static void Dalvik_dalvik_system_DexFile_isDexOptNeeded(const u4* args,
    472     JValue* pResult)
    473 {
    474     StringObject* nameObj = (StringObject*) args[0];
    475     char* name;
    476     DexCacheStatus status;
    477     int result;
    478 
    479     name = dvmCreateCstrFromString(nameObj);
    480     if (name == NULL) {
    481         dvmThrowNullPointerException("fileName == null");
    482         RETURN_VOID();
    483     }
    484     if (access(name, R_OK) != 0) {
    485         dvmThrowFileNotFoundException(name);
    486         free(name);
    487         RETURN_VOID();
    488     }
    489     status = dvmDexCacheStatus(name);
    490     ALOGV("dvmDexCacheStatus(%s) returned %d", name, status);
    491 
    492     result = true;
    493     switch (status) {
    494     default: //FALLTHROUGH
    495     case DEX_CACHE_BAD_ARCHIVE:
    496         dvmThrowIOException(name);
    497         result = -1;
    498         break;
    499     case DEX_CACHE_OK:
    500         result = false;
    501         break;
    502     case DEX_CACHE_STALE:
    503         result = true;
    504         break;
    505     case DEX_CACHE_STALE_ODEX:
    506         dvmThrowStaleDexCacheError(name);
    507         result = -1;
    508         break;
    509     }
    510     free(name);
    511 
    512     if (result >= 0) {
    513         RETURN_BOOLEAN(result);
    514     } else {
    515         RETURN_VOID();
    516     }
    517 }
    518 
    519 const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
    520     { "openDexFileNative",  "(Ljava/lang/String;Ljava/lang/String;I)I",
    521         Dalvik_dalvik_system_DexFile_openDexFileNative },
    522     { "openDexFile",        "([B)I",
    523         Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
    524     { "closeDexFile",       "(I)V",
    525         Dalvik_dalvik_system_DexFile_closeDexFile },
    526     { "defineClassNative",  "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;",
    527         Dalvik_dalvik_system_DexFile_defineClassNative },
    528     { "getClassNameList",   "(I)[Ljava/lang/String;",
    529         Dalvik_dalvik_system_DexFile_getClassNameList },
    530     { "isDexOptNeeded",     "(Ljava/lang/String;)Z",
    531         Dalvik_dalvik_system_DexFile_isDexOptNeeded },
    532     { NULL, NULL, NULL },
    533 };
    534