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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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