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