diff options
-rw-r--r-- | dex2oat/dex2oat.cc | 18 | ||||
-rw-r--r-- | dex2oat/dex2oat_test.cc | 11 | ||||
-rw-r--r-- | runtime/class_loader_context.cc | 31 | ||||
-rw-r--r-- | runtime/class_loader_context.h | 31 | ||||
-rw-r--r-- | runtime/class_loader_context_test.cc | 14 | ||||
-rw-r--r-- | runtime/oat_file_assistant.cc | 31 | ||||
-rw-r--r-- | runtime/oat_file_assistant.h | 11 | ||||
-rw-r--r-- | runtime/oat_file_assistant_test.cc | 69 | ||||
-rw-r--r-- | runtime/oat_file_manager.cc | 46 | ||||
-rw-r--r-- | runtime/oat_file_manager.h | 17 | ||||
-rw-r--r-- | tools/dexfuzz/src/dexfuzz/executors/Executor.java | 2 |
11 files changed, 200 insertions, 81 deletions
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index e0ad64946e..6fbfdef4a2 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1308,7 +1308,7 @@ class Dex2Oat FINAL { } else if (option.starts_with("--class-loader-context=")) { class_loader_context_ = ClassLoaderContext::Create( option.substr(strlen("--class-loader-context=")).data()); - if (class_loader_context_== nullptr) { + if (class_loader_context_ == nullptr) { Usage("Option --class-loader-context has an incorrect format: %s", option.data()); } } else if (option.starts_with("--dirty-image-objects=")) { @@ -1576,20 +1576,12 @@ class Dex2Oat FINAL { } // Open dex files for class path. + if (class_loader_context_ == nullptr) { - // TODO(calin): Temporary workaround while we transition to use - // --class-loader-context instead of --runtime-arg -cp - if (runtime_->GetClassPathString().empty()) { - class_loader_context_ = std::unique_ptr<ClassLoaderContext>( - new ClassLoaderContext()); - } else { - std::string spec = runtime_->GetClassPathString() == OatFile::kSpecialSharedLibrary - ? OatFile::kSpecialSharedLibrary - : "PCL[" + runtime_->GetClassPathString() + "]"; - class_loader_context_ = ClassLoaderContext::Create(spec); - } + // If no context was specified use the default one (which is an empty PathClassLoader). + class_loader_context_ = std::unique_ptr<ClassLoaderContext>(ClassLoaderContext::Default()); } - CHECK(class_loader_context_ != nullptr); + DCHECK_EQ(oat_writers_.size(), 1u); // Note: Ideally we would reject context where the source dex files are also diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 32877a8cd6..12bceb3c1f 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -134,7 +134,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { } } - // Check the input compiler filter against the generated oat file's filter. Mayb be overridden + // Check the input compiler filter against the generated oat file's filter. May be overridden // in subclasses when equality is not expected. virtual void CheckFilter(CompilerFilter::Filter expected, CompilerFilter::Filter actual) { EXPECT_EQ(expected, actual); @@ -153,14 +153,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { std::vector<std::string> argv; argv.push_back(runtime->GetCompilerExecutable()); - argv.push_back("--runtime-arg"); - argv.push_back("-classpath"); - argv.push_back("--runtime-arg"); - std::string class_path = runtime->GetClassPathString(); - if (class_path == "") { - class_path = OatFile::kSpecialSharedLibrary; - } - argv.push_back(class_path); + if (runtime->IsJavaDebuggable()) { argv.push_back("--debuggable"); } diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 92d0f8d5ae..e7051b35d8 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -68,6 +68,10 @@ ClassLoaderContext::~ClassLoaderContext() { } } +std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Default() { + return Create(""); +} + std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string& spec) { std::unique_ptr<ClassLoaderContext> result(new ClassLoaderContext()); if (result->Parse(spec)) { @@ -263,7 +267,16 @@ bool ClassLoaderContext::RemoveLocationsFromClassPaths( return removed_locations; } +std::string ClassLoaderContext::EncodeContextForDex2oat(const std::string& base_dir) const { + return EncodeContext(base_dir, /*for_dex2oat*/ true); +} + std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_dir) const { + return EncodeContext(base_dir, /*for_dex2oat*/ false); +} + +std::string ClassLoaderContext::EncodeContext(const std::string& base_dir, + bool for_dex2oat) const { CheckDexFilesOpened("EncodeContextForOatFile"); if (special_shared_library_) { return OatFile::kSpecialSharedLibrary; @@ -286,8 +299,17 @@ std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_ } out << GetClassLoaderTypeName(info.type); out << kClassLoaderOpeningMark; + std::set<std::string> seen_locations; 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]; + if (for_dex2oat) { + // dex2oat only needs the base location. It cannot accept multidex locations. + // So ensure we only add each file once. + bool new_insert = seen_locations.insert(dex_file->GetBaseLocation()).second; + if (!new_insert) { + continue; + } + } const std::string& location = dex_file->GetLocation(); if (k > 0) { out << kClasspathSeparator; @@ -298,8 +320,11 @@ std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_ } else { out << dex_file->GetLocation().c_str(); } - out << kDexFileChecksumSeparator; - out << dex_file->GetLocationChecksum(); + // dex2oat does not need the checksums. + if (!for_dex2oat) { + out << kDexFileChecksumSeparator; + out << dex_file->GetLocationChecksum(); + } } out << kClassLoaderClosingMark; } @@ -593,7 +618,7 @@ std::unique_ptr<ClassLoaderContext> ClassLoaderContext::CreateContextForClassLoa } } -bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) { +bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const { ClassLoaderContext expected_context; if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) { LOG(WARNING) << "Invalid class loader context: " << context_spec; diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 37dd02b07f..9afa880da4 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -34,9 +34,6 @@ class OatFile; // Utility class which holds the class loader context used during compilation/verification. class ClassLoaderContext { public: - // Creates an empty context (with no class loaders). - ClassLoaderContext(); - ~ClassLoaderContext(); // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files. @@ -82,9 +79,16 @@ class ClassLoaderContext { // (so that it can be read and verified at runtime against the actual class // loader hierarchy). // Should only be called if OpenDexFiles() returned true. - // E.g. if the context is PCL[a.dex:b.dex] this will return "a.dex*a_checksum*b.dex*a_checksum". + // E.g. if the context is PCL[a.dex:b.dex] this will return + // "PCL[a.dex*a_checksum*b.dex*a_checksum]". std::string EncodeContextForOatFile(const std::string& base_dir) const; + // Encodes the context as a string suitable to be passed to dex2oat. + // This is the same as EncodeContextForOatFile but without adding the checksums + // and only adding each dex files once (no multidex). + // Should only be called if OpenDexFiles() returned true. + std::string EncodeContextForDex2oat(const std::string& base_dir) const; + // Flattens the opened dex files into the given vector. // Should only be called if OpenDexFiles() returned true. std::vector<const DexFile*> FlattenOpenedDexFiles() const; @@ -94,7 +98,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) - bool VerifyClassLoaderContextMatch(const std::string& context_spec); + bool VerifyClassLoaderContextMatch(const std::string& context_spec) const; // Creates the class loader context from the given string. // The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]... @@ -119,6 +123,10 @@ class ClassLoaderContext { static std::unique_ptr<ClassLoaderContext> CreateContextForClassLoader(jobject class_loader, jobjectArray dex_elements); + // Returns the default class loader context to be used when none is specified. + // This will return a context with a single and empty PathClassLoader. + static std::unique_ptr<ClassLoaderContext> Default(); + private: enum ClassLoaderType { kInvalidClassLoader = 0, @@ -144,6 +152,9 @@ class ClassLoaderContext { explicit ClassLoaderInfo(ClassLoaderType cl_type) : type(cl_type) {} }; + // Creates an empty context (with no class loaders). + ClassLoaderContext(); + // Constructs an empty context. // `owns_the_dex_files` specifies whether or not the context will own the opened dex files // present in the class loader chain. If `owns_the_dex_files` is true then OpenDexFiles cannot @@ -173,7 +184,15 @@ class ClassLoaderContext { bool AddInfoToContextFromClassLoader(ScopedObjectAccessAlreadyRunnable& soa, Handle<mirror::ClassLoader> class_loader, Handle<mirror::ObjectArray<mirror::Object>> dex_elements) - REQUIRES_SHARED(Locks::mutator_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); + + // Encodes the context as a string suitable to be passed to dex2oat or to be added to the + // oat file as the class path key. + // If for_dex2oat is true, the encoding adds each file once (i.e. it does not add multidex + // location). Otherwise, for oat files, the encoding adds all the dex files (including multidex) + // together with their checksums. + // Should only be called if OpenDexFiles() returned true. + std::string EncodeContext(const std::string& base_dir, bool for_dex2oat) const; // 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 2b85188e38..d4688c1904 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -455,6 +455,20 @@ TEST_F(ClassLoaderContextTest, EncodeInOatFile) { ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile("")); } +TEST_F(ClassLoaderContextTest, EncodeForDex2oat) { + std::string dex1_name = GetTestDexFileName("Main"); + std::string dex2_name = GetTestDexFileName("MultiDex"); + std::unique_ptr<ClassLoaderContext> context = + ClassLoaderContext::Create("PCL[" + dex1_name + ":" + dex2_name + "]"); + ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "")); + + std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main"); + std::vector<std::unique_ptr<const DexFile>> dex2 = OpenTestDexFiles("MultiDex"); + std::string encoding = context->EncodeContextForDex2oat(""); + std::string expected_encoding = "PCL[" + dex1_name + ":" + dex2_name + "]"; + ASSERT_EQ(expected_encoding, context->EncodeContextForDex2oat("")); +} + // TODO(calin) add a test which creates the context for a class loader together with dex_elements. TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { // The chain is diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index dae41c1b67..3794f51cb7 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -37,6 +37,7 @@ #include "scoped_thread_state_change-inl.h" #include "utils.h" #include "vdex_file.h" +#include "class_loader_context.h" namespace art { @@ -225,13 +226,25 @@ bool OatFileAssistant::IsUpToDate() { } OatFileAssistant::ResultOfAttemptToUpdate -OatFileAssistant::MakeUpToDate(bool profile_changed, std::string* error_msg) { +OatFileAssistant::MakeUpToDate(bool profile_changed, + const std::string& class_loader_context, + std::string* error_msg) { CompilerFilter::Filter target; if (!GetRuntimeCompilerFilterOption(&target, error_msg)) { return kUpdateNotAttempted; } OatFileInfo& info = GetBestInfo(); + // TODO(calin, jeffhao): the context should really be passed to GetDexOptNeeded: b/62269291. + // This is actually not trivial in the current logic as it will interact with the collision + // check: + // - currently, if the context does not match but we have no collisions we still accept the + // oat file. + // - if GetDexOptNeeded would return kDex2OatFromScratch for a context mismatch and we make + // the oat code up to date the collision check becomes useless. + // - however, MakeUpToDate will not always succeed (e.g. for primary apks, or for dex files + // loaded in other processes). So it boils down to how far do we want to complicate + // the logic in order to enable the use of oat files. Maybe its time to try simplify it. switch (info.GetDexOptNeeded(target, profile_changed, /*downgrade*/ false)) { case kNoDexOptNeeded: return kUpdateSucceeded; @@ -243,7 +256,7 @@ OatFileAssistant::MakeUpToDate(bool profile_changed, std::string* error_msg) { case kDex2OatForBootImage: case kDex2OatForRelocation: case kDex2OatForFilter: - return GenerateOatFileNoChecks(info, target, error_msg); + return GenerateOatFileNoChecks(info, target, class_loader_context, error_msg); } UNREACHABLE(); } @@ -628,7 +641,10 @@ static bool PrepareOdexDirectories(const std::string& dex_location, } OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks( - OatFileAssistant::OatFileInfo& info, CompilerFilter::Filter filter, std::string* error_msg) { + OatFileAssistant::OatFileInfo& info, + CompilerFilter::Filter filter, + const std::string& class_loader_context, + std::string* error_msg) { CHECK(error_msg != nullptr); Runtime* runtime = Runtime::Current(); @@ -704,6 +720,7 @@ OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChe args.push_back("--oat-fd=" + std::to_string(oat_file->Fd())); args.push_back("--oat-location=" + oat_file_name); args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); + args.push_back("--class-loader-context=" + class_loader_context); if (!Dex2Oat(args, error_msg)) { // Manually delete the oat and vdex files. This ensures there is no garbage @@ -743,14 +760,6 @@ bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args, std::vector<std::string> argv; argv.push_back(runtime->GetCompilerExecutable()); - argv.push_back("--runtime-arg"); - argv.push_back("-classpath"); - argv.push_back("--runtime-arg"); - std::string class_path = runtime->GetClassPathString(); - if (class_path == "") { - class_path = OatFile::kSpecialSharedLibrary; - } - argv.push_back(class_path); if (runtime->IsJavaDebuggable()) { argv.push_back("--debuggable"); } diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 320aa4f860..5eec943689 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -185,12 +185,17 @@ class OatFileAssistant { // profile_changed should be true to indicate the profile has recently // changed for this dex location. // + // If the dex files need to be made up to date, class_loader_context will be + // passed to dex2oat. + // // Returns the result of attempting to update the code. // // If the result is not kUpdateSucceeded, the value of error_msg will be set // to a string describing why there was a failure or the update was not // attempted. error_msg must not be null. - ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed, std::string* error_msg); + ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed, + const std::string& class_loader_context, + std::string* error_msg); // Returns an oat file that can be used for loading dex files. // Returns null if no suitable oat file was found. @@ -389,7 +394,8 @@ class OatFileAssistant { }; // Generate the oat file for the given info from the dex file using the - // current runtime compiler options and the specified filter. + // current runtime compiler options, the specified filter and class loader + // context. // This does not check the current status before attempting to generate the // oat file. // @@ -398,6 +404,7 @@ class OatFileAssistant { // attempted. error_msg must not be null. ResultOfAttemptToUpdate GenerateOatFileNoChecks(OatFileInfo& info, CompilerFilter::Filter target, + const std::string& class_loader_context, std::string* error_msg); // Return info for the best oat file. diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index c59dafcb38..e048177fa4 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -27,8 +27,10 @@ #include "art_field-inl.h" #include "class_linker-inl.h" +#include "class_loader_context.h" #include "common_runtime_test.h" #include "dexopt_test.h" +#include "oat_file.h" #include "oat_file_manager.h" #include "os.h" #include "scoped_thread_state_change-inl.h" @@ -37,6 +39,8 @@ namespace art { +static const std::string kSpecialSharedLibrary = "&"; + class OatFileAssistantTest : public DexoptTest {}; class OatFileAssistantNoDex2OatTest : public DexoptTest { @@ -116,7 +120,8 @@ TEST_F(OatFileAssistantTest, NoDexNoOat) { // Trying to make the oat file up to date should not fail or crash. std::string error_msg; - EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, oat_file_assistant.MakeUpToDate(false, &error_msg)); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)); // Trying to get the best oat file should fail, but not crash. std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); @@ -769,7 +774,8 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) { std::string error_msg; Runtime::Current()->AddCompilerOption("--compiler-filter=speed"); EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, - oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg; + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) << + error_msg; EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); @@ -949,7 +955,7 @@ TEST_F(OatFileAssistantTest, GenNoDex) { // We should get kUpdateSucceeded from MakeUpToDate since there's nothing // that can be done in this situation. ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, - oat_file_assistant.MakeUpToDate(false, &error_msg)); + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)); // Verify it didn't create an oat in the default location (dalvik-cache). OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false); @@ -1028,7 +1034,7 @@ TEST_F(OatFileAssistantTest, ShortDexLocation) { std::string error_msg; Runtime::Current()->AddCompilerOption("--compiler-filter=speed"); EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, - oat_file_assistant.MakeUpToDate(false, &error_msg)); + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)); EXPECT_TRUE(error_msg.empty()); } @@ -1175,7 +1181,8 @@ TEST_F(OatFileAssistantTest, RuntimeCompilerFilterOptionUsed) { std::string error_msg; Runtime::Current()->AddCompilerOption("--compiler-filter=quicken"); EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, - oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg; + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) << + error_msg; EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken)); EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter, @@ -1183,7 +1190,8 @@ TEST_F(OatFileAssistantTest, RuntimeCompilerFilterOptionUsed) { Runtime::Current()->AddCompilerOption("--compiler-filter=speed"); EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, - oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg; + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) + << error_msg; EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken)); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, @@ -1191,7 +1199,7 @@ TEST_F(OatFileAssistantTest, RuntimeCompilerFilterOptionUsed) { Runtime::Current()->AddCompilerOption("--compiler-filter=bogus"); EXPECT_EQ(OatFileAssistant::kUpdateNotAttempted, - oat_file_assistant.MakeUpToDate(false, &error_msg)); + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)); } TEST(OatFileAssistantUtilsTest, DexLocationToOdexFilename) { @@ -1251,7 +1259,8 @@ TEST_F(OatFileAssistantTest, DefaultMakeUpToDateFilter) { OatFileAssistant::kDefaultCompilerFilterForDexLoading; std::string error_msg; EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, - oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg; + oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) << + error_msg; EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded(default_filter)); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); @@ -1259,6 +1268,50 @@ TEST_F(OatFileAssistantTest, DefaultMakeUpToDateFilter) { EXPECT_EQ(default_filter, oat_file->GetCompilerFilter()); } +TEST_F(OatFileAssistantTest, MakeUpToDateWithSpecialSharedLibrary) { + std::string dex_location = GetScratchDir() + "/TestDex.jar"; + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + const CompilerFilter::Filter default_filter = + OatFileAssistant::kDefaultCompilerFilterForDexLoading; + std::string error_msg; + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(default_filter)); + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + EXPECT_NE(nullptr, oat_file.get()); + EXPECT_EQ(kSpecialSharedLibrary, + oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); +} + +TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) { + std::string dex_location = GetScratchDir() + "/TestDex.jar"; + std::string context_location = GetScratchDir() + "/ContextDex.jar"; + Copy(GetDexSrc1(), dex_location); + Copy(GetDexSrc2(), context_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + const CompilerFilter::Filter default_filter = + OatFileAssistant::kDefaultCompilerFilterForDexLoading; + std::string error_msg; + std::string context_str = "PCL[" + context_location + "]"; + int status = oat_file_assistant.MakeUpToDate(false, context_str, &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded, + oat_file_assistant.GetDexOptNeeded(default_filter)); + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + EXPECT_NE(nullptr, oat_file.get()); + std::unique_ptr<ClassLoaderContext> context = + ClassLoaderContext::Create(context_str); + context->OpenDexFiles(kRuntimeISA, ""); + EXPECT_EQ(context->EncodeContextForOatFile(""), + oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); +} + // TODO: More Tests: // * Test class linker falls back to unquickened dex for DexNoOat // * Test class linker falls back to unquickened dex for MultiDexNoOat diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index e950fca862..5baf59c665 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -329,12 +329,14 @@ static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded, // Check for class-def collisions in dex files. // -// This first walks the class loader chain, getting all the dex files from the class loader. If -// the class loader is null or one of the class loaders in the chain is unsupported, we collect -// dex files from all open non-boot oat files to be safe. +// This first walks the class loader chain present in the given context, getting all the dex files +// from the class loader. // -// This first checks whether the shared libraries are in the expected order and the oat files -// have the expected checksums. If so, we exit early. Otherwise, we do the collision check. +// If the context is null (which means the initial class loader was null or unsupported) +// this returns false. b/37777332. +// +// This first checks whether all class loaders in the context have the same type and +// classpath. If so, we exit early. Otherwise, we do the collision check. // // The collision check works by maintaining a heap with one class from each dex file, sorted by the // class descriptor. Then a dex-file/class pair is continually removed from the heap and compared @@ -342,23 +344,11 @@ static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded, // the two elements agree on whether their dex file was from an already-loaded oat-file or the // new oat file. Any disagreement indicates a collision. bool OatFileManager::HasCollisions(const OatFile* oat_file, - jobject class_loader, - jobjectArray dex_elements, + const ClassLoaderContext* context, std::string* error_msg /*out*/) const { DCHECK(oat_file != nullptr); DCHECK(error_msg != nullptr); - // If the class_loader is null there's not much we can do. This happens if a dex files is loaded - // directly with DexFile APIs instead of using class loaders. - if (class_loader == nullptr) { - LOG(WARNING) << "Opening an oat file without a class loader. " - << "Are you using the deprecated DexFile APIs?"; - return false; - } - - std::unique_ptr<ClassLoaderContext> context = - ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements); - // The context might be null if there are unrecognized class loaders in the chain or they // don't meet sensible sanity conditions. In this case we assume that the app knows what it's // doing and accept the oat file. @@ -406,6 +396,17 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( Locks::mutator_lock_->AssertNotHeld(self); Runtime* const runtime = Runtime::Current(); + std::unique_ptr<ClassLoaderContext> context; + // If the class_loader is null there's not much we can do. This happens if a dex files is loaded + // directly with DexFile APIs instead of using class loaders. + if (class_loader == nullptr) { + LOG(WARNING) << "Opening an oat file without a class loader. " + << "Are you using the deprecated DexFile APIs?"; + context = nullptr; + } else { + context = ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements); + } + OatFileAssistant oat_file_assistant(dex_location, kRuntimeISA, !runtime->IsAotCompiler()); @@ -425,7 +426,12 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // Update the oat file on disk if we can, based on the --compiler-filter // option derived from the current runtime options. // This may fail, but that's okay. Best effort is all that matters here. - switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/false, /*out*/ &error_msg)) { + + const std::string& dex2oat_context = context == nullptr + ? OatFile::kSpecialSharedLibrary + : context->EncodeContextForDex2oat(/*base_dir*/ ""); + switch (oat_file_assistant.MakeUpToDate( + /*profile_changed*/false, dex2oat_context, /*out*/ &error_msg)) { case OatFileAssistant::kUpdateFailed: LOG(WARNING) << error_msg; break; @@ -451,7 +457,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { // Take the file only if it has no collisions, or we must take it because of preopting. bool accept_oat_file = - !HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg); + !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg); if (!accept_oat_file) { // Failed the collision check. Print warning. if (Runtime::Current()->IsDexFileFallbackEnabled()) { diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 05a5f5b7b9..4523494c5c 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -35,6 +35,7 @@ class ImageSpace; } // namespace space } // namespace gc +class ClassLoaderContext; class DexFile; class OatFile; @@ -105,15 +106,17 @@ class OatFileManager { void DumpForSigQuit(std::ostream& os); private: - // Check that the shared libraries in the given oat file match those in the given class loader and - // dex elements. If the class loader is null or we do not support one of the class loaders in the - // chain, compare against all non-boot oat files instead. If the shared libraries are not ok, - // check for duplicate class definitions of the given oat file against the oat files (either from - // the class loader and dex elements if possible or all non-boot oat files otherwise). + // Check that the class loader context of the given oat file matches the given context. + // This will perform a check that all class loaders in the chain have the same type and + // classpath. + // If the context is null (which means the initial class loader was null or unsupported) + // this returns false. + // If the context does not validate the method will check for duplicate class definitions of + // the given oat file against the oat files (either from the class loaders if possible or all + // non-boot oat files otherwise). // Return true if there are any class definition collisions in the oat_file. bool HasCollisions(const OatFile* oat_file, - jobject class_loader, - jobjectArray dex_elements, + const ClassLoaderContext* context, /*out*/ std::string* error_msg) const REQUIRES(!Locks::oat_file_manager_lock_); diff --git a/tools/dexfuzz/src/dexfuzz/executors/Executor.java b/tools/dexfuzz/src/dexfuzz/executors/Executor.java index 074672d0ff..0367a83059 100644 --- a/tools/dexfuzz/src/dexfuzz/executors/Executor.java +++ b/tools/dexfuzz/src/dexfuzz/executors/Executor.java @@ -114,8 +114,6 @@ public abstract class Executor { commandBuilder.append("--oat-file=output.oat "); commandBuilder.append("--android-root=").append(device.getAndroidHostOut()).append(" "); - commandBuilder.append("--runtime-arg -classpath "); - commandBuilder.append("--runtime-arg ").append(programName).append(" "); commandBuilder.append("--dex-file=").append(programName).append(" "); commandBuilder.append("--compiler-filter=quicken --runtime-arg -Xnorelocate "); |