• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
hasDexExtension(const char * name)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  */
dvmFreeDexOrJar(void * vptr)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  */
hashcmpDexOrJar(const void * tableVal,const void * newVal)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  */
validateCookie(int cookie)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  */
addToDexFileTable(DexOrJar * pDexOrJar)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  */
Dalvik_dalvik_system_DexFile_openDexFile(const u4 * args,JValue * pResult)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     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  */
Dalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4 * args,JValue * pResult)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  */
Dalvik_dalvik_system_DexFile_closeDexFile(const u4 * args,JValue * pResult)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 defineClass(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  */
Dalvik_dalvik_system_DexFile_defineClass(const u4 * args,JValue * pResult)349 static void Dalvik_dalvik_system_DexFile_defineClass(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  */
Dalvik_dalvik_system_DexFile_getClassNameList(const u4 * args,JValue * pResult)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  */
Dalvik_dalvik_system_DexFile_isDexOptNeeded(const u4 * args,JValue * pResult)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     { "openDexFile",        "(Ljava/lang/String;Ljava/lang/String;I)I",
521         Dalvik_dalvik_system_DexFile_openDexFile },
522     { "openDexFile",        "([B)I",
523         Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
524     { "closeDexFile",       "(I)V",
525         Dalvik_dalvik_system_DexFile_closeDexFile },
526     { "defineClass",        "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;",
527         Dalvik_dalvik_system_DexFile_defineClass },
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