diff options
| -rw-r--r-- | build/Android.gtest.mk | 1 | ||||
| -rw-r--r-- | dex2oat/dex2oat.cc | 5 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 198 | ||||
| -rw-r--r-- | runtime/class_linker.h | 14 | ||||
| -rw-r--r-- | runtime/interpreter/unstarted_runtime.cc | 97 | ||||
| -rw-r--r-- | runtime/native/java_lang_VMClassLoader.cc | 4 | ||||
| -rw-r--r-- | runtime/oat.h | 1 | ||||
| -rw-r--r-- | runtime/oat_file.cc | 87 | ||||
| -rw-r--r-- | runtime/oat_file.h | 12 | ||||
| -rw-r--r-- | runtime/oat_file_test.cc | 58 |
10 files changed, 389 insertions, 88 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index d9d09bcc63..7283710bfa 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -67,6 +67,7 @@ ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex Nested +ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY ART_GTEST_proxy_test_DEX_DEPS := Interfaces ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index eda7ec6f6f..7e32b43e66 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1237,6 +1237,11 @@ class Dex2Oat FINAL { for (auto& class_path_file : class_path_files_) { class_path_files.push_back(class_path_file.get()); } + + // Store the classpath we have right now. + key_value_store_->Put(OatHeader::kClassPathKey, + OatFile::EncodeDexFileDependencies(class_path_files)); + // Then the dex files we'll compile. Thus we'll resolve the class-path first. class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end()); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 0d92fc215c..4e59217af6 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1259,94 +1259,124 @@ ClassPathEntry FindInClassPath(const char* descriptor, return ClassPathEntry(nullptr, nullptr); } -mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa, - Thread* self, const char* descriptor, - size_t hash, - Handle<mirror::ClassLoader> class_loader) { - // Can we special case for a well understood PathClassLoader with the BootClassLoader as parent? - if (class_loader->GetClass() != - soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) || - class_loader->GetParent()->GetClass() != - soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) { - return nullptr; - } - ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); - // Check if this would be found in the parent boot class loader. - if (pair.second != nullptr) { - mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr); - if (klass != nullptr) { - // May return null if resolution on another thread fails. - klass = EnsureResolved(self, descriptor, klass); +static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + mirror::ClassLoader* class_loader) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return class_loader == nullptr || + class_loader->GetClass() == + soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader); +} + +bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + Thread* self, const char* descriptor, + size_t hash, + Handle<mirror::ClassLoader> class_loader, + mirror::Class** result) { + // Termination case: boot class-loader. + if (IsBootClassLoader(soa, class_loader.Get())) { + // The boot class loader, search the boot class path. + ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); + if (pair.second != nullptr) { + mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr); + if (klass != nullptr) { + *result = EnsureResolved(self, descriptor, klass); + } else { + *result = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), + *pair.first, *pair.second); + } + if (*result == nullptr) { + CHECK(self->IsExceptionPending()) << descriptor; + self->ClearException(); + } } else { - // May OOME. - klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first, - *pair.second); + *result = nullptr; } - if (klass == nullptr) { - CHECK(self->IsExceptionPending()) << descriptor; - self->ClearException(); - } - return klass; - } else { - // Handle as if this is the child PathClassLoader. - // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). - StackHandleScope<3> hs(self); - // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. - // We need to get the DexPathList and loop through it. - ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie); - ArtField* const dex_file_field = - soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); - mirror::Object* dex_path_list = - soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)-> - GetObject(class_loader.Get()); - if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) { - // DexPathList has an array dexElements of Elements[] which each contain a dex file. - mirror::Object* dex_elements_obj = - soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> - GetObject(dex_path_list); - // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look - // at the mCookie which is a DexFile vector. - if (dex_elements_obj != nullptr) { - Handle<mirror::ObjectArray<mirror::Object>> dex_elements = - hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()); - for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { - mirror::Object* element = dex_elements->GetWithoutChecks(i); - if (element == nullptr) { - // Should never happen, fall back to java code to throw a NPE. + return true; + } + + // Unsupported class-loader? + if (class_loader->GetClass() != + soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) { + *result = nullptr; + return false; + } + + // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). + StackHandleScope<4> hs(self); + Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent())); + bool recursive_result = FindClassInPathClassLoader(soa, self, descriptor, hash, h_parent, result); + + if (!recursive_result) { + // Something wrong up the chain. + return false; + } + + if (*result != nullptr) { + // Found the class up the chain. + return true; + } + + // Handle this step. + // Handle as if this is the child PathClassLoader. + // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. + // We need to get the DexPathList and loop through it. + ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie); + ArtField* const dex_file_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + mirror::Object* dex_path_list = + soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)-> + GetObject(class_loader.Get()); + if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) { + // DexPathList has an array dexElements of Elements[] which each contain a dex file. + mirror::Object* dex_elements_obj = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> + GetObject(dex_path_list); + // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look + // at the mCookie which is a DexFile vector. + if (dex_elements_obj != nullptr) { + Handle<mirror::ObjectArray<mirror::Object>> dex_elements = + hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()); + for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { + mirror::Object* element = dex_elements->GetWithoutChecks(i); + if (element == nullptr) { + // Should never happen, fall back to java code to throw a NPE. + break; + } + mirror::Object* dex_file = dex_file_field->GetObject(element); + if (dex_file != nullptr) { + mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray(); + if (long_array == nullptr) { + // This should never happen so log a warning. + LOG(WARNING) << "Null DexFile::mCookie for " << descriptor; break; } - mirror::Object* dex_file = dex_file_field->GetObject(element); - if (dex_file != nullptr) { - mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray(); - if (long_array == nullptr) { - // This should never happen so log a warning. - LOG(WARNING) << "Null DexFile::mCookie for " << descriptor; - break; - } - int32_t long_array_size = long_array->GetLength(); - for (int32_t j = 0; j < long_array_size; ++j) { - const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>( - long_array->GetWithoutChecks(j))); - const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash); - if (dex_class_def != nullptr) { - RegisterDexFile(*cp_dex_file); - mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader, - *cp_dex_file, *dex_class_def); - if (klass == nullptr) { - CHECK(self->IsExceptionPending()) << descriptor; - self->ClearException(); - return nullptr; - } - return klass; + int32_t long_array_size = long_array->GetLength(); + for (int32_t j = 0; j < long_array_size; ++j) { + const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>( + long_array->GetWithoutChecks(j))); + const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash); + if (dex_class_def != nullptr) { + RegisterDexFile(*cp_dex_file); + mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader, + *cp_dex_file, *dex_class_def); + if (klass == nullptr) { + CHECK(self->IsExceptionPending()) << descriptor; + self->ClearException(); + // TODO: Is it really right to break here, and not check the other dex files? + return true; } + *result = klass; + return true; } } } } } self->AssertNoPendingException(); - return nullptr; } + + // Result is still null from the parent call, no need to set it again... + return true; } mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, @@ -1384,10 +1414,18 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, } } else { ScopedObjectAccessUnchecked soa(self); - mirror::Class* cp_klass = FindClassInPathClassLoader(soa, self, descriptor, hash, - class_loader); - if (cp_klass != nullptr) { - return cp_klass; + mirror::Class* cp_klass; + if (FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader, &cp_klass)) { + // The chain was understood. So the value in cp_klass is either the class we were looking + // for, or not found. + if (cp_klass != nullptr) { + return cp_klass; + } + // TODO: We handle the boot classpath loader in FindClassInPathClassLoader. Try to unify this + // and the branch above. TODO: throw the right exception here. + + // We'll let the Java-side rediscover all this and throw the exception with the right stack + // trace. } if (Runtime::Current()->IsAotCompiler()) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 2427462427..68624b08b6 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -117,11 +117,15 @@ class ClassLinker { Handle<mirror::ClassLoader> class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Find a class in the path class loader, loading it if necessary without using JNI. Hash - // function is supposed to be ComputeModifiedUtf8Hash(descriptor). - mirror::Class* FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa, - Thread* self, const char* descriptor, size_t hash, - Handle<mirror::ClassLoader> class_loader) + // Finds a class in the path class loader, loading it if necessary without using JNI. Hash + // function is supposed to be ComputeModifiedUtf8Hash(descriptor). Returns true if the + // class-loader chain could be handled, false otherwise, i.e., a non-supported class-loader + // was encountered while walking the parent chain (currently only BootClassLoader and + // PathClassLoader are supported). + bool FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + Thread* self, const char* descriptor, size_t hash, + Handle<mirror::ClassLoader> class_loader, + mirror::Class** result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Finds a class by its descriptor using the "system" class loader, ie by searching the diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index a971c1b3cf..fbb07e8e8f 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -39,6 +39,7 @@ #include "thread.h" #include "transaction.h" #include "well_known_classes.h" +#include "zip_archive.h" namespace art { namespace interpreter { @@ -641,6 +642,100 @@ static void UnstartedMemoryPeekArrayEntry( } } +// This allows reading security.properties in an unstarted runtime and initialize Security. +static void UnstartedSecurityGetSecurityPropertiesReader( + Thread* self, + ShadowFrame* shadow_frame ATTRIBUTE_UNUSED, + JValue* result, + size_t arg_offset ATTRIBUTE_UNUSED) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Runtime* runtime = Runtime::Current(); + const std::vector<const DexFile*>& path = runtime->GetClassLinker()->GetBootClassPath(); + std::string canonical(DexFile::GetDexCanonicalLocation(path[0]->GetLocation().c_str())); + mirror::String* string_data; + + // Use a block to enclose the I/O and MemMap code so buffers are released early. + { + std::string error_msg; + std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(canonical.c_str(), &error_msg)); + if (zip_archive.get() == nullptr) { + AbortTransactionOrFail(self, "Could not open zip file %s: %s", canonical.c_str(), + error_msg.c_str()); + return; + } + std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find("java/security/security.properties", + &error_msg)); + if (zip_entry.get() == nullptr) { + AbortTransactionOrFail(self, "Could not find security.properties file in %s: %s", + canonical.c_str(), error_msg.c_str()); + return; + } + std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(canonical.c_str(), + "java/security/security.properties", + &error_msg)); + if (map.get() == nullptr) { + AbortTransactionOrFail(self, "Could not unzip security.properties file in %s: %s", + canonical.c_str(), error_msg.c_str()); + return; + } + + uint32_t length = zip_entry->GetUncompressedLength(); + std::unique_ptr<char[]> tmp(new char[length + 1]); + memcpy(tmp.get(), map->Begin(), length); + tmp.get()[length] = 0; // null terminator + + string_data = mirror::String::AllocFromModifiedUtf8(self, tmp.get()); + } + + if (string_data == nullptr) { + AbortTransactionOrFail(self, "Could not create string from file content of %s", + canonical.c_str()); + return; + } + + // Create a StringReader. + StackHandleScope<3> hs(self); + Handle<mirror::String> h_string(hs.NewHandle(string_data)); + + Handle<mirror::Class> h_class(hs.NewHandle( + runtime->GetClassLinker()->FindClass(self, + "Ljava/io/StringReader;", + NullHandle<mirror::ClassLoader>()))); + if (h_class.Get() == nullptr) { + AbortTransactionOrFail(self, "Could not find StringReader class"); + return; + } + + if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) { + AbortTransactionOrFail(self, "Could not initialize StringReader class"); + return; + } + + Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self))); + if (h_obj.Get() == nullptr) { + AbortTransactionOrFail(self, "Could not allocate StringReader object"); + return; + } + + mirror::ArtMethod* constructor = h_class->FindDeclaredDirectMethod("<init>", + "(Ljava/lang/String;)V"); + if (constructor == nullptr) { + AbortTransactionOrFail(self, "Could not find StringReader constructor"); + return; + } + + uint32_t args[1]; + args[0] = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h_string.Get())); + EnterInterpreterFromInvoke(self, constructor, h_obj.Get(), args, nullptr); + + if (self->IsExceptionPending()) { + AbortTransactionOrFail(self, "Could not run StringReader constructor"); + return; + } + + result->SetL(h_obj.Get()); +} + static void UnstartedJNIVMRuntimeNewUnpaddedArray(Thread* self, mirror::ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED, @@ -963,6 +1058,8 @@ static void UnstartedRuntimeInitializeInvokeHandlers() { &UnstartedMemoryPeekEntry }, { "void libcore.io.Memory.peekByteArray(long, byte[], int, int)", &UnstartedMemoryPeekArrayEntry }, + { "java.io.Reader java.security.Security.getSecurityPropertiesReader()", + &UnstartedSecurityGetSecurityPropertiesReader }, }; for (auto& def : defs) { diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index 35932e0b89..0c39f2b53e 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -44,8 +44,8 @@ static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoa if (loader != nullptr) { // Try the common case. StackHandleScope<1> hs(soa.Self()); - c = cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), descriptor_hash, - hs.NewHandle(loader)); + cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), descriptor_hash, + hs.NewHandle(loader), &c); if (c != nullptr) { return soa.AddLocalReference<jclass>(c); } diff --git a/runtime/oat.h b/runtime/oat.h index de95fef550..a31e09a3cf 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -38,6 +38,7 @@ class PACKED(4) OatHeader { static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDex2OatHostKey = "dex2oat-host"; static constexpr const char* kPicKey = "pic"; + static constexpr const char* kClassPathKey = "classpath"; static OatHeader* Create(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 81703b1b2a..d3c4b49146 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -20,6 +20,7 @@ #include <string.h> #include <unistd.h> +#include <cstdlib> #include <sstream> #include "base/bit_vector.h" @@ -592,4 +593,90 @@ bool OatFile::IsPic() const { // TODO: Check against oat_patches. b/18144996 } +static constexpr char kDexClassPathEncodingSeparator = '*'; + +std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) { + std::ostringstream out; + + for (const DexFile* dex_file : dex_files) { + out << dex_file->GetLocation().c_str(); + out << kDexClassPathEncodingSeparator; + out << dex_file->GetLocationChecksum(); + out << kDexClassPathEncodingSeparator; + } + + return out.str(); +} + +bool OatFile::CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg) { + if (dex_dependencies == nullptr || dex_dependencies[0] == 0) { + // No dependencies. + return true; + } + + // Assumption: this is not performance-critical. So it's OK to do this with a std::string and + // Split() instead of manual parsing of the combined char*. + std::vector<std::string> split; + Split(dex_dependencies, kDexClassPathEncodingSeparator, &split); + if (split.size() % 2 != 0) { + // Expected pairs of location and checksum. + *msg = StringPrintf("Odd number of elements in dependency list %s", dex_dependencies); + return false; + } + + for (auto it = split.begin(), end = split.end(); it != end; it += 2) { + std::string& location = *it; + std::string& checksum = *(it + 1); + int64_t converted = strtoll(checksum.c_str(), nullptr, 10); + if (converted == 0) { + // Conversion error. + *msg = StringPrintf("Conversion error for %s", checksum.c_str()); + return false; + } + + uint32_t dex_checksum; + std::string error_msg; + if (DexFile::GetChecksum(DexFile::GetDexCanonicalLocation(location.c_str()).c_str(), + &dex_checksum, + &error_msg)) { + if (converted != dex_checksum) { + *msg = StringPrintf("Checksums don't match for %s: %" PRId64 " vs %u", + location.c_str(), converted, dex_checksum); + return false; + } + } else { + // Problem retrieving checksum. + // TODO: odex files? + *msg = StringPrintf("Could not retrieve checksum for %s: %s", location.c_str(), + error_msg.c_str()); + return false; + } + } + + return true; +} + +bool OatFile::GetDexLocationsFromDependencies(const char* dex_dependencies, + std::vector<std::string>* locations) { + DCHECK(locations != nullptr); + if (dex_dependencies == nullptr || dex_dependencies[0] == 0) { + return true; + } + + // Assumption: this is not performance-critical. So it's OK to do this with a std::string and + // Split() instead of manual parsing of the combined char*. + std::vector<std::string> split; + Split(dex_dependencies, kDexClassPathEncodingSeparator, &split); + if (split.size() % 2 != 0) { + // Expected pairs of location and checksum. + return false; + } + + for (auto it = split.begin(), end = split.end(); it != end; it += 2) { + locations->push_back(*it); + } + + return true; +} + } // namespace art diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 73a8c8e45a..a5d5ae8a7b 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -248,6 +248,18 @@ class OatFile FINAL { static std::string ResolveRelativeEncodedDexLocation( const char* abs_dex_location, const std::string& rel_dex_location); + // Create a dependency list (dex locations and checksums) for the given dex files. + static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files); + + // Check the given dependency list against their dex files - thus the name "Static," this does + // not check the class-loader environment, only whether there have been file updates. + static bool CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg); + + // Get the dex locations of a dependency list. Note: this is *not* cleaned for synthetic + // locations of multidex files. + static bool GetDexLocationsFromDependencies(const char* dex_dependencies, + std::vector<std::string>* locations); + private: static void CheckLocation(const std::string& location); diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc index f2213e9879..a88553ca7b 100644 --- a/runtime/oat_file_test.cc +++ b/runtime/oat_file_test.cc @@ -20,9 +20,15 @@ #include <gtest/gtest.h> +#include "common_runtime_test.h" +#include "scoped_thread_state_change.h" + namespace art { -TEST(OatFileTest, ResolveRelativeEncodedDexLocation) { +class OatFileTest : public CommonRuntimeTest { +}; + +TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) { EXPECT_EQ(std::string("/data/app/foo/base.apk"), OatFile::ResolveRelativeEncodedDexLocation( nullptr, "/data/app/foo/base.apk")); @@ -56,4 +62,54 @@ TEST(OatFileTest, ResolveRelativeEncodedDexLocation) { "/data/app/foo/base.apk", "o/base.apk")); } +static std::vector<const DexFile*> ToConstDexFiles( + const std::vector<std::unique_ptr<const DexFile>>& in) { + std::vector<const DexFile*> ret; + for (auto& d : in) { + ret.push_back(d.get()); + } + return ret; +} + +TEST_F(OatFileTest, DexFileDependencies) { + std::string error_msg; + + // No dependencies. + EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(nullptr, &error_msg)) << error_msg; + EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies("", &error_msg)) << error_msg; + + // Ill-formed dependencies. + EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc", &error_msg)); + EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*def", &error_msg)); + EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*def*", &error_msg)); + + // Unsatisfiable dependency. + EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*", &error_msg)); + + // Load some dex files to be able to do a real test. + ScopedObjectAccess soa(Thread::Current()); + + std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Main"); + std::vector<const DexFile*> dex_files_const1 = ToConstDexFiles(dex_files1); + std::string encoding1 = OatFile::EncodeDexFileDependencies(dex_files_const1); + EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding1.c_str(), &error_msg)) + << error_msg << " " << encoding1; + std::vector<std::string> split1; + EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding1.c_str(), &split1)); + ASSERT_EQ(split1.size(), 1U); + EXPECT_EQ(split1[0], dex_files_const1[0]->GetLocation()); + + std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); + EXPECT_GT(dex_files2.size(), 1U); + std::vector<const DexFile*> dex_files_const2 = ToConstDexFiles(dex_files2); + std::string encoding2 = OatFile::EncodeDexFileDependencies(dex_files_const2); + EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding2.c_str(), &error_msg)) + << error_msg << " " << encoding2; + std::vector<std::string> split2; + EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding2.c_str(), &split2)); + ASSERT_EQ(split2.size(), 2U); + EXPECT_EQ(split2[0], dex_files_const2[0]->GetLocation()); + EXPECT_EQ(split2[1], dex_files_const2[1]->GetLocation()); +} + } // namespace art |