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 openDexFile(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_openDexFile(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     RETURN_PTR(pDexOrJar);
    236 }
    237 
    238 /*
    239  * private static int openDexFile(byte[] fileContents) throws IOException
    240  *
    241  * Open a DEX file represented in a byte[], returning a pointer to our
    242  * internal data structure.
    243  *
    244  * The system will only perform "essential" optimizations on the given file.
    245  *
    246  * TODO: should be using "long" for a pointer.
    247  */
    248 static void Dalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4* args,
    249     JValue* pResult)
    250 {
    251     ArrayObject* fileContentsObj = (ArrayObject*) args[0];
    252     u4 length;
    253     u1* pBytes;
    254     RawDexFile* pRawDexFile;
    255     DexOrJar* pDexOrJar = NULL;
    256 
    257     if (fileContentsObj == NULL) {
    258         dvmThrowNullPointerException("fileContents == null");
    259         RETURN_VOID();
    260     }
    261 
    262     /* TODO: Avoid making a copy of the array. (note array *is* modified) */
    263     length = fileContentsObj->length;
    264     pBytes = (u1*) malloc(length);
    265 
    266     if (pBytes == NULL) {
    267         dvmThrowRuntimeException("unable to allocate DEX memory");
    268         RETURN_VOID();
    269     }
    270 
    271     memcpy(pBytes, fileContentsObj->contents, length);
    272 
    273     if (dvmRawDexFileOpenArray(pBytes, length, &pRawDexFile) != 0) {
    274         ALOGV("Unable to open in-memory DEX file");
    275         free(pBytes);
    276         dvmThrowRuntimeException("unable to open in-memory DEX file");
    277         RETURN_VOID();
    278     }
    279 
    280     ALOGV("Opening in-memory DEX");
    281     pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
    282     pDexOrJar->isDex = true;
    283     pDexOrJar->pRawDexFile = pRawDexFile;
    284     pDexOrJar->pDexMemory = pBytes;
    285     pDexOrJar->fileName = strdup("<memory>"); // Needs to be free()able.
    286     addToDexFileTable(pDexOrJar);
    287 
    288     RETURN_PTR(pDexOrJar);
    289 }
    290 
    291 /*
    292  * private static void closeDexFile(int cookie)
    293  *
    294  * Release resources associated with a user-loaded DEX file.
    295  */
    296 static void Dalvik_dalvik_system_DexFile_closeDexFile(const u4* args,
    297     JValue* pResult)
    298 {
    299     int cookie = args[0];
    300     DexOrJar* pDexOrJar = (DexOrJar*) cookie;
    301 
    302     if (pDexOrJar == NULL)
    303         RETURN_VOID();
    304     if (!validateCookie(cookie))
    305         RETURN_VOID();
    306 
    307     ALOGV("Closing DEX file %p (%s)", pDexOrJar, pDexOrJar->fileName);
    308 
    309     /*
    310      * We can't just free arbitrary DEX files because they have bits and
    311      * pieces of loaded classes.  The only exception to this rule is if
    312      * they were never used to load classes.
    313      *
    314      * If we can't free them here, dvmInternalNativeShutdown() will free
    315      * them when the VM shuts down.
    316      */
    317     if (pDexOrJar->okayToFree) {
    318         u4 hash = (u4) pDexOrJar;
    319         dvmHashTableLock(gDvm.userDexFiles);
    320         if (!dvmHashTableRemove(gDvm.userDexFiles, hash, pDexOrJar)) {
    321             ALOGW("WARNING: could not remove '%s' from DEX hash table",
    322                 pDexOrJar->fileName);
    323         }
    324         dvmHashTableUnlock(gDvm.userDexFiles);
    325         ALOGV("+++ freeing DexFile '%s' resources", pDexOrJar->fileName);
    326         dvmFreeDexOrJar(pDexOrJar);
    327     } else {
    328         ALOGV("+++ NOT freeing DexFile '%s' resources", pDexOrJar->fileName);
    329     }
    330 
    331     RETURN_VOID();
    332 }
    333 
    334 /*
    335  * private static Class defineClass(String name, ClassLoader loader,
    336  *      int cookie)
    337  *
    338  * Load a class from a DEX file.  This is roughly equivalent to defineClass()
    339  * in a regular VM -- it's invoked by the class loader to cause the
    340  * creation of a specific class.  The difference is that the search for and
    341  * reading of the bytes is done within the VM.
    342  *
    343  * The class name is a "binary name", e.g. "java.lang.String".
    344  *
    345  * Returns a null pointer with no exception if the class was not found.
    346  * Throws an exception on other failures.
    347  */
    348 static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args,
    349     JValue* pResult)
    350 {
    351     StringObject* nameObj = (StringObject*) args[0];
    352     Object* loader = (Object*) args[1];
    353     int cookie = args[2];
    354     ClassObject* clazz = NULL;
    355     DexOrJar* pDexOrJar = (DexOrJar*) cookie;
    356     DvmDex* pDvmDex;
    357     char* name;
    358     char* descriptor;
    359 
    360     name = dvmCreateCstrFromString(nameObj);
    361     descriptor = dvmDotToDescriptor(name);
    362     ALOGV("--- Explicit class load '%s' l=%p c=0x%08x",
    363         descriptor, loader, cookie);
    364     free(name);
    365 
    366     if (!validateCookie(cookie))
    367         RETURN_VOID();
    368 
    369     if (pDexOrJar->isDex)
    370         pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
    371     else
    372         pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
    373 
    374     /* once we load something, we can't unmap the storage */
    375     pDexOrJar->okayToFree = false;
    376 
    377     clazz = dvmDefineClass(pDvmDex, descriptor, loader);
    378     Thread* self = dvmThreadSelf();
    379     if (dvmCheckException(self)) {
    380         /*
    381          * If we threw a "class not found" exception, stifle it, since the
    382          * contract in the higher method says we simply return null if
    383          * the class is not found.
    384          */
    385         Object* excep = dvmGetException(self);
    386         if (strcmp(excep->clazz->descriptor,
    387                    "Ljava/lang/ClassNotFoundException;") == 0 ||
    388             strcmp(excep->clazz->descriptor,
    389                    "Ljava/lang/NoClassDefFoundError;") == 0)
    390         {
    391             dvmClearException(self);
    392         }
    393         clazz = NULL;
    394     }
    395 
    396     free(descriptor);
    397     RETURN_PTR(clazz);
    398 }
    399 
    400 /*
    401  * private static String[] getClassNameList(int cookie)
    402  *
    403  * Returns a String array that holds the names of all classes in the
    404  * specified DEX file.
    405  */
    406 static void Dalvik_dalvik_system_DexFile_getClassNameList(const u4* args,
    407     JValue* pResult)
    408 {
    409     int cookie = args[0];
    410     DexOrJar* pDexOrJar = (DexOrJar*) cookie;
    411     Thread* self = dvmThreadSelf();
    412 
    413     if (!validateCookie(cookie))
    414         RETURN_VOID();
    415 
    416     DvmDex* pDvmDex;
    417     if (pDexOrJar->isDex)
    418         pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
    419     else
    420         pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
    421     assert(pDvmDex != NULL);
    422     DexFile* pDexFile = pDvmDex->pDexFile;
    423 
    424     int count = pDexFile->pHeader->classDefsSize;
    425     ClassObject* arrayClass =
    426         dvmFindArrayClassForElement(gDvm.classJavaLangString);
    427     ArrayObject* stringArray =
    428         dvmAllocArrayByClass(arrayClass, count, ALLOC_DEFAULT);
    429     if (stringArray == NULL) {
    430         /* probably OOM */
    431         ALOGD("Failed allocating array of %d strings", count);
    432         assert(dvmCheckException(self));
    433         RETURN_VOID();
    434     }
    435 
    436     int i;
    437     for (i = 0; i < count; i++) {
    438         const DexClassDef* pClassDef = dexGetClassDef(pDexFile, i);
    439         const char* descriptor =
    440             dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
    441 
    442         char* className = dvmDescriptorToDot(descriptor);
    443         StringObject* str = dvmCreateStringFromCstr(className);
    444         dvmSetObjectArrayElement(stringArray, i, (Object *)str);
    445         dvmReleaseTrackedAlloc((Object *)str, self);
    446         free(className);
    447     }
    448 
    449     dvmReleaseTrackedAlloc((Object*)stringArray, self);
    450     RETURN_PTR(stringArray);
    451 }
    452 
    453 /*
    454  * public static boolean isDexOptNeeded(String fileName)
    455  *         throws FileNotFoundException, IOException
    456  *
    457  * Returns true if the VM believes that the apk/jar file is out of date
    458  * and should be passed through "dexopt" again.
    459  *
    460  * @param fileName the absolute path to the apk/jar file to examine.
    461  * @return true if dexopt should be called on the file, false otherwise.
    462  * @throws java.io.FileNotFoundException if fileName is not readable,
    463  *         not a file, or not present.
    464  * @throws java.io.IOException if fileName is not a valid apk/jar file or
    465  *         if problems occur while parsing it.
    466  * @throws java.lang.NullPointerException if fileName is null.
    467  * @throws dalvik.system.StaleDexCacheError if the optimized dex file
    468  *         is stale but exists on a read-only partition.
    469  */
    470 static void Dalvik_dalvik_system_DexFile_isDexOptNeeded(const u4* args,
    471     JValue* pResult)
    472 {
    473     StringObject* nameObj = (StringObject*) args[0];
    474     char* name;
    475     DexCacheStatus status;
    476     int result;
    477 
    478     name = dvmCreateCstrFromString(nameObj);
    479     if (name == NULL) {
    480         dvmThrowNullPointerException("fileName == null");
    481         RETURN_VOID();
    482     }
    483     if (access(name, R_OK) != 0) {
    484         dvmThrowFileNotFoundException(name);
    485         free(name);
    486         RETURN_VOID();
    487     }
    488     status = dvmDexCacheStatus(name);
    489     ALOGV("dvmDexCacheStatus(%s) returned %d", name, status);
    490 
    491     result = true;
    492     switch (status) {
    493     default: //FALLTHROUGH
    494     case DEX_CACHE_BAD_ARCHIVE:
    495         dvmThrowIOException(name);
    496         result = -1;
    497         break;
    498     case DEX_CACHE_OK:
    499         result = false;
    500         break;
    501     case DEX_CACHE_STALE:
    502         result = true;
    503         break;
    504     case DEX_CACHE_STALE_ODEX:
    505         dvmThrowStaleDexCacheError(name);
    506         result = -1;
    507         break;
    508     }
    509     free(name);
    510 
    511     if (result >= 0) {
    512         RETURN_BOOLEAN(result);
    513     } else {
    514         RETURN_VOID();
    515     }
    516 }
    517 
    518 const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
    519     { "openDexFile",        "(Ljava/lang/String;Ljava/lang/String;I)I",
    520         Dalvik_dalvik_system_DexFile_openDexFile },
    521     { "openDexFile",        "([B)I",
    522         Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
    523     { "closeDexFile",       "(I)V",
    524         Dalvik_dalvik_system_DexFile_closeDexFile },
    525     { "defineClass",        "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;",
    526         Dalvik_dalvik_system_DexFile_defineClass },
    527     { "getClassNameList",   "(I)[Ljava/lang/String;",
    528         Dalvik_dalvik_system_DexFile_getClassNameList },
    529     { "isDexOptNeeded",     "(Ljava/lang/String;)Z",
    530         Dalvik_dalvik_system_DexFile_isDexOptNeeded },
    531     { NULL, NULL, NULL },
    532 };
    533