diff options
| -rw-r--r-- | runtime/class_loader_context.cc | 32 | ||||
| -rw-r--r-- | runtime/class_loader_context.h | 8 | ||||
| -rw-r--r-- | runtime/class_loader_context_test.cc | 147 |
3 files changed, 93 insertions, 94 deletions
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 56573f550e..3bd45966e4 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -187,7 +187,10 @@ bool ClassLoaderContext::Parse(const std::string& spec, bool parse_checksums) { // Opens requested class path files and appends them to opened_dex_files. If the dex files have // been stripped, this opens them from their oat files (which get added to opened_oat_files). bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& classpath_dir) { - CHECK(!dex_files_open_attempted_) << "OpenDexFiles should not be called twice"; + if (dex_files_open_attempted_) { + // Do not attempt to re-open the files if we already tried. + return dex_files_open_result_; + } dex_files_open_attempted_ = true; // Assume we can open all dex files. If not, we will set this to false as we go. @@ -203,6 +206,7 @@ bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& cla // TODO(calin): Refine the dex opening interface to be able to tell if an archive contains // no dex files. So that we can distinguish the real failures... for (ClassLoaderInfo& info : class_loader_chain_) { + size_t opened_dex_files_index = info.opened_dex_files.size(); for (const std::string& cp_elem : info.classpath) { // If path is relative, append it to the provided base directory. std::string raw_location = cp_elem; @@ -249,6 +253,23 @@ bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& cla } } } + + // We finished opening the dex files from the classpath. + // Now update the classpath and the checksum with the locations of the dex files. + // + // We do this because initially the classpath contains the paths of the dex files; and + // some of them might be multi-dexes. So in order to have a consistent view we replace all the + // file paths with the actual dex locations being loaded. + // This will allow the context to VerifyClassLoaderContextMatch which expects or multidex + // location in the class paths. + // Note that this will also remove the paths that could not be opened. + info.classpath.clear(); + info.checksums.clear(); + for (size_t k = opened_dex_files_index; k < info.opened_dex_files.size(); k++) { + std::unique_ptr<const DexFile>& dex = info.opened_dex_files[k]; + info.classpath.push_back(dex->GetLocation()); + info.checksums.push_back(dex->GetLocationChecksum()); + } } return dex_files_open_result_; @@ -637,13 +658,20 @@ static bool IsAbsoluteLocation(const std::string& location) { } bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const { + DCHECK(dex_files_open_attempted_); + DCHECK(dex_files_open_result_); + ClassLoaderContext expected_context; if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) { LOG(WARNING) << "Invalid class loader context: " << context_spec; return false; } - if (expected_context.special_shared_library_) { + // Special shared library contexts always match. They essentially instruct the runtime + // to ignore the class path check because the oat file is known to be loaded in different + // contexts. OatFileManager will further verify if the oat file can be loaded based on the + // collision check. + if (special_shared_library_ || expected_context.special_shared_library_) { return true; } diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 9afa880da4..692a6cda5b 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -41,7 +41,12 @@ class ClassLoaderContext { // to ClassLoaderInfo::opened_oat_files. The 'classpath_dir' argument specifies the directory to // use for the relative class paths. // Returns true if all dex files where successfully opened. - // It may be called only once per ClassLoaderContext. The second call will abort. + // It may be called only once per ClassLoaderContext. Subsequent calls will return the same + // result without doing anything. + // + // This will replace the class path locations with the locations of the opened dex files. + // (Note that one dex file can contain multidexes. Each multidex will be added to the classpath + // separately.) // // Note that a "false" return could mean that either an apk/jar contained no dex files or // that we hit a I/O or checksum mismatch error. @@ -98,6 +103,7 @@ class ClassLoaderContext { // - the number and type of the class loaders from the chain matches // - the class loader from the same position have the same classpath // (the order and checksum of the dex files matches) + // This should be called after OpenDexFiles(). bool VerifyClassLoaderContextMatch(const std::string& context_spec) const; // Creates the class loader context from the given string. diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 18472743fb..ae3dcecb4a 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -87,60 +87,29 @@ class ClassLoaderContextTest : public CommonRuntimeTest { void VerifyOpenDexFiles( ClassLoaderContext* context, size_t index, - std::vector<std::vector<std::unique_ptr<const DexFile>>*>& all_dex_files, - LocationCheck mode = LocationCheck::kEquals, - BaseLocationCheck base_mode = BaseLocationCheck::kEquals) { + std::vector<std::unique_ptr<const DexFile>>* all_dex_files) { ASSERT_TRUE(context != nullptr); ASSERT_TRUE(context->dex_files_open_attempted_); ASSERT_TRUE(context->dex_files_open_result_); ClassLoaderContext::ClassLoaderInfo& info = context->class_loader_chain_[index]; - ASSERT_EQ(all_dex_files.size(), info.classpath.size()); + ASSERT_EQ(all_dex_files->size(), info.classpath.size()); + ASSERT_EQ(all_dex_files->size(), info.opened_dex_files.size()); size_t cur_open_dex_index = 0; - for (size_t k = 0; k < all_dex_files.size(); k++) { - std::vector<std::unique_ptr<const DexFile>>& dex_files_for_cp_elem = *(all_dex_files[k]); - for (size_t i = 0; i < dex_files_for_cp_elem.size(); i++) { - ASSERT_LT(cur_open_dex_index, info.opened_dex_files.size()); - - std::unique_ptr<const DexFile>& opened_dex_file = + for (size_t k = 0; k < all_dex_files->size(); k++) { + std::unique_ptr<const DexFile>& opened_dex_file = info.opened_dex_files[cur_open_dex_index++]; - std::unique_ptr<const DexFile>& expected_dex_file = dex_files_for_cp_elem[i]; - - std::string expected_location = expected_dex_file->GetBaseLocation(); - UniqueCPtr<const char[]> expected_real_location( - realpath(expected_location.c_str(), nullptr)); - ASSERT_TRUE(expected_real_location != nullptr) << expected_location; - expected_location.assign(expected_real_location.get()); - expected_location += DexFile::GetMultiDexSuffix(expected_dex_file->GetLocation()); - - switch (mode) { - case LocationCheck::kEquals: - ASSERT_EQ(expected_dex_file->GetLocation(), opened_dex_file->GetLocation()); - break; - case LocationCheck::kEndsWith: - ASSERT_TRUE(android::base::EndsWith(expected_dex_file->GetLocation(), - opened_dex_file->GetLocation().c_str())) - << opened_dex_file->GetLocation() << " vs " << expected_dex_file->GetLocation(); - break; - } - ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum()); - - std::string class_path_location = info.classpath[k]; - UniqueCPtr<const char[]> class_path_location_real( - realpath(class_path_location.c_str(), nullptr)); - ASSERT_TRUE(class_path_location_real != nullptr); - class_path_location.assign(class_path_location_real.get()); - switch (base_mode) { - case BaseLocationCheck::kEquals: - ASSERT_EQ(class_path_location, opened_dex_file->GetBaseLocation()); - break; - - case BaseLocationCheck::kEndsWith: - ASSERT_TRUE(android::base::EndsWith(opened_dex_file->GetBaseLocation(), - class_path_location.c_str())) - << info.classpath[k] << " vs " << opened_dex_file->GetBaseLocation(); - break; - } - } + std::unique_ptr<const DexFile>& expected_dex_file = (*all_dex_files)[k]; + + std::string expected_location = expected_dex_file->GetBaseLocation(); + UniqueCPtr<const char[]> expected_real_location( + realpath(expected_location.c_str(), nullptr)); + ASSERT_TRUE(expected_real_location != nullptr) << expected_location; + expected_location.assign(expected_real_location.get()); + expected_location += DexFile::GetMultiDexSuffix(expected_dex_file->GetLocation()); + + ASSERT_EQ(expected_location, opened_dex_file->GetLocation()); + ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum()); + ASSERT_EQ(info.classpath[k], opened_dex_file->GetLocation()); } } @@ -182,6 +151,11 @@ class ClassLoaderContextTest : public CommonRuntimeTest { } } + void PretendContextOpenedDexFiles(ClassLoaderContext* context) { + context->dex_files_open_attempted_ = true; + context->dex_files_open_result_ = true; + } + private: void VerifyClassLoaderInfo(ClassLoaderContext* context, size_t index, @@ -201,11 +175,9 @@ class ClassLoaderContextTest : public CommonRuntimeTest { ClassLoaderContext::ClassLoaderType type, const std::string& test_name) { std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(test_name.c_str()); - std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files; - all_dex_files.push_back(&dex_files); VerifyClassLoaderInfo(context, index, type, GetTestDexFileName(test_name.c_str())); - VerifyOpenDexFiles(context, index, all_dex_files); + VerifyOpenDexFiles(context, index, &dex_files); } }; @@ -276,11 +248,8 @@ TEST_F(ClassLoaderContextTest, OpenInvalidDexFiles) { TEST_F(ClassLoaderContextTest, OpenValidDexFiles) { std::string multidex_name = GetTestDexFileName("MultiDex"); - std::vector<std::unique_ptr<const DexFile>> multidex_files = OpenTestDexFiles("MultiDex"); std::string myclass_dex_name = GetTestDexFileName("MyClass"); - std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass"); std::string dex_name = GetTestDexFileName("Main"); - std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Main"); std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create( @@ -290,14 +259,16 @@ TEST_F(ClassLoaderContextTest, OpenValidDexFiles) { ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ "")); VerifyContextSize(context.get(), 2); - std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0; - all_dex_files0.push_back(&multidex_files); - all_dex_files0.push_back(&myclass_dex_files); - std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files1; - all_dex_files1.push_back(&dex_files); - - VerifyOpenDexFiles(context.get(), 0, all_dex_files0); - VerifyOpenDexFiles(context.get(), 1, all_dex_files1); + + std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex"); + std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass"); + for (size_t i = 0; i < myclass_dex_files.size(); i++) { + all_dex_files0.emplace_back(myclass_dex_files[i].release()); + } + VerifyOpenDexFiles(context.get(), 0, &all_dex_files0); + + std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main"); + VerifyOpenDexFiles(context.get(), 1, &all_dex_files1); } class ScratchSymLink { @@ -330,11 +301,10 @@ TEST_F(ClassLoaderContextTest, OpenValidDexFilesSymLink) { ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ "")); VerifyContextSize(context.get(), 1); - std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0; + std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass"); - all_dex_files0.push_back(&myclass_dex_files); - VerifyOpenDexFiles(context.get(), 0, all_dex_files0); + VerifyOpenDexFiles(context.get(), 0, &myclass_dex_files); } static std::string CreateRelativeString(const std::string& in, const char* cwd) { @@ -353,11 +323,8 @@ TEST_F(ClassLoaderContextTest, OpenValidDexFilesRelative) { PLOG(FATAL) << "Could not get working directory"; } std::string multidex_name = CreateRelativeString(GetTestDexFileName("MultiDex"), cwd_buf); - std::vector<std::unique_ptr<const DexFile>> multidex_files = OpenTestDexFiles("MultiDex"); std::string myclass_dex_name = CreateRelativeString(GetTestDexFileName("MyClass"), cwd_buf); - std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass"); std::string dex_name = CreateRelativeString(GetTestDexFileName("Main"), cwd_buf); - std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Main"); std::unique_ptr<ClassLoaderContext> context = @@ -367,15 +334,15 @@ TEST_F(ClassLoaderContextTest, OpenValidDexFilesRelative) { ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ "")); - VerifyContextSize(context.get(), 2); - std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0; - all_dex_files0.push_back(&multidex_files); - all_dex_files0.push_back(&myclass_dex_files); - std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files1; - all_dex_files1.push_back(&dex_files); - - VerifyOpenDexFiles(context.get(), 0, all_dex_files0, LocationCheck::kEndsWith); - VerifyOpenDexFiles(context.get(), 1, all_dex_files1, LocationCheck::kEndsWith); + std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex"); + std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass"); + for (size_t i = 0; i < myclass_dex_files.size(); i++) { + all_dex_files0.emplace_back(myclass_dex_files[i].release()); + } + VerifyOpenDexFiles(context.get(), 0, &all_dex_files0); + + std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main"); + VerifyOpenDexFiles(context.get(), 1, &all_dex_files1); } TEST_F(ClassLoaderContextTest, OpenValidDexFilesClasspathDir) { @@ -384,12 +351,8 @@ TEST_F(ClassLoaderContextTest, OpenValidDexFilesClasspathDir) { PLOG(FATAL) << "Could not get working directory"; } std::string multidex_name = CreateRelativeString(GetTestDexFileName("MultiDex"), cwd_buf); - std::vector<std::unique_ptr<const DexFile>> multidex_files = OpenTestDexFiles("MultiDex"); std::string myclass_dex_name = CreateRelativeString(GetTestDexFileName("MyClass"), cwd_buf); - std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass"); std::string dex_name = CreateRelativeString(GetTestDexFileName("Main"), cwd_buf); - std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Main"); - std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create( @@ -399,16 +362,15 @@ TEST_F(ClassLoaderContextTest, OpenValidDexFilesClasspathDir) { ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, cwd_buf)); VerifyContextSize(context.get(), 2); - std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0; - all_dex_files0.push_back(&multidex_files); - all_dex_files0.push_back(&myclass_dex_files); - std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files1; - all_dex_files1.push_back(&dex_files); - - VerifyOpenDexFiles( - context.get(), 0, all_dex_files0, LocationCheck::kEquals, BaseLocationCheck::kEndsWith); - VerifyOpenDexFiles( - context.get(), 1, all_dex_files1, LocationCheck::kEquals, BaseLocationCheck::kEndsWith); + std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex"); + std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass"); + for (size_t i = 0; i < myclass_dex_files.size(); i++) { + all_dex_files0.emplace_back(myclass_dex_files[i].release()); + } + VerifyOpenDexFiles(context.get(), 0, &all_dex_files0); + + std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main"); + VerifyOpenDexFiles(context.get(), 1, &all_dex_files1); } TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) { @@ -660,6 +622,9 @@ TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { std::string context_spec = "PCL[a.dex*123:b.dex*456];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); VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); |