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