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