Home | History | Annotate | Download | only in native
      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