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