dalvik_system_DexFile.cc revision f1d3455064792ac1c486a4a9c24279a37b4af473
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#include <unistd.h> 18 19#include "base/logging.h" 20#include "class_linker.h" 21#include "common_throws.h" 22#include "dex_file-inl.h" 23#include "gc/space/image_space.h" 24#include "gc/space/space-inl.h" 25#include "image.h" 26#include "jni_internal.h" 27#include "mirror/class_loader.h" 28#include "mirror/object-inl.h" 29#include "mirror/string.h" 30#include "oat.h" 31#include "os.h" 32#include "runtime.h" 33#include "scoped_thread_state_change.h" 34#include "ScopedLocalRef.h" 35#include "ScopedUtfChars.h" 36#include "toStringArray.h" 37#include "zip_archive.h" 38 39namespace art { 40 41// A smart pointer that provides read-only access to a Java string's UTF chars. 42// Unlike libcore's NullableScopedUtfChars, this will *not* throw NullPointerException if 43// passed a null jstring. The correct idiom is: 44// 45// NullableScopedUtfChars name(env, javaName); 46// if (env->ExceptionCheck()) { 47// return NULL; 48// } 49// // ... use name.c_str() 50// 51// TODO: rewrite to get rid of this, or change ScopedUtfChars to offer this option. 52class NullableScopedUtfChars { 53 public: 54 NullableScopedUtfChars(JNIEnv* env, jstring s) : mEnv(env), mString(s) { 55 mUtfChars = (s != NULL) ? env->GetStringUTFChars(s, NULL) : NULL; 56 } 57 58 ~NullableScopedUtfChars() { 59 if (mUtfChars) { 60 mEnv->ReleaseStringUTFChars(mString, mUtfChars); 61 } 62 } 63 64 const char* c_str() const { 65 return mUtfChars; 66 } 67 68 size_t size() const { 69 return strlen(mUtfChars); 70 } 71 72 // Element access. 73 const char& operator[](size_t n) const { 74 return mUtfChars[n]; 75 } 76 77 private: 78 JNIEnv* mEnv; 79 jstring mString; 80 const char* mUtfChars; 81 82 // Disallow copy and assignment. 83 NullableScopedUtfChars(const NullableScopedUtfChars&); 84 void operator=(const NullableScopedUtfChars&); 85}; 86 87static jint DexFile_openDexFile(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) { 88 ScopedUtfChars sourceName(env, javaSourceName); 89 if (sourceName.c_str() == NULL) { 90 return 0; 91 } 92 std::string source(sourceName.c_str()); 93 NullableScopedUtfChars outputName(env, javaOutputName); 94 if (env->ExceptionCheck()) { 95 return 0; 96 } 97 ScopedObjectAccess soa(env); 98 const DexFile* dex_file; 99 if (outputName.c_str() == NULL) { 100 dex_file = Runtime::Current()->GetClassLinker()->FindDexFileInOatFileFromDexLocation(source); 101 } else { 102 std::string output(outputName.c_str()); 103 dex_file = 104 Runtime::Current()->GetClassLinker()->FindOrCreateOatFileForDexLocation(source, output); 105 } 106 if (dex_file == NULL) { 107 LOG(WARNING) << "Failed to open dex file: " << source; 108 ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); 109 soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/IOException;", 110 "Unable to open dex file: %s", source.c_str()); 111 return 0; 112 } 113 return static_cast<jint>(reinterpret_cast<uintptr_t>(dex_file)); 114} 115 116static const DexFile* toDexFile(int dex_file_address) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 117 const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address)); 118 if (dex_file == NULL) { 119 ThrowNullPointerException(NULL, "dex_file == null"); 120 } 121 return dex_file; 122} 123 124static void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) { 125 const DexFile* dex_file; 126 { 127 ScopedObjectAccess soa(env); 128 dex_file = toDexFile(cookie); 129 } 130 if (dex_file == NULL) { 131 return; 132 } 133 if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) { 134 return; 135 } 136 delete dex_file; 137} 138 139static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader, 140 jint cookie) { 141 ScopedObjectAccess soa(env); 142 const DexFile* dex_file = toDexFile(cookie); 143 if (dex_file == NULL) { 144 return NULL; 145 } 146 ScopedUtfChars class_name(env, javaName); 147 if (class_name.c_str() == NULL) { 148 return NULL; 149 } 150 const std::string descriptor(DotToDescriptor(class_name.c_str())); 151 const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor); 152 if (dex_class_def == NULL) { 153 return NULL; 154 } 155 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); 156 class_linker->RegisterDexFile(*dex_file); 157 mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(javaLoader); 158 mirror::Class* result = class_linker->DefineClass(descriptor, class_loader, *dex_file, *dex_class_def); 159 return soa.AddLocalReference<jclass>(result); 160} 161 162static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) { 163 const DexFile* dex_file; 164 { 165 ScopedObjectAccess soa(env); 166 dex_file = toDexFile(cookie); 167 } 168 if (dex_file == NULL) { 169 return NULL; 170 } 171 172 std::vector<std::string> class_names; 173 for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { 174 const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); 175 const char* descriptor = dex_file->GetClassDescriptor(class_def); 176 class_names.push_back(DescriptorToDot(descriptor)); 177 } 178 return toStringArray(env, class_names); 179} 180 181static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) { 182 bool debug_logging = false; 183 184 ScopedUtfChars filename(env, javaFilename); 185 if (filename.c_str() == NULL) { 186 LOG(ERROR) << "DexFile_isDexOptNeeded null filename"; 187 return JNI_TRUE; 188 } 189 190 if (!OS::FileExists(filename.c_str())) { 191 LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename.c_str() << "' does not exist"; 192 ScopedObjectAccess soa(env); 193 ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); 194 soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/FileNotFoundException;", 195 "%s", filename.c_str()); 196 return JNI_TRUE; 197 } 198 199 // Always treat elements of the bootclasspath as up-to-date. The 200 // fact that code is running at all means that this should be true. 201 Runtime* runtime = Runtime::Current(); 202 ClassLinker* class_linker = runtime->GetClassLinker(); 203 const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath(); 204 for (size_t i = 0; i < boot_class_path.size(); i++) { 205 if (boot_class_path[i]->GetLocation() == filename.c_str()) { 206 if (debug_logging) { 207 LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename.c_str(); 208 } 209 return JNI_FALSE; 210 } 211 } 212 213 // Check if we have an odex file next to the dex file. 214 std::string odex_filename(OatFile::DexFilenameToOdexFilename(filename.c_str())); 215 UniquePtr<const OatFile> oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false)); 216 if (oat_file.get() != NULL) { 217 ScopedObjectAccess soa(env); 218 const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename.c_str()); 219 if (oat_dex_file == NULL) { 220 if (debug_logging) { 221 LOG(INFO) << "DexFile_isDexOptNeeded GetOatDexFile failed"; 222 } 223 } else { 224 uint32_t location_checksum; 225 // If we have no classes.dex checksum such as in a user build, assume up-to-date. 226 if (!DexFile::GetChecksum(filename.c_str(), location_checksum)) { 227 if (debug_logging) { 228 LOG(INFO) << "DexFile_isDexOptNeeded ignoring precompiled stripped file: " 229 << filename.c_str(); 230 } 231 return JNI_FALSE; 232 } 233 if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) { 234 if (debug_logging) { 235 LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << odex_filename 236 << " is up-to-date checksum compared to " << filename.c_str(); 237 } 238 return JNI_FALSE; 239 } 240 } 241 } 242 243 // Check if we have an oat file in the cache 244 std::string cache_location(GetDalvikCacheFilenameOrDie(filename.c_str())); 245 oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL, false)); 246 if (oat_file.get() == NULL) { 247 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 248 << " does not exist for " << filename.c_str(); 249 return JNI_TRUE; 250 } 251 252 gc::Heap* heap = runtime->GetHeap(); 253 const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); 254 // TODO: C++0x auto 255 typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It; 256 for (It it = spaces.begin(), end = spaces.end(); it != end; ++it) { 257 if ((*it)->IsImageSpace()) { 258 // TODO: Ensure this works with multiple image spaces. 259 const ImageHeader& image_header = (*it)->AsImageSpace()->GetImageHeader(); 260 if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() != image_header.GetOatChecksum()) { 261 ScopedObjectAccess soa(env); 262 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 263 << " has out-of-date oat checksum compared to " 264 << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); 265 return JNI_TRUE; 266 } 267 if (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin() 268 != reinterpret_cast<uint32_t>(image_header.GetOatDataBegin())) { 269 ScopedObjectAccess soa(env); 270 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 271 << " has out-of-date oat begin compared to " 272 << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); 273 return JNI_TRUE; 274 } 275 } 276 } 277 278 ScopedObjectAccess soa(env); 279 uint32_t location_checksum; 280 if (!DexFile::GetChecksum(filename.c_str(), location_checksum)) { 281 LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename.c_str(); 282 return JNI_TRUE; 283 } 284 285 if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) { 286 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 287 << " has out-of-date checksum compared to " << filename.c_str(); 288 return JNI_TRUE; 289 } 290 291 if (debug_logging) { 292 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 293 << " is up-to-date for " << filename.c_str(); 294 } 295 return JNI_FALSE; 296} 297 298static JNINativeMethod gMethods[] = { 299 NATIVE_METHOD(DexFile, closeDexFile, "(I)V"), 300 NATIVE_METHOD(DexFile, defineClassNative, "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;"), 301 NATIVE_METHOD(DexFile, getClassNameList, "(I)[Ljava/lang/String;"), 302 NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"), 303 NATIVE_METHOD(DexFile, openDexFile, "(Ljava/lang/String;Ljava/lang/String;I)I"), 304}; 305 306void register_dalvik_system_DexFile(JNIEnv* env) { 307 REGISTER_NATIVE_METHODS("dalvik/system/DexFile"); 308} 309 310} // namespace art 311