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