From 1a9ac537f52b88dea02d02c3063283e32a085fb5 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Tue, 5 Mar 2019 11:57:13 +0000 Subject: 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 --- runtime/class_loader_context_test.cc | 136 +++++++++++++++++++++++++++++++++-- 1 file changed, 131 insertions(+), 5 deletions(-) (limited to 'runtime/class_loader_context_test.cc') diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 3c5f1ef95d..7fba830787 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -61,6 +61,13 @@ class ClassLoaderContextTest : public CommonRuntimeTest { 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 @@ class ClassLoaderContextTest : public CommonRuntimeTest { 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 @@ class ClassLoaderContextTest : public CommonRuntimeTest { 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, ParseValidSharedLibraryContext) { } TEST_F(ClassLoaderContextTest, ParseValidContextPCL) { - std::unique_ptr context = - ClassLoaderContext::Create("PCL[a.dex]"); + std::unique_ptr context = ClassLoaderContext::Create("PCL[a.dex]"); VerifyContextSize(context.get(), 1); VerifyClassLoaderPCL(context.get(), 0, "a.dex"); } TEST_F(ClassLoaderContextTest, ParseValidContextDLC) { - std::unique_ptr context = - ClassLoaderContext::Create("DLC[a.dex]"); + std::unique_ptr 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 context = ClassLoaderContext::Create("IMC[a.dex]"); + ASSERT_TRUE(context == nullptr); +} + TEST_F(ClassLoaderContextTest, ParseValidContextChain) { std::unique_ptr context = ClassLoaderContext::Create("PCL[a.dex:b.dex];DLC[c.dex:d.dex];PCL[e.dex]"); @@ -408,7 +436,6 @@ TEST_F(ClassLoaderContextTest, OpenValidDexFilesRelative) { return; } - std::unique_ptr context = ClassLoaderContext::Create( "PCL[" + multidex_name + ":" + myclass_dex_name + "];" + @@ -469,6 +496,19 @@ TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) { ASSERT_FALSE(context->OpenDexFiles(InstructionSet::kArm, "")); } +TEST_F(ClassLoaderContextTest, OpenDexFilesForIMCFails) { + std::unique_ptr 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 context = @@ -1079,6 +1119,34 @@ TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { 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 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 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { 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 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 context = ParseContextWithChecksums(context_spec); @@ -1200,6 +1284,25 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchWithSL) { 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 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 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { 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 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); -- cgit v1.2.3-59-g8ed1b