Add stored class loader context option
Motivation: Enable having a different class loader context during
preopt vs the one stored in the oat file.
Added test.
Bug: 70934104
Bug: 67345922
Test: test-art-host
Change-Id: I6c0851370e0740e5f47faf25a5494022034f6fa4
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 6667fd6..693ead5 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -428,6 +428,10 @@
UsageError(" --class-loader-context=<string spec>: a string specifying the intended");
UsageError(" runtime loading context for the compiled dex files.");
UsageError("");
+ UsageError(" --stored-class-loader-context=<string spec>: a string specifying the intended");
+ UsageError(" runtime loading context that is stored in the oat file. Overrides");
+ UsageError(" --class-loader-context. Note that this ignores the classpath_dir arg.");
+ UsageError("");
UsageError(" It describes how the class loader chain should be built in order to ensure");
UsageError(" classes are resolved during dex2aot as they would be resolved at runtime.");
UsageError(" This spec will be encoded in the oat file. If at runtime the dex file is");
@@ -1260,11 +1264,32 @@
ParseInstructionSetFeatures(*args.Get(M::TargetInstructionSetFeatures), parser_options.get());
}
if (args.Exists(M::ClassLoaderContext)) {
- class_loader_context_ = ClassLoaderContext::Create(*args.Get(M::ClassLoaderContext));
+ std::string class_loader_context_arg = *args.Get(M::ClassLoaderContext);
+ class_loader_context_ = ClassLoaderContext::Create(class_loader_context_arg);
if (class_loader_context_ == nullptr) {
Usage("Option --class-loader-context has an incorrect format: %s",
- args.Get(M::ClassLoaderContext)->c_str());
+ class_loader_context_arg.c_str());
}
+ if (args.Exists(M::StoredClassLoaderContext)) {
+ stored_class_loader_context_.reset(new std::string(*args.Get(M::StoredClassLoaderContext)));
+ std::unique_ptr<ClassLoaderContext> temp_context =
+ ClassLoaderContext::Create(*stored_class_loader_context_);
+ if (temp_context == nullptr) {
+ Usage("Option --stored-class-loader-context has an incorrect format: %s",
+ stored_class_loader_context_->c_str());
+ } else if (!class_loader_context_->VerifyClassLoaderContextMatch(
+ *stored_class_loader_context_,
+ /*verify_names*/ false,
+ /*verify_checksums*/ false)) {
+ Usage(
+ "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'",
+ stored_class_loader_context_->c_str(),
+ class_loader_context_arg.c_str());
+ }
+ }
+ } else if (args.Exists(M::StoredClassLoaderContext)) {
+ Usage("Option --stored-class-loader-context should only be used if "
+ "--class-loader-context is also specified");
}
if (!ReadCompilerOptions(args, compiler_options_.get(), &error_msg)) {
@@ -1579,7 +1604,7 @@
if (class_loader_context_ == nullptr) {
// If no context was specified use the default one (which is an empty PathClassLoader).
- class_loader_context_ = std::unique_ptr<ClassLoaderContext>(ClassLoaderContext::Default());
+ class_loader_context_ = ClassLoaderContext::Default();
}
DCHECK_EQ(oat_writers_.size(), 1u);
@@ -1605,8 +1630,15 @@
}
// Store the class loader context in the oat header.
- key_value_store_->Put(OatHeader::kClassPathKey,
- class_loader_context_->EncodeContextForOatFile(classpath_dir_));
+ // TODO: deprecate this since store_class_loader_context should be enough to cover the users
+ // of classpath_dir as well.
+ std::string class_path_key;
+ if (stored_class_loader_context_ != nullptr) {
+ class_path_key = *stored_class_loader_context_;
+ } else {
+ class_path_key = class_loader_context_->EncodeContextForOatFile(classpath_dir_);
+ }
+ key_value_store_->Put(OatHeader::kClassPathKey, class_path_key);
}
// Now that we have finalized key_value_store_, start writing the oat file.
@@ -2855,6 +2887,9 @@
// The spec describing how the class loader should be setup for compilation.
std::unique_ptr<ClassLoaderContext> class_loader_context_;
+ // The class loader context stored in the oat file. May be equal to class_loader_context_.
+ std::unique_ptr<std::string> stored_class_loader_context_;
+
size_t thread_count_;
uint64_t start_ns_;
uint64_t start_cputime_ns_;
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index 0d68f4f..5843691 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -245,6 +245,9 @@
.Define("--class-loader-context=_")
.WithType<std::string>()
.IntoKey(M::ClassLoaderContext)
+ .Define("--stored-class-loader-context=_")
+ .WithType<std::string>()
+ .IntoKey(M::StoredClassLoaderContext)
.Define("--compact-dex-level=_")
.WithType<CompactDexLevel>()
.WithValueMap({{"none", CompactDexLevel::kCompactDexLevelNone},
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index 01f9d94..1a913a9 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -88,6 +88,7 @@
DEX2OAT_OPTIONS_KEY (Unit, ForceDeterminism)
DEX2OAT_OPTIONS_KEY (std::string, ClasspathDir)
DEX2OAT_OPTIONS_KEY (std::string, ClassLoaderContext)
+DEX2OAT_OPTIONS_KEY (std::string, StoredClassLoaderContext)
DEX2OAT_OPTIONS_KEY (std::string, DirtyImageObjects)
DEX2OAT_OPTIONS_KEY (std::vector<std::string>, RuntimeOptions)
DEX2OAT_OPTIONS_KEY (std::string, CompilationReason)
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index c890f8be..710a6af 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -2125,4 +2125,33 @@
EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtFields).Size(), 0u);
}
+TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) {
+ const std::string out_dir = GetScratchDir();
+ const std::string odex_location = out_dir + "/base.odex";
+ const std::string valid_context = "PCL[" + GetUsedDexLocation() + "]";
+ const std::string stored_context = "PCL[/system/not_real_lib.jar]";
+ // The class path should not be valid and should fail being stored.
+ GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
+ odex_location,
+ CompilerFilter::Filter::kQuicken,
+ { "--class-loader-context=" + stored_context },
+ true, // expect_success
+ false, // use_fd
+ [&](const OatFile& oat_file) {
+ EXPECT_NE(oat_file.GetClassLoaderContext(), stored_context);
+ EXPECT_NE(oat_file.GetClassLoaderContext(), valid_context);
+ });
+ // The stored context should match what we expect even though it's invalid.
+ GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
+ odex_location,
+ CompilerFilter::Filter::kQuicken,
+ { "--class-loader-context=" + valid_context,
+ "--stored-class-loader-context=" + stored_context },
+ true, // expect_success
+ false, // use_fd
+ [&](const OatFile& oat_file) {
+ EXPECT_EQ(oat_file.GetClassLoaderContext(), stored_context);
+ });
+}
+
} // namespace art
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index e646520..216ad8f 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -649,12 +649,16 @@
return !location.empty() && location[0] == '/';
}
-bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const {
- DCHECK(dex_files_open_attempted_);
- DCHECK(dex_files_open_result_);
+bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec,
+ bool verify_names,
+ bool verify_checksums) const {
+ if (verify_names || verify_checksums) {
+ DCHECK(dex_files_open_attempted_);
+ DCHECK(dex_files_open_result_);
+ }
ClassLoaderContext expected_context;
- if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) {
+ if (!expected_context.Parse(context_spec, verify_checksums)) {
LOG(WARNING) << "Invalid class loader context: " << context_spec;
return false;
}
@@ -693,8 +697,14 @@
return false;
}
- DCHECK_EQ(info.classpath.size(), info.checksums.size());
- DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size());
+ if (verify_checksums) {
+ DCHECK_EQ(info.classpath.size(), info.checksums.size());
+ DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size());
+ }
+
+ if (!verify_names) {
+ continue;
+ }
for (size_t k = 0; k < info.classpath.size(); k++) {
// Compute the dex location that must be compared.
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 692a6cd..231acc4 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -104,7 +104,11 @@
// - 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;
+ // Names are only verified if verify_names is true.
+ // Checksums are only verified if verify_checksums is true.
+ bool VerifyClassLoaderContextMatch(const std::string& context_spec,
+ bool verify_names = true,
+ bool verify_checksums = true) const;
// Creates the class loader context from the given string.
// The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]...