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