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