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