• 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     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  */
Dalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4 * args,JValue * pResult)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  */
Dalvik_dalvik_system_DexFile_closeDexFile(const u4 * args,JValue * pResult)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  */
Dalvik_dalvik_system_DexFile_defineClass(const u4 * args,JValue * pResult)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  */
Dalvik_dalvik_system_DexFile_getClassNameList(const u4 * args,JValue * pResult)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  */
Dalvik_dalvik_system_DexFile_isDexOptNeeded(const u4 * args,JValue * pResult)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