dalvik_system_DexFile.cc revision 7dfb28c066159e6cde8181720f0c451a700ef966
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 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 VLOG(class_linker) << "Failed to find dex_file"; 145 return NULL; 146 } 147 ScopedUtfChars class_name(env, javaName); 148 if (class_name.c_str() == NULL) { 149 VLOG(class_linker) << "Failed to find class_name"; 150 return NULL; 151 } 152 const std::string descriptor(DotToDescriptor(class_name.c_str())); 153 const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor); 154 if (dex_class_def == NULL) { 155 VLOG(class_linker) << "Failed to find dex_class_def"; 156 return NULL; 157 } 158 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); 159 class_linker->RegisterDexFile(*dex_file); 160 mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(javaLoader); 161 mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file, 162 *dex_class_def); 163 VLOG(class_linker) << "DexFile_defineClassNative returning " << result; 164 return soa.AddLocalReference<jclass>(result); 165} 166 167static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) { 168 const DexFile* dex_file; 169 { 170 ScopedObjectAccess soa(env); 171 dex_file = toDexFile(cookie); 172 } 173 if (dex_file == NULL) { 174 return NULL; 175 } 176 177 std::vector<std::string> class_names; 178 for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { 179 const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); 180 const char* descriptor = dex_file->GetClassDescriptor(class_def); 181 class_names.push_back(DescriptorToDot(descriptor)); 182 } 183 return toStringArray(env, class_names); 184} 185 186static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) { 187 bool debug_logging = false; 188 189 ScopedUtfChars filename(env, javaFilename); 190 if (filename.c_str() == NULL) { 191 LOG(ERROR) << "DexFile_isDexOptNeeded null filename"; 192 return JNI_TRUE; 193 } 194 195 if (!OS::FileExists(filename.c_str())) { 196 LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename.c_str() << "' does not exist"; 197 ScopedObjectAccess soa(env); 198 ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); 199 soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/FileNotFoundException;", 200 "%s", filename.c_str()); 201 return JNI_TRUE; 202 } 203 204 // Always treat elements of the bootclasspath as up-to-date. The 205 // fact that code is running at all means that this should be true. 206 Runtime* runtime = Runtime::Current(); 207 ClassLinker* class_linker = runtime->GetClassLinker(); 208 const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath(); 209 for (size_t i = 0; i < boot_class_path.size(); i++) { 210 if (boot_class_path[i]->GetLocation() == filename.c_str()) { 211 if (debug_logging) { 212 LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename.c_str(); 213 } 214 return JNI_FALSE; 215 } 216 } 217 218 // Check if we have an odex file next to the dex file. 219 std::string odex_filename(OatFile::DexFilenameToOdexFilename(filename.c_str())); 220 UniquePtr<const OatFile> oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false)); 221 if (oat_file.get() != NULL) { 222 ScopedObjectAccess soa(env); 223 const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename.c_str()); 224 if (oat_dex_file == NULL) { 225 if (debug_logging) { 226 LOG(INFO) << "DexFile_isDexOptNeeded GetOatDexFile failed"; 227 } 228 } else { 229 uint32_t location_checksum; 230 // If we have no classes.dex checksum such as in a user build, assume up-to-date. 231 if (!DexFile::GetChecksum(filename.c_str(), location_checksum)) { 232 if (debug_logging) { 233 LOG(INFO) << "DexFile_isDexOptNeeded ignoring precompiled stripped file: " 234 << filename.c_str(); 235 } 236 return JNI_FALSE; 237 } 238 if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) { 239 if (debug_logging) { 240 LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << odex_filename 241 << " is up-to-date checksum compared to " << filename.c_str(); 242 } 243 return JNI_FALSE; 244 } 245 } 246 } 247 248 // Check if we have an oat file in the cache 249 std::string cache_location(GetDalvikCacheFilenameOrDie(filename.c_str())); 250 oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL, false)); 251 if (oat_file.get() == NULL) { 252 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 253 << " does not exist for " << filename.c_str(); 254 return JNI_TRUE; 255 } 256 257 for (const auto& space : runtime->GetHeap()->GetContinuousSpaces()) { 258 if (space->IsImageSpace()) { 259 // TODO: Ensure this works with multiple image spaces. 260 const ImageHeader& image_header = space->AsImageSpace()->GetImageHeader(); 261 if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() != image_header.GetOatChecksum()) { 262 ScopedObjectAccess soa(env); 263 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 264 << " has out-of-date oat checksum compared to " 265 << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); 266 return JNI_TRUE; 267 } 268 if (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin() 269 != reinterpret_cast<uint32_t>(image_header.GetOatDataBegin())) { 270 ScopedObjectAccess soa(env); 271 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 272 << " has out-of-date oat begin compared to " 273 << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); 274 return JNI_TRUE; 275 } 276 } 277 } 278 279 ScopedObjectAccess soa(env); 280 uint32_t location_checksum; 281 if (!DexFile::GetChecksum(filename.c_str(), location_checksum)) { 282 LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename.c_str(); 283 return JNI_TRUE; 284 } 285 286 if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) { 287 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 288 << " has out-of-date checksum compared to " << filename.c_str(); 289 return JNI_TRUE; 290 } 291 292 if (debug_logging) { 293 LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location 294 << " is up-to-date for " << filename.c_str(); 295 } 296 return JNI_FALSE; 297} 298 299static JNINativeMethod gMethods[] = { 300 NATIVE_METHOD(DexFile, closeDexFile, "(I)V"), 301 NATIVE_METHOD(DexFile, defineClassNative, "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;"), 302 NATIVE_METHOD(DexFile, getClassNameList, "(I)[Ljava/lang/String;"), 303 NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"), 304 NATIVE_METHOD(DexFile, openDexFileNative, "(Ljava/lang/String;Ljava/lang/String;I)I"), 305}; 306 307void register_dalvik_system_DexFile(JNIEnv* env) { 308 REGISTER_NATIVE_METHODS("dalvik/system/DexFile"); 309} 310 311} // namespace art 312