diff options
| -rw-r--r-- | dex2oat/dex2oat_test.cc | 23 | ||||
| -rw-r--r-- | runtime/class_loader_context.cc | 105 | ||||
| -rw-r--r-- | runtime/class_loader_context.h | 21 | ||||
| -rw-r--r-- | runtime/class_loader_context_test.cc | 78 | ||||
| -rw-r--r-- | runtime/oat.h | 3 | ||||
| -rw-r--r-- | runtime/oat_file.cc | 22 | ||||
| -rw-r--r-- | runtime/oat_file.h | 5 | ||||
| -rw-r--r-- | runtime/oat_file_manager.cc | 56 |
8 files changed, 234 insertions, 79 deletions
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 1505eb56c8..b08b055a65 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -937,7 +937,7 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest { return GetOdexDir() + "/Context.odex"; } - const char* kEmptyClassPathKey = ""; + const char* kEmptyClassPathKey = "PCL[]"; }; TEST_F(Dex2oatClassLoaderContextTest, InvalidContext) { @@ -961,10 +961,10 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithTheSourceDexFiles) { TEST_F(Dex2oatClassLoaderContextTest, ContextWithOtherDexFiles) { std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Nested"); - std::string expected_classpath_key = - OatFile::EncodeDexFileDependencies(MakeNonOwningPointerVector(dex_files), ""); std::string context = "PCL[" + dex_files[0]->GetLocation() + "]"; + std::string expected_classpath_key = "PCL[" + + dex_files[0]->GetLocation() + "*" + std::to_string(dex_files[0]->GetLocationChecksum()) + "]"; RunTest(context.c_str(), expected_classpath_key.c_str(), true); } @@ -974,7 +974,7 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithStrippedDexFiles) { std::string context = "PCL[" + stripped_classpath + "]"; // Expect an empty context because stripped dex files cannot be open. - RunTest(context.c_str(), /*expected_classpath_key*/ "" , /*expected_success*/ true); + RunTest(context.c_str(), kEmptyClassPathKey , /*expected_success*/ true); } TEST_F(Dex2oatClassLoaderContextTest, ContextWithStrippedDexFilesBackedByOdex) { @@ -993,19 +993,26 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithStrippedDexFilesBackedByOdex) { Copy(GetStrippedDexSrc1(), stripped_classpath); std::string context = "PCL[" + stripped_classpath + "]"; - std::string expected_classpath; + std::string expected_classpath_key; { // Open the oat file to get the expected classpath. OatFileAssistant oat_file_assistant(stripped_classpath.c_str(), kRuntimeISA, false); std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile()); std::vector<std::unique_ptr<const DexFile>> oat_dex_files = OatFileAssistant::LoadDexFiles(*oat_file, stripped_classpath.c_str()); - expected_classpath = OatFile::EncodeDexFileDependencies( - MakeNonOwningPointerVector(oat_dex_files), ""); + expected_classpath_key = "PCL["; + for (size_t i = 0; i < oat_dex_files.size(); i++) { + if (i > 0) { + expected_classpath_key + ":"; + } + expected_classpath_key += oat_dex_files[i]->GetLocation() + "*" + + std::to_string(oat_dex_files[i]->GetLocationChecksum()); + } + expected_classpath_key += "]"; } RunTest(context.c_str(), - expected_classpath.c_str(), + expected_classpath_key.c_str(), /*expected_success*/ true, /*use_second_source*/ true); } diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 5cbcd8f1ff..2bed1d5b2d 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -31,8 +31,9 @@ static constexpr char kPathClassLoaderString[] = "PCL"; static constexpr char kDelegateLastClassLoaderString[] = "DLC"; static constexpr char kClassLoaderOpeningMark = '['; static constexpr char kClassLoaderClosingMark = ']'; -static constexpr char kClassLoaderSep = ';'; -static constexpr char kClasspathSep = ':'; +static constexpr char kClassLoaderSeparator = ';'; +static constexpr char kClasspathSeparator = ':'; +static constexpr char kDexFileChecksumSeparator = '*'; ClassLoaderContext::ClassLoaderContext() : special_shared_library_(false), @@ -48,9 +49,11 @@ std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string } } -// The expected format is: "ClassLoaderType1[ClasspathElem1:ClasspathElem2...]". +// The expected format is: "ClassLoaderType1[ClasspathElem1*Checksum1:ClasspathElem2*Checksum2...]". +// The checksum part of the format is expected only if parse_cheksums is true. bool ClassLoaderContext::ParseClassLoaderSpec(const std::string& class_loader_spec, - ClassLoaderType class_loader_type) { + ClassLoaderType class_loader_type, + bool parse_checksums) { const char* class_loader_type_str = GetClassLoaderTypeName(class_loader_type); size_t type_str_size = strlen(class_loader_type_str); @@ -70,7 +73,26 @@ bool ClassLoaderContext::ParseClassLoaderSpec(const std::string& class_loader_sp class_loader_spec.length() - type_str_size - 2); class_loader_chain_.push_back(ClassLoaderInfo(class_loader_type)); - Split(classpath, kClasspathSep, &class_loader_chain_.back().classpath); + + if (!parse_checksums) { + Split(classpath, kClasspathSeparator, &class_loader_chain_.back().classpath); + } else { + std::vector<std::string> classpath_elements; + Split(classpath, kClasspathSeparator, &classpath_elements); + for (const std::string& element : classpath_elements) { + std::vector<std::string> dex_file_with_checksum; + Split(element, kDexFileChecksumSeparator, &dex_file_with_checksum); + if (dex_file_with_checksum.size() != 2) { + return false; + } + uint32_t checksum = 0; + if (!ParseInt(dex_file_with_checksum[1].c_str(), &checksum)) { + return false; + } + class_loader_chain_.back().classpath.push_back(dex_file_with_checksum[0]); + class_loader_chain_.back().checksums.push_back(checksum); + } + } return true; } @@ -93,11 +115,11 @@ ClassLoaderContext::ExtractClassLoaderType(const std::string& class_loader_spec) // The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]... // ClassLoaderType is either "PCL" (PathClassLoader) or "DLC" (DelegateLastClassLoader). // ClasspathElem is the path of dex/jar/apk file. -bool ClassLoaderContext::Parse(const std::string& spec) { +bool ClassLoaderContext::Parse(const std::string& spec, bool parse_checksums) { if (spec.empty()) { - LOG(ERROR) << "Empty string passed to Parse"; - return false; + return true; } + // Stop early if we detect the special shared library, which may be passed as the classpath // for dex2oat when we want to skip the shared libraries check. if (spec == OatFile::kSpecialSharedLibrary) { @@ -107,7 +129,7 @@ bool ClassLoaderContext::Parse(const std::string& spec) { } std::vector<std::string> class_loaders; - Split(spec, kClassLoaderSep, &class_loaders); + Split(spec, kClassLoaderSeparator, &class_loaders); for (const std::string& class_loader : class_loaders) { ClassLoaderType type = ExtractClassLoaderType(class_loader); @@ -115,7 +137,7 @@ bool ClassLoaderContext::Parse(const std::string& spec) { LOG(ERROR) << "Invalid class loader type: " << class_loader; return false; } - if (!ParseClassLoaderSpec(class_loader, type)) { + if (!ParseClassLoaderSpec(class_loader, type, parse_checksums)) { LOG(ERROR) << "Invalid class loader spec: " << class_loader; return false; } @@ -219,12 +241,33 @@ std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_ return ""; } - // TODO(calin): Transition period: assume we only have a classloader until - // the oat file assistant implements the full class loader check. - CHECK_EQ(1u, class_loader_chain_.size()); + std::ostringstream out; - return OatFile::EncodeDexFileDependencies(MakeNonOwningPointerVector( - class_loader_chain_[0].opened_dex_files), base_dir); + for (size_t i = 0; i < class_loader_chain_.size(); i++) { + const ClassLoaderInfo& info = class_loader_chain_[i]; + if (i > 0) { + out << kClassLoaderSeparator; + } + out << GetClassLoaderTypeName(info.type); + out << kClassLoaderOpeningMark; + for (size_t k = 0; k < info.opened_dex_files.size(); k++) { + const std::unique_ptr<const DexFile>& dex_file = info.opened_dex_files[k]; + const std::string& location = dex_file->GetLocation(); + if (k > 0) { + out << kClasspathSeparator; + } + // Find paths that were relative and convert them back from absolute. + if (!base_dir.empty() && location.substr(0, base_dir.length()) == base_dir) { + out << location.substr(base_dir.length() + 1).c_str(); + } else { + out << dex_file->GetLocation().c_str(); + } + out << kDexFileChecksumSeparator; + out << dex_file->GetLocationChecksum(); + } + out << kClassLoaderClosingMark; + } + return out.str(); } jobject ClassLoaderContext::CreateClassLoader( @@ -281,5 +324,37 @@ void ClassLoaderContext::CheckDexFilesOpened(const std::string& calling_method) << "Dex files were not successfully opened before the call to " << calling_method << "attempt=" << dex_files_open_attempted_ << ", result=" << dex_files_open_result_; } + +bool ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey( + const std::string& context_spec, + std::vector<std::string>* out_classpath, + std::vector<uint32_t>* out_checksums, + bool* out_is_special_shared_library) { + ClassLoaderContext context; + if (!context.Parse(context_spec, /*parse_checksums*/ true)) { + LOG(ERROR) << "Invalid class loader context: " << context_spec; + return false; + } + + *out_is_special_shared_library = context.special_shared_library_; + if (context.special_shared_library_) { + return true; + } + + if (context.class_loader_chain_.empty()) { + return true; + } + + // TODO(calin): assert that we only have a PathClassLoader until the logic for + // checking the context covers all case. + CHECK_EQ(1u, context.class_loader_chain_.size()); + const ClassLoaderInfo& info = context.class_loader_chain_[0]; + CHECK_EQ(kPathClassLoader, info.type); + DCHECK_EQ(info.classpath.size(), info.checksums.size()); + + *out_classpath = info.classpath; + *out_checksums = info.checksums; + return true; +} } // namespace art diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 4af5017ef2..9727a3b0dd 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -59,6 +59,8 @@ class ClassLoaderContext { // The compilation sources are appended to the classpath of the top class loader // (i.e the class loader whose parent is the BootClassLoader). // Should only be called if OpenDexFiles() returned true. + // If the context is empty, this method only creates a single PathClassLoader with the + // given compilation_sources. jobject CreateClassLoader(const std::vector<const DexFile*>& compilation_sources) const; // Encodes the context as a string suitable to be added in oat files. @@ -80,6 +82,17 @@ class ClassLoaderContext { // class loader for the source dex files. static std::unique_ptr<ClassLoaderContext> Create(const std::string& spec); + // Decodes the class loader context stored in the oat file with EncodeContextForOatFile. + // Returns true if the format matches, or false otherwise. If the return is true, the out + // arguments will contain the classpath dex files, their checksums and whether or not the + // context is a special shared library. + // The method asserts that the context is made out of only one PathClassLoader. + static bool DecodePathClassLoaderContextFromOatFileKey( + const std::string& context_spec, + std::vector<std::string>* out_classpath, + std::vector<uint32_t>* out_checksums, + bool* out_is_special_shared_library); + private: enum ClassLoaderType { kInvalidClassLoader = 0, @@ -93,6 +106,9 @@ class ClassLoaderContext { // The list of class path elements that this loader loads. // Note that this list may contain relative paths. std::vector<std::string> classpath; + // The list of class path elements checksums. + // May be empty if the checksums are not given when the context is created. + std::vector<uint32_t> checksums; // After OpenDexFiles is called this holds the opened dex files. std::vector<std::unique_ptr<const DexFile>> opened_dex_files; // After OpenDexFiles, in case some of the dex files were opened from their oat files @@ -104,13 +120,14 @@ class ClassLoaderContext { // Reads the class loader spec in place and returns true if the spec is valid and the // compilation context was constructed. - bool Parse(const std::string& spec); + bool Parse(const std::string& spec, bool parse_checksums = false); // Attempts to parse a single class loader spec for the given class_loader_type. // If successful the class loader spec will be added to the chain. // Returns whether or not the operation was successful. bool ParseClassLoaderSpec(const std::string& class_loader_spec, - ClassLoaderType class_loader_type); + ClassLoaderType class_loader_type, + bool parse_checksums = false); // Extracts the class loader type from the given spec. // Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 4643e78226..03eb0e42a0 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -230,6 +230,42 @@ TEST_F(ClassLoaderContextTest, CreateClassLoader) { } } +TEST_F(ClassLoaderContextTest, CreateClassLoaderWithEmptyContext) { + std::unique_ptr<ClassLoaderContext> context = + ClassLoaderContext::Create(""); + ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "")); + + std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex"); + + std::vector<const DexFile*> compilation_sources_raw = + MakeNonOwningPointerVector(compilation_sources); + jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw); + ASSERT_TRUE(jclass_loader != nullptr); + + ScopedObjectAccess soa(Thread::Current()); + + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader = hs.NewHandle( + soa.Decode<mirror::ClassLoader>(jclass_loader)); + + ASSERT_TRUE(class_loader->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)); + ASSERT_TRUE(class_loader->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + + + std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(jclass_loader); + + // The compilation sources should be the only files present in the class loader + ASSERT_EQ(compilation_sources.size(), class_loader_dex_files.size()); + for (size_t i = 0; i < compilation_sources.size(); i++) { + ASSERT_EQ(compilation_sources[i]->GetLocation(), + class_loader_dex_files[i]->GetLocation()); + ASSERT_EQ(compilation_sources[i]->GetLocationChecksum(), + class_loader_dex_files[i]->GetLocationChecksum()); + } +} + TEST_F(ClassLoaderContextTest, RemoveSourceLocations) { std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("PCL[a.dex]"); @@ -256,10 +292,46 @@ TEST_F(ClassLoaderContextTest, EncodeInOatFile) { std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main"); std::vector<std::unique_ptr<const DexFile>> dex2 = OpenTestDexFiles("MyClass"); std::string encoding = context->EncodeContextForOatFile(""); - std::string expected_encoding = - dex1[0]->GetLocation() + "*" + std::to_string(dex1[0]->GetLocationChecksum()) + "*" + - dex2[0]->GetLocation() + "*" + std::to_string(dex2[0]->GetLocationChecksum()) + "*"; + std::string expected_encoding = "PCL[" + + dex1[0]->GetLocation() + "*" + std::to_string(dex1[0]->GetLocationChecksum()) + ":" + + dex2[0]->GetLocation() + "*" + std::to_string(dex2[0]->GetLocationChecksum()) + "]"; ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile("")); } +TEST_F(ClassLoaderContextTest, DecodeOatFileKey) { + std::string oat_file_encoding = "PCL[a.dex*123:b.dex*456]"; + std::vector<std::string> classpath; + std::vector<uint32_t> checksums; + bool is_special_shared_library; + bool result = ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey( + oat_file_encoding, + &classpath, + &checksums, + &is_special_shared_library); + ASSERT_TRUE(result); + ASSERT_FALSE(is_special_shared_library); + ASSERT_EQ(2u, classpath.size()); + ASSERT_EQ(2u, checksums.size()); + ASSERT_EQ("a.dex", classpath[0]); + ASSERT_EQ(123u, checksums[0]); + ASSERT_EQ("b.dex", classpath[1]); + ASSERT_EQ(456u, checksums[1]); +} + +TEST_F(ClassLoaderContextTest, DecodeOatFileKeySpecialLibrary) { + std::string oat_file_encoding = "&"; + std::vector<std::string> classpath; + std::vector<uint32_t> checksums; + bool is_special_shared_library; + bool result = ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey( + oat_file_encoding, + &classpath, + &checksums, + &is_special_shared_library); + ASSERT_TRUE(result); + ASSERT_TRUE(is_special_shared_library); + ASSERT_TRUE(classpath.empty()); + ASSERT_TRUE(checksums.empty()); +} + } // namespace art diff --git a/runtime/oat.h b/runtime/oat.h index 521cc40764..5e61907d82 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '1', '2', '7', '\0' }; // .bss ArtMethod* section. + // Last oat version changed reason: update classpath key format. + static constexpr uint8_t kOatVersion[] = { '1', '2', '8', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 2ed30df372..1c1189d7de 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1574,28 +1574,6 @@ CompilerFilter::Filter OatFile::GetCompilerFilter() const { return GetOatHeader().GetCompilerFilter(); } -static constexpr char kDexClassPathEncodingSeparator = '*'; - -std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files, - const std::string& base_dir) { - std::ostringstream out; - - for (const DexFile* dex_file : dex_files) { - const std::string& location = dex_file->GetLocation(); - // Find paths that were relative and convert them back from absolute. - if (!base_dir.empty() && location.substr(0, base_dir.length()) == base_dir) { - out << location.substr(base_dir.length() + 1).c_str(); - } else { - out << dex_file->GetLocation().c_str(); - } - out << kDexClassPathEncodingSeparator; - out << dex_file->GetLocationChecksum(); - out << kDexClassPathEncodingSeparator; - } - - return out.str(); -} - OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found) { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 6393e091db..b112b84564 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -296,11 +296,6 @@ class OatFile { 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. - // Removes dex file paths prefixed with base_dir to convert them back to relative paths. - static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files, - const std::string& base_dir); - // Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on // error and sets found to false. static OatClass FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found); diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 630945a829..b166961795 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -28,6 +28,7 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "class_linker.h" +#include "class_loader_context.h" #include "dex_file-inl.h" #include "dex_file_tracking_registrar.h" #include "gc/scoped_gc_critical_section.h" @@ -421,38 +422,47 @@ static void GetDexFilesFromDexElementsArray( } } -static bool AreSharedLibrariesOk(const std::string& shared_libraries, - std::vector<const DexFile*>& dex_files) { - // If no shared libraries, we expect no dex files. - if (shared_libraries.empty()) { - return dex_files.empty(); - } - // If we find the special shared library, skip the shared libraries check. - if (shared_libraries.compare(OatFile::kSpecialSharedLibrary) == 0) { - return true; +static bool AreSharedLibrariesOk(const std::string& context_spec, + std::vector<const DexFile*>& dex_files, + std::string* error_msg) { + std::vector<std::string> classpath; + std::vector<uint32_t> checksums; + bool is_special_shared_library; + if (!ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey( + context_spec, &classpath, &checksums, &is_special_shared_library)) { + *error_msg = "Could not decode the class loader context from the oat file key."; + return false; } - // Shared libraries is a series of dex file paths and their checksums, each separated by '*'. - std::vector<std::string> shared_libraries_split; - Split(shared_libraries, '*', &shared_libraries_split); - // Sanity check size of dex files and split shared libraries. Should be 2x as many entries in - // the split shared libraries since it contains pairs of filename/checksum. - if (dex_files.size() * 2 != shared_libraries_split.size()) { + DCHECK_EQ(classpath.size(), checksums.size()); + + // The classpath size should match the number of dex files. + if (classpath.size() != dex_files.size()) { + *error_msg = "The number of loaded dex files does not match the number of files " + "specified in the context. Expected=" + std::to_string(classpath.size()) + + ", found=" + std::to_string(dex_files.size()); return false; } + // If we find the special shared library, skip the shared libraries check. + if (is_special_shared_library) { + return true; + } + // Check that the loaded dex files have the same order and checksums as the shared libraries. for (size_t i = 0; i < dex_files.size(); ++i) { + const std::string& dex_location = dex_files[i]->GetLocation(); + uint32_t dex_location_checksum = dex_files[i]->GetLocationChecksum(); std::string absolute_library_path = - OatFile::ResolveRelativeEncodedDexLocation(dex_files[i]->GetLocation().c_str(), - shared_libraries_split[i * 2]); - if (dex_files[i]->GetLocation() != absolute_library_path) { + OatFile::ResolveRelativeEncodedDexLocation(dex_location.c_str(), classpath[i]); + if (dex_location != absolute_library_path) { + *error_msg = "SharedLibraryCheck: expected=" + absolute_library_path + ", found=" + + dex_location; return false; } - char* end; - size_t shared_lib_checksum = strtoul(shared_libraries_split[i * 2 + 1].c_str(), &end, 10); - uint32_t dex_checksum = dex_files[i]->GetLocationChecksum(); - if (*end != '\0' || dex_checksum != shared_lib_checksum) { + if (dex_location_checksum != checksums[i]) { + *error_msg = "SharedLibraryCheck: checksum mismatch for " + dex_location + ". Expected=" + + std::to_string(checksums[i]) + ", found=" + std::to_string(dex_location_checksum); return false; } } @@ -586,7 +596,7 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, // Exit if shared libraries are ok. Do a full duplicate classes check otherwise. const std::string shared_libraries(oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); - if (AreSharedLibrariesOk(shared_libraries, dex_files_loaded)) { + if (AreSharedLibrariesOk(shared_libraries, dex_files_loaded, error_msg)) { return false; } |