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