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 */ 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 */ 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 */ 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 */ 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 int flags = args[2]; 115 DexOrJar* pDexOrJar = NULL; 116 JarFile* pJarFile; 117 RawDexFile* pRawDexFile; 118 char* sourceName; 119 char* outputName; 120 121 if (sourceNameObj == NULL) { 122 dvmThrowException("Ljava/lang/NullPointerException;", NULL); 123 RETURN_VOID(); 124 } 125 126 sourceName = dvmCreateCstrFromString(sourceNameObj); 127 if (outputNameObj != NULL) 128 outputName = dvmCreateCstrFromString(outputNameObj); 129 else 130 outputName = NULL; 131 132 /* 133 * We have to deal with the possibility that somebody might try to 134 * open one of our bootstrap class DEX files. The set of dependencies 135 * will be different, and hence the results of optimization might be 136 * different, which means we'd actually need to have two versions of 137 * the optimized DEX: one that only knows about part of the boot class 138 * path, and one that knows about everything in it. The latter might 139 * optimize field/method accesses based on a class that appeared later 140 * in the class path. 141 * 142 * We can't let the user-defined class loader open it and start using 143 * the classes, since the optimized form of the code skips some of 144 * the method and field resolution that we would ordinarily do, and 145 * we'd have the wrong semantics. 146 * 147 * We have to reject attempts to manually open a DEX file from the boot 148 * class path. The easiest way to do this is by filename, which works 149 * out because variations in name (e.g. "/system/framework/./ext.jar") 150 * result in us hitting a different dalvik-cache entry. It's also fine 151 * if the caller specifies their own output file. 152 */ 153 if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) { 154 LOGW("Refusing to reopen boot DEX '%s'\n", sourceName); 155 dvmThrowException("Ljava/io/IOException;", 156 "Re-opening BOOTCLASSPATH DEX files is not allowed"); 157 free(sourceName); 158 RETURN_VOID(); 159 } 160 161 /* 162 * Try to open it directly as a DEX. If that fails, try it as a Zip 163 * with a "classes.dex" inside. 164 */ 165 if (dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) { 166 LOGV("Opening DEX file '%s' (DEX)\n", sourceName); 167 168 pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); 169 pDexOrJar->isDex = true; 170 pDexOrJar->pRawDexFile = pRawDexFile; 171 } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) { 172 LOGV("Opening DEX file '%s' (Jar)\n", sourceName); 173 174 pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); 175 pDexOrJar->isDex = false; 176 pDexOrJar->pJarFile = pJarFile; 177 } else { 178 LOGV("Unable to open DEX file '%s'\n", sourceName); 179 dvmThrowException("Ljava/io/IOException;", "unable to open DEX file"); 180 } 181 182 if (pDexOrJar != NULL) { 183 pDexOrJar->fileName = sourceName; 184 185 /* add to hash table */ 186 u4 hash = dvmComputeUtf8Hash(sourceName); 187 void* result; 188 dvmHashTableLock(gDvm.userDexFiles); 189 result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, 190 hashcmpDexOrJar, true); 191 dvmHashTableUnlock(gDvm.userDexFiles); 192 if (result != pDexOrJar) { 193 LOGE("Pointer has already been added?\n"); 194 dvmAbort(); 195 } 196 197 pDexOrJar->okayToFree = true; 198 } else 199 free(sourceName); 200 201 RETURN_PTR(pDexOrJar); 202 } 203 204 /* 205 * private static void closeDexFile(int cookie) 206 * 207 * Release resources associated with a user-loaded DEX file. 208 */ 209 static void Dalvik_dalvik_system_DexFile_closeDexFile(const u4* args, 210 JValue* pResult) 211 { 212 int cookie = args[0]; 213 DexOrJar* pDexOrJar = (DexOrJar*) cookie; 214 215 if (pDexOrJar == NULL) 216 RETURN_VOID(); 217 218 LOGV("Closing DEX file %p (%s)\n", pDexOrJar, pDexOrJar->fileName); 219 220 if (!validateCookie(cookie)) 221 RETURN_VOID(); 222 223 /* 224 * We can't just free arbitrary DEX files because they have bits and 225 * pieces of loaded classes. The only exception to this rule is if 226 * they were never used to load classes. 227 * 228 * If we can't free them here, dvmInternalNativeShutdown() will free 229 * them when the VM shuts down. 230 */ 231 if (pDexOrJar->okayToFree) { 232 u4 hash = dvmComputeUtf8Hash(pDexOrJar->fileName); 233 dvmHashTableLock(gDvm.userDexFiles); 234 if (!dvmHashTableRemove(gDvm.userDexFiles, hash, pDexOrJar)) { 235 LOGW("WARNING: could not remove '%s' from DEX hash table\n", 236 pDexOrJar->fileName); 237 } 238 dvmHashTableUnlock(gDvm.userDexFiles); 239 LOGV("+++ freeing DexFile '%s' resources\n", pDexOrJar->fileName); 240 dvmFreeDexOrJar(pDexOrJar); 241 } else { 242 LOGV("+++ NOT freeing DexFile '%s' resources\n", pDexOrJar->fileName); 243 } 244 245 RETURN_VOID(); 246 } 247 248 /* 249 * private static Class defineClass(String name, ClassLoader loader, 250 * int cookie, ProtectionDomain pd) 251 * 252 * Load a class from a DEX file. This is roughly equivalent to defineClass() 253 * in a regular VM -- it's invoked by the class loader to cause the 254 * creation of a specific class. The difference is that the search for and 255 * reading of the bytes is done within the VM. 256 * 257 * The class name is a "binary name", e.g. "java.lang.String". 258 * 259 * Returns a null pointer with no exception if the class was not found. 260 * Throws an exception on other failures. 261 */ 262 static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args, 263 JValue* pResult) 264 { 265 StringObject* nameObj = (StringObject*) args[0]; 266 Object* loader = (Object*) args[1]; 267 int cookie = args[2]; 268 Object* pd = (Object*) args[3]; 269 ClassObject* clazz = NULL; 270 DexOrJar* pDexOrJar = (DexOrJar*) cookie; 271 DvmDex* pDvmDex; 272 char* name; 273 char* descriptor; 274 275 name = dvmCreateCstrFromString(nameObj); 276 descriptor = dvmDotToDescriptor(name); 277 LOGV("--- Explicit class load '%s' 0x%08x\n", descriptor, cookie); 278 free(name); 279 280 if (!validateCookie(cookie)) 281 RETURN_VOID(); 282 283 if (pDexOrJar->isDex) 284 pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile); 285 else 286 pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile); 287 288 /* once we load something, we can't unmap the storage */ 289 pDexOrJar->okayToFree = false; 290 291 clazz = dvmDefineClass(pDvmDex, descriptor, loader); 292 Thread* self = dvmThreadSelf(); 293 if (dvmCheckException(self)) { 294 /* 295 * If we threw a "class not found" exception, stifle it, since the 296 * contract in the higher method says we simply return null if 297 * the class is not found. 298 */ 299 Object* excep = dvmGetException(self); 300 if (strcmp(excep->clazz->descriptor, 301 "Ljava/lang/ClassNotFoundException;") == 0 || 302 strcmp(excep->clazz->descriptor, 303 "Ljava/lang/NoClassDefFoundError;") == 0) 304 { 305 dvmClearException(self); 306 } 307 clazz = NULL; 308 } 309 310 /* 311 * Set the ProtectionDomain -- do we need this to happen before we 312 * link the class and make it available? If so, we need to pass it 313 * through dvmDefineClass (and figure out some other 314 * stuff, like where it comes from for bootstrap classes). 315 */ 316 if (clazz != NULL) { 317 //LOGI("SETTING pd '%s' to %p\n", clazz->descriptor, pd); 318 dvmSetFieldObject((Object*) clazz, gDvm.offJavaLangClass_pd, pd); 319 } 320 321 free(descriptor); 322 RETURN_PTR(clazz); 323 } 324 325 /* 326 * private static String[] getClassNameList(int cookie) 327 * 328 * Returns a String array that holds the names of all classes in the 329 * specified DEX file. 330 */ 331 static void Dalvik_dalvik_system_DexFile_getClassNameList(const u4* args, 332 JValue* pResult) 333 { 334 int cookie = args[0]; 335 DexOrJar* pDexOrJar = (DexOrJar*) cookie; 336 DvmDex* pDvmDex; 337 DexFile* pDexFile; 338 ArrayObject* stringArray; 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 RETURN_VOID(); // should be an OOM pending 355 356 StringObject** contents = (StringObject**) stringArray->contents; 357 int i; 358 for (i = 0; i < count; i++) { 359 const DexClassDef* pClassDef = dexGetClassDef(pDexFile, i); 360 const char* descriptor = 361 dexStringByTypeIdx(pDexFile, pClassDef->classIdx); 362 363 char* className = dvmDescriptorToDot(descriptor); 364 contents[i] = dvmCreateStringFromCstr(className, ALLOC_DEFAULT); 365 dvmReleaseTrackedAlloc((Object*) contents[i], NULL); 366 free(className); 367 } 368 369 dvmReleaseTrackedAlloc((Object*)stringArray, NULL); 370 RETURN_PTR(stringArray); 371 } 372 373 /* 374 * public static boolean isDexOptNeeded(String apkName) 375 * throws FileNotFoundException, IOException 376 * 377 * Returns true if the VM believes that the apk/jar file is out of date 378 * and should be passed through "dexopt" again. 379 * 380 * @param fileName the absolute path to the apk/jar file to examine. 381 * @return true if dexopt should be called on the file, false otherwise. 382 * @throws java.io.FileNotFoundException if fileName is not readable, 383 * not a file, or not present. 384 * @throws java.io.IOException if fileName is not a valid apk/jar file or 385 * if problems occur while parsing it. 386 * @throws java.lang.NullPointerException if fileName is null. 387 * @throws dalvik.system.StaleDexCacheError if the optimized dex file 388 * is stale but exists on a read-only partition. 389 */ 390 static void Dalvik_dalvik_system_DexFile_isDexOptNeeded(const u4* args, 391 JValue* pResult) 392 { 393 StringObject* nameObj = (StringObject*) args[0]; 394 char* name; 395 DexCacheStatus status; 396 int result; 397 398 name = dvmCreateCstrFromString(nameObj); 399 if (name == NULL) { 400 dvmThrowException("Ljava/lang/NullPointerException;", NULL); 401 RETURN_VOID(); 402 } 403 if (access(name, R_OK) != 0) { 404 dvmThrowException("Ljava/io/FileNotFoundException;", name); 405 free(name); 406 RETURN_VOID(); 407 } 408 status = dvmDexCacheStatus(name); 409 LOGV("dvmDexCacheStatus(%s) returned %d\n", name, status); 410 411 result = true; 412 switch (status) { 413 default: //FALLTHROUGH 414 case DEX_CACHE_BAD_ARCHIVE: 415 dvmThrowException("Ljava/io/IOException;", name); 416 result = -1; 417 break; 418 case DEX_CACHE_OK: 419 result = false; 420 break; 421 case DEX_CACHE_STALE: 422 result = true; 423 break; 424 case DEX_CACHE_STALE_ODEX: 425 dvmThrowException("Ldalvik/system/StaleDexCacheError;", name); 426 result = -1; 427 break; 428 } 429 free(name); 430 431 if (result >= 0) { 432 RETURN_BOOLEAN(result); 433 } else { 434 RETURN_VOID(); 435 } 436 } 437 438 const DalvikNativeMethod dvm_dalvik_system_DexFile[] = { 439 { "openDexFile", "(Ljava/lang/String;Ljava/lang/String;I)I", 440 Dalvik_dalvik_system_DexFile_openDexFile }, 441 { "closeDexFile", "(I)V", 442 Dalvik_dalvik_system_DexFile_closeDexFile }, 443 { "defineClass", "(Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/security/ProtectionDomain;)Ljava/lang/Class;", 444 Dalvik_dalvik_system_DexFile_defineClass }, 445 { "getClassNameList", "(I)[Ljava/lang/String;", 446 Dalvik_dalvik_system_DexFile_getClassNameList }, 447 { "isDexOptNeeded", "(Ljava/lang/String;)Z", 448 Dalvik_dalvik_system_DexFile_isDexOptNeeded }, 449 { NULL, NULL, NULL }, 450 }; 451 452