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 */ 26static 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 */ 37struct 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 */ 49void 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 */ 69static 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 */ 81static 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 */ 107static 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 openDexFileNative(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 */ 151static void Dalvik_dalvik_system_DexFile_openDexFileNative(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 */ 249static 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 */ 297static 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 defineClassNative(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 */ 349static void Dalvik_dalvik_system_DexFile_defineClassNative(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 */ 407static 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 */ 471static 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 519const DalvikNativeMethod dvm_dalvik_system_DexFile[] = { 520 { "openDexFileNative", "(Ljava/lang/String;Ljava/lang/String;I)I", 521 Dalvik_dalvik_system_DexFile_openDexFileNative }, 522 { "openDexFile", "([B)I", 523 Dalvik_dalvik_system_DexFile_openDexFile_bytearray }, 524 { "closeDexFile", "(I)V", 525 Dalvik_dalvik_system_DexFile_closeDexFile }, 526 { "defineClassNative", "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;", 527 Dalvik_dalvik_system_DexFile_defineClassNative }, 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