Move dex files dependencies (en/de)coding to ClassLoaderContext
Encode the full class loader context in the oat file (rather than just a
list of dex files).
The context encoding matches the format used by dex2oat with the addition
of checksums.
Temporarily assert that at decoding time we are operating on a
PathClassLoader until the checking logic covers all supported cases.
Also, bump the version of the oat file because the format of the classpath
key has changed.
This is a transition step to minimize the size of follow up changes.
Test: m test-art-host
Bug: 38138251
Change-Id: I9ec0cfe092ce1afccb741a36e737896880d5f1d2
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 1505eb5..b08b055 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -937,7 +937,7 @@
return GetOdexDir() + "/Context.odex";
}
- const char* kEmptyClassPathKey = "";
+ const char* kEmptyClassPathKey = "PCL[]";
};
TEST_F(Dex2oatClassLoaderContextTest, InvalidContext) {
@@ -961,10 +961,10 @@
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 @@
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 @@
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 5cbcd8f..2bed1d5 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -31,8 +31,9 @@
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 @@
}
}
-// 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 @@
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 @@
// 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 @@
}
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 @@
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 @@
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 @@
<< "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 4af5017..9727a3b 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -59,6 +59,8 @@
// 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 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 @@
// 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 @@
// 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 4643e78..03eb0e4 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -230,6 +230,42 @@
}
}
+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 @@
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 521cc40..5e61907 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,8 @@
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 2ed30df..1c1189d 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1574,28 +1574,6 @@
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 6393e09..b112b84 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -296,11 +296,6 @@
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 630945a..b166961 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 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;
- }
- // 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()) {
+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;
}
+ 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 @@
// 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;
}