• 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 /*
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  */
dvmFreeDexOrJar(void * vptr)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  */
hashcmpDexOrJar(const void * tableVal,const void * newVal)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  */
validateCookie(int cookie)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  */
Dalvik_dalvik_system_DexFile_openDexFile(const u4 * args,JValue * pResult)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  */
Dalvik_dalvik_system_DexFile_closeDexFile(const u4 * args,JValue * pResult)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  */
Dalvik_dalvik_system_DexFile_defineClass(const u4 * args,JValue * pResult)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  */
Dalvik_dalvik_system_DexFile_getClassNameList(const u4 * args,JValue * pResult)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  */
Dalvik_dalvik_system_DexFile_isDexOptNeeded(const u4 * args,JValue * pResult)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