Support InMemoryDexClassLoader in ClassLoaderContext
Add new class loader tag IMC to class loader context spec which
represents InMemoryDexClassLoader. A special case is required to not
attempt to open its dex files as the dex location does not correspond
to a real file path. This is achieved by setting load-attempted variable
to 'true' when encountering IMC whilst parsing a spec. Context with IMC
can still have opened dex files if it was created from an existing class
loader.
Bug: 72131483
Test: m test-art-host-gtest-class_loader_context_text
Change-Id: Ic64065819018a1e56dee0f65405d26beb8fd7bbd
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c7cf43f..1dd856e 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -9460,7 +9460,9 @@
CHECK(self->GetJniEnv()->IsSameObject(loader_class,
WellKnownClasses::dalvik_system_PathClassLoader) ||
self->GetJniEnv()->IsSameObject(loader_class,
- WellKnownClasses::dalvik_system_DelegateLastClassLoader));
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader) ||
+ self->GetJniEnv()->IsSameObject(loader_class,
+ WellKnownClasses::dalvik_system_InMemoryDexClassLoader));
// SOAAlreadyRunnable is protected, and we need something to add a global reference.
// We could move the jobject to the callers, but all call-sites do this...
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 61843f4..165b42d 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -44,6 +44,7 @@
static constexpr char kPathClassLoaderString[] = "PCL";
static constexpr char kDelegateLastClassLoaderString[] = "DLC";
+static constexpr char kInMemoryDexClassLoaderString[] = "IMC";
static constexpr char kClassLoaderOpeningMark = '[';
static constexpr char kClassLoaderClosingMark = ']';
static constexpr char kClassLoaderSharedLibraryOpeningMark = '{';
@@ -154,6 +155,24 @@
if (class_loader_type == kInvalidClassLoader) {
return nullptr;
}
+
+ // InMemoryDexClassLoader's dex location is always bogus. Special-case it.
+ if (class_loader_type == kInMemoryDexClassLoader) {
+ if (parse_checksums) {
+ // Make sure that OpenDexFiles() will never be attempted on this context
+ // because the dex locations of IMC do not correspond to real files.
+ CHECK(!dex_files_open_attempted_ || !dex_files_open_result_)
+ << "Parsing spec not supported when context created from a ClassLoader object";
+ dex_files_open_attempted_ = true;
+ dex_files_open_result_ = false;
+ } else {
+ // Checksums are not provided and dex locations themselves have no meaning
+ // (although we keep them in the spec to simplify parsing). Treat this as
+ // an unknown class loader.
+ return nullptr;
+ }
+ }
+
const char* class_loader_type_str = GetClassLoaderTypeName(class_loader_type);
size_t type_str_size = strlen(class_loader_type_str);
@@ -192,6 +211,7 @@
if (!android::base::ParseUint(dex_file_with_checksum[1].c_str(), &checksum)) {
return nullptr;
}
+
info->classpath.push_back(dex_file_with_checksum[0]);
info->checksums.push_back(checksum);
}
@@ -261,7 +281,9 @@
// recognized.
ClassLoaderContext::ClassLoaderType
ClassLoaderContext::ExtractClassLoaderType(const std::string& class_loader_spec) {
- const ClassLoaderType kValidTypes[] = {kPathClassLoader, kDelegateLastClassLoader};
+ const ClassLoaderType kValidTypes[] = { kPathClassLoader,
+ kDelegateLastClassLoader,
+ kInMemoryDexClassLoader };
for (const ClassLoaderType& type : kValidTypes) {
const char* type_str = GetClassLoaderTypeName(type);
if (class_loader_spec.compare(0, strlen(type_str), type_str) == 0) {
@@ -649,6 +671,8 @@
return WellKnownClasses::dalvik_system_PathClassLoader;
case ClassLoaderContext::kDelegateLastClassLoader:
return WellKnownClasses::dalvik_system_DelegateLastClassLoader;
+ case ClassLoaderContext::kInMemoryDexClassLoader:
+ return WellKnownClasses::dalvik_system_InMemoryDexClassLoader;
case ClassLoaderContext::kInvalidClassLoader: break; // will fail after the switch.
}
LOG(FATAL) << "Invalid class loader type " << type;
@@ -818,6 +842,7 @@
switch (type) {
case kPathClassLoader: return kPathClassLoaderString;
case kDelegateLastClassLoader: return kDelegateLastClassLoaderString;
+ case kInMemoryDexClassLoader: return kInMemoryDexClassLoaderString;
default:
LOG(FATAL) << "Invalid class loader type " << type;
UNREACHABLE();
@@ -867,7 +892,9 @@
Handle<mirror::ClassLoader> class_loader,
std::vector<const DexFile*>* out_dex_files)
REQUIRES_SHARED(Locks::mutator_lock_) {
- CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader));
+ CHECK(IsPathOrDexClassLoader(soa, class_loader) ||
+ IsDelegateLastClassLoader(soa, class_loader) ||
+ IsInMemoryDexClassLoader(soa, class_loader));
// All supported class loaders inherit from BaseDexClassLoader.
// We need to get the DexPathList and loop through it.
@@ -986,6 +1013,8 @@
type = kPathClassLoader;
} else if (IsDelegateLastClassLoader(soa, class_loader)) {
type = kDelegateLastClassLoader;
+ } else if (IsInMemoryDexClassLoader(soa, class_loader)) {
+ type = kInMemoryDexClassLoader;
} else {
LOG(WARNING) << "Unsupported class loader";
return false;
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index f8387ce..224b4d6 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -45,7 +45,8 @@
enum ClassLoaderType {
kInvalidClassLoader = 0,
kPathClassLoader = 1,
- kDelegateLastClassLoader = 2
+ kDelegateLastClassLoader = 2,
+ kInMemoryDexClassLoader = 3
};
~ClassLoaderContext();
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 3c5f1ef..7fba830 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -61,6 +61,13 @@
context, index, ClassLoaderContext::kDelegateLastClassLoader, classpath);
}
+ void VerifyClassLoaderIMC(ClassLoaderContext* context,
+ size_t index,
+ const std::string& classpath) {
+ VerifyClassLoaderInfo(
+ context, index, ClassLoaderContext::kInMemoryDexClassLoader, classpath);
+ }
+
void VerifyClassLoaderSharedLibraryPCL(ClassLoaderContext* context,
size_t loader_index,
size_t shared_library_index,
@@ -70,6 +77,15 @@
classpath);
}
+ void VerifyClassLoaderSharedLibraryIMC(ClassLoaderContext* context,
+ size_t loader_index,
+ size_t shared_library_index,
+ const std::string& classpath) {
+ VerifyClassLoaderInfoSL(
+ context, loader_index, shared_library_index, ClassLoaderContext::kInMemoryDexClassLoader,
+ classpath);
+ }
+
void VerifySharedLibrariesSize(ClassLoaderContext* context,
size_t loader_index,
size_t expected_size) {
@@ -102,6 +118,13 @@
context, index, ClassLoaderContext::kDelegateLastClassLoader, test_name);
}
+ void VerifyClassLoaderIMCFromTestDex(ClassLoaderContext* context,
+ size_t index,
+ const std::string& test_name) {
+ VerifyClassLoaderFromTestDex(
+ context, index, ClassLoaderContext::kInMemoryDexClassLoader, test_name);
+ }
+
enum class LocationCheck {
kEquals,
kEndsWith
@@ -249,19 +272,24 @@
}
TEST_F(ClassLoaderContextTest, ParseValidContextPCL) {
- std::unique_ptr<ClassLoaderContext> context =
- ClassLoaderContext::Create("PCL[a.dex]");
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("PCL[a.dex]");
VerifyContextSize(context.get(), 1);
VerifyClassLoaderPCL(context.get(), 0, "a.dex");
}
TEST_F(ClassLoaderContextTest, ParseValidContextDLC) {
- std::unique_ptr<ClassLoaderContext> context =
- ClassLoaderContext::Create("DLC[a.dex]");
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("DLC[a.dex]");
VerifyContextSize(context.get(), 1);
VerifyClassLoaderDLC(context.get(), 0, "a.dex");
}
+TEST_F(ClassLoaderContextTest, ParseInvalidContextIMC) {
+ // IMC is treated as an unknown class loader unless a checksum is provided.
+ // This is because the dex location is always bogus.
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("IMC[a.dex]");
+ ASSERT_TRUE(context == nullptr);
+}
+
TEST_F(ClassLoaderContextTest, ParseValidContextChain) {
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create("PCL[a.dex:b.dex];DLC[c.dex:d.dex];PCL[e.dex]");
@@ -408,7 +436,6 @@
return;
}
-
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create(
"PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
@@ -469,6 +496,19 @@
ASSERT_FALSE(context->OpenDexFiles(InstructionSet::kArm, ""));
}
+TEST_F(ClassLoaderContextTest, OpenDexFilesForIMCFails) {
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string dex_name = GetTestDexFileName("Main");
+
+ context = ParseContextWithChecksums("PCL[" + dex_name + "*111]");
+ VerifyContextSize(context.get(), 1);
+ ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "."));
+
+ context = ParseContextWithChecksums("IMC[" + dex_name + "*111]");
+ VerifyContextSize(context.get(), 1);
+ ASSERT_FALSE(context->OpenDexFiles(InstructionSet::kArm, "."));
+}
+
TEST_F(ClassLoaderContextTest, CreateClassLoader) {
std::string dex_name = GetTestDexFileName("Main");
std::unique_ptr<ClassLoaderContext> context =
@@ -1079,6 +1119,34 @@
VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA");
}
+TEST_F(ClassLoaderContextTest, CreateContextForClassLoaderIMC) {
+ // The chain is
+ // ClassLoaderA (PathClassLoader)
+ // ^
+ // |
+ // ClassLoaderB (InMemoryDexClassLoader)
+ // ^
+ // |
+ // ClassLoaderC (InMemoryDexClassLoader)
+ // ^
+ // |
+ // ClassLoaderD (DelegateLastClassLoader)
+
+ jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+ jobject class_loader_b = LoadDexInInMemoryDexClassLoader("ForClassLoaderB", class_loader_a);
+ jobject class_loader_c = LoadDexInInMemoryDexClassLoader("ForClassLoaderC", class_loader_b);
+ jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
+
+ std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
+
+ VerifyContextForClassLoader(context.get());
+ VerifyContextSize(context.get(), 4);
+
+ VerifyClassLoaderDLCFromTestDex(context.get(), 0, "ForClassLoaderD");
+ VerifyClassLoaderIMCFromTestDex(context.get(), 1, "ForClassLoaderC");
+ VerifyClassLoaderIMCFromTestDex(context.get(), 2, "ForClassLoaderB");
+ VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA");
+}
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextFirstElement) {
std::string context_spec = "PCL[]";
@@ -1133,6 +1201,22 @@
ClassLoaderContext::VerificationResult::kMismatch);
}
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextWithIMCMatch) {
+ std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];IMC[d.dex*111]";
+ std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
+ // Pretend that we successfully open the dex files to pass the DCHECKS.
+ // (as it's much easier to test all the corner cases without relying on actual dex files).
+ PretendContextOpenedDexFiles(context.get());
+
+ VerifyContextSize(context.get(), 3);
+ VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");
+ VerifyClassLoaderDLC(context.get(), 1, "c.dex");
+ VerifyClassLoaderIMC(context.get(), 2, "d.dex");
+
+ ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
+ ClassLoaderContext::VerificationResult::kVerifies);
+}
+
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchSpecial) {
std::string context_spec = "&";
std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
@@ -1200,6 +1284,25 @@
ClassLoaderContext::VerificationResult::kMismatch);
}
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchWithIMCSL) {
+ std::string context_spec =
+ "IMC[a.dex*123:b.dex*456]{IMC[d.dex*321];IMC[e.dex*654]#IMC[f.dex*098:g.dex*999]}"
+ ";DLC[c.dex*890]";
+ std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
+ // Pretend that we successfully open the dex files to pass the DCHECKS.
+ // (as it's much easier to test all the corner cases without relying on actual dex files).
+ PretendContextOpenedDexFiles(context.get());
+
+ VerifyContextSize(context.get(), 2);
+ VerifyClassLoaderIMC(context.get(), 0, "a.dex:b.dex");
+ VerifyClassLoaderDLC(context.get(), 1, "c.dex");
+ VerifyClassLoaderSharedLibraryIMC(context.get(), 0, 0, "d.dex");
+ VerifyClassLoaderSharedLibraryIMC(context.get(), 0, 1, "f.dex:g.dex");
+
+ ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
+ ClassLoaderContext::VerificationResult::kVerifies);
+}
+
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) {
jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a);
@@ -1223,6 +1326,29 @@
ClassLoaderContext::VerificationResult::kVerifies);
}
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingIMC) {
+ jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+ jobject class_loader_b = LoadDexInInMemoryDexClassLoader("ForClassLoaderB", class_loader_a);
+ jobject class_loader_c = LoadDexInInMemoryDexClassLoader("ForClassLoaderC", class_loader_b);
+ jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
+
+ std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
+
+ std::string context_with_no_base_dir = context->EncodeContextForOatFile("");
+ ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir),
+ ClassLoaderContext::VerificationResult::kVerifies);
+
+ std::string dex_location = GetTestDexFileName("ForClassLoaderA");
+ size_t pos = dex_location.rfind('/');
+ ASSERT_NE(std::string::npos, pos);
+ std::string parent = dex_location.substr(0, pos);
+
+ std::string context_with_base_dir = context->EncodeContextForOatFile(parent);
+ ASSERT_NE(context_with_base_dir, context_with_no_base_dir);
+ ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir),
+ ClassLoaderContext::VerificationResult::kVerifies);
+}
+
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) {
jobject class_loader = LoadDexInPathClassLoader("MultiDex", nullptr);
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index ca410d9..87c7ba6 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -331,6 +331,13 @@
parent_loader);
}
+jobject CommonRuntimeTestImpl::LoadDexInInMemoryDexClassLoader(const std::string& dex_name,
+ jobject parent_loader) {
+ return LoadDexInWellKnownClassLoader(dex_name,
+ WellKnownClasses::dalvik_system_InMemoryDexClassLoader,
+ parent_loader);
+}
+
void CommonRuntimeTestImpl::FillHeap(Thread* self,
ClassLinker* class_linker,
VariableSizedHandleScope* handle_scope) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index ab90fc5..9b1e653 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -125,6 +125,7 @@
jobject parent_loader,
jobject shared_libraries = nullptr);
jobject LoadDexInDelegateLastClassLoader(const std::string& dex_name, jobject parent_loader);
+ jobject LoadDexInInMemoryDexClassLoader(const std::string& dex_name, jobject parent_loader);
jobject LoadDexInWellKnownClassLoader(const std::string& dex_name,
jclass loader_class,
jobject parent_loader,