dalvik_system_DexFile.cc revision 60836d5a9bcf8b30984aae4279a4f6233b0bf622
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_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) { 88 ScopedUtfChars sourceName(env, javaSourceName); 89 if (sourceName.c_str() == NULL) { 90 return 0; 91 } 92 NullableScopedUtfChars outputName(env, javaOutputName); 93 if (env->ExceptionCheck()) { 94 return 0; 95 } 96 97 uint32_t dex_location_checksum; 98 uint32_t* dex_location_checksum_pointer = &dex_location_checksum; 99 std::string error_msg; 100 if (!DexFile::GetChecksum(sourceName.c_str(), dex_location_checksum_pointer, &error_msg)) { 101 dex_location_checksum_pointer = NULL; 102 } 103 104 ClassLinker* linker = Runtime::Current()->GetClassLinker(); 105 const DexFile* dex_file; 106 if (outputName.c_str() == nullptr) { 107 // FindOrCreateOatFileForDexLocation can tolerate a missing dex_location_checksum 108 error_msg.clear(); 109 dex_file = linker->FindDexFileInOatFileFromDexLocation(sourceName.c_str(), 110 dex_location_checksum_pointer, &error_msg); 111 } else { 112 // FindOrCreateOatFileForDexLocation requires the dex_location_checksum 113 if (dex_location_checksum_pointer == NULL) { 114 ScopedObjectAccess soa(env); 115 DCHECK(!error_msg.empty()); 116 ThrowIOException("%s", error_msg.c_str()); 117 return 0; 118 } 119 dex_file = linker->FindOrCreateOatFileForDexLocation(sourceName.c_str(), dex_location_checksum, 120 outputName.c_str(), &error_msg); 121 } 122 if (dex_file == nullptr) { 123 ScopedObjectAccess soa(env); 124 CHECK(!error_msg.empty()); 125 ThrowIOException("%s", error_msg.c_str()); 126 return 0; 127 } 128 return static_cast<jint>(reinterpret_cast<uintptr_t>(dex_file)); 129} 130 131static const DexFile* toDexFile(int dex_file_address, JNIEnv* env) { 132 const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address)); 133 if (UNLIKELY(dex_file == nullptr)) { 134 ScopedObjectAccess soa(env); 135 ThrowNullPointerException(NULL, "dex_file == null"); 136 } 137 return dex_file; 138} 139 140static void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) { 141 const DexFile* dex_file; 142 dex_file = toDexFile(cookie, env); 143 if (dex_file == nullptr) { 144 return; 145 } 146 if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) { 147 return; 148 } 149 delete dex_file; 150} 151 152static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader, 153 jint cookie) { 154 const DexFile* dex_file = toDexFile(cookie, env); 155 if (dex_file == NULL) { 156 VLOG(class_linker) << "Failed to find dex_file"; 157 return NULL; 158 } 159 ScopedUtfChars class_name(env, javaName); 160 if (class_name.c_str() == NULL) { 161 VLOG(class_linker) << "Failed to find class_name"; 162 return NULL; 163 } 164 const std::string descriptor(DotToDescriptor(class_name.c_str())); 165 const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str()); 166 if (dex_class_def == NULL) { 167 VLOG(class_linker) << "Failed to find dex_class_def"; 168 return NULL; 169 } 170 ScopedObjectAccess soa(env); 171 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); 172 class_linker->RegisterDexFile(*dex_file); 173 SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(javaLoader)); 174 mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file, 175 *dex_class_def); 176 VLOG(class_linker) << "DexFile_defineClassNative returning " << result; 177 return soa.AddLocalReference<jclass>(result); 178} 179 180static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) { 181 const DexFile* dex_file; 182 dex_file = toDexFile(cookie, env); 183 if (dex_file == nullptr) { 184 return nullptr; 185 } 186 187 std::vector<std::string> class_names; 188 for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { 189 const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); 190 const char* descriptor = dex_file->GetClassDescriptor(class_def); 191 class_names.push_back(DescriptorToDot(descriptor)); 192 } 193 return toStringArray(env, class_names); 194} 195 196static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) { 197 const bool kVerboseLogging = false; // Spammy logging. 198 const bool kDebugLogging = true; // Logging useful for debugging. 199 200 ScopedUtfChars filename(env, javaFilename); 201 202 if ((filename.c_str() == nullptr) || !OS::FileExists(filename.c_str())) { 203 LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename.c_str() << "' does not exist"; 204 ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException")); 205 const char* message = (filename.c_str() == nullptr) ? "<empty file name>" : filename.c_str(); 206 env->ThrowNew(fnfe.get(), message); 207 return JNI_FALSE; 208 } 209 210 // Always treat elements of the bootclasspath as up-to-date. The 211 // fact that code is running at all means that this should be true. 212 Runtime* runtime = Runtime::Current(); 213 ClassLinker* class_linker = runtime->GetClassLinker(); 214 const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath(); 215 for (size_t i = 0; i < boot_class_path.size(); i++) { 216 if (boot_class_path[i]->GetLocation() == filename.c_str()) { 217 if (kVerboseLogging) { 218 LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename.c_str(); 219 } 220 return JNI_FALSE; 221 } 222 } 223 224 // Check if we have an odex file next to the dex file. 225 std::string odex_filename(OatFile::DexFilenameToOdexFilename(filename.c_str())); 226 std::string error_msg; 227 UniquePtr<const OatFile> oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false, 228 &error_msg)); 229 if (oat_file.get() == nullptr) { 230 if (kVerboseLogging) { 231 LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << filename.c_str() 232 << "': " << error_msg; 233 } 234 error_msg.clear(); 235 } else { 236 const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename.c_str(), NULL, 237 kDebugLogging); 238 if (oat_dex_file != nullptr) { 239 uint32_t location_checksum; 240 // If its not possible to read the classes.dex assume up-to-date as we won't be able to 241 // compile it anyway. 242 if (!DexFile::GetChecksum(filename.c_str(), &location_checksum, &error_msg)) { 243 if (kVerboseLogging) { 244 LOG(INFO) << "DexFile_isDexOptNeeded ignoring precompiled stripped file: " 245 << filename.c_str() << ": " << error_msg; 246 } 247 return JNI_FALSE; 248 } 249 if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum, 250 &error_msg)) { 251 if (kVerboseLogging) { 252 LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << odex_filename 253 << " has an up-to-date checksum compared to " << filename.c_str(); 254 } 255 return JNI_FALSE; 256 } else { 257 if (kVerboseLogging) { 258 LOG(INFO) << "DexFile_isDexOptNeeded found precompiled file " << odex_filename 259 << " with an out-of-date checksum compared to " << filename.c_str() 260 << ": " << error_msg; 261 } 262 error_msg.clear(); 263 } 264 } 265 } 266 267 // Check if we have an oat file in the cache 268 std::string cache_location(GetDalvikCacheFilenameOrDie(filename.c_str())); 269 oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL, false, &error_msg)); 270 if (oat_file.get() == nullptr) { 271 if (kDebugLogging) { 272 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 273 << " does not exist for " << filename.c_str() << ": " << error_msg; 274 } 275 return JNI_TRUE; 276 } 277 278 for (const auto& space : runtime->GetHeap()->GetContinuousSpaces()) { 279 if (space->IsImageSpace()) { 280 // TODO: Ensure this works with multiple image spaces. 281 const ImageHeader& image_header = space->AsImageSpace()->GetImageHeader(); 282 if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() != 283 image_header.GetOatChecksum()) { 284 if (kDebugLogging) { 285 ScopedObjectAccess soa(env); 286 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 287 << " has out-of-date oat checksum compared to " 288 << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); 289 } 290 return JNI_TRUE; 291 } 292 if (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin() 293 != reinterpret_cast<uint32_t>(image_header.GetOatDataBegin())) { 294 if (kDebugLogging) { 295 ScopedObjectAccess soa(env); 296 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 297 << " has out-of-date oat begin compared to " 298 << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); 299 } 300 return JNI_TRUE; 301 } 302 } 303 } 304 305 uint32_t location_checksum; 306 if (!DexFile::GetChecksum(filename.c_str(), &location_checksum, &error_msg)) { 307 if (kDebugLogging) { 308 LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename.c_str() 309 << " (error " << error_msg << ")"; 310 } 311 return JNI_TRUE; 312 } 313 314 if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum, 315 &error_msg)) { 316 if (kDebugLogging) { 317 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 318 << " has out-of-date checksum compared to " << filename.c_str() 319 << " (error " << error_msg << ")"; 320 } 321 return JNI_TRUE; 322 } 323 324 if (kVerboseLogging) { 325 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 326 << " is up-to-date for " << filename.c_str(); 327 } 328 CHECK(error_msg.empty()) << error_msg; 329 return JNI_FALSE; 330} 331 332static JNINativeMethod gMethods[] = { 333 NATIVE_METHOD(DexFile, closeDexFile, "(I)V"), 334 NATIVE_METHOD(DexFile, defineClassNative, "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;"), 335 NATIVE_METHOD(DexFile, getClassNameList, "(I)[Ljava/lang/String;"), 336 NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"), 337 NATIVE_METHOD(DexFile, openDexFileNative, "(Ljava/lang/String;Ljava/lang/String;I)I"), 338}; 339 340void register_dalvik_system_DexFile(JNIEnv* env) { 341 REGISTER_NATIVE_METHODS("dalvik/system/DexFile"); 342} 343 344} // namespace art 345