Move dex files dependencies (en/de)coding to ClassLoaderContext
Encode the full class loader context in the oat file (rather than just a
list of dex files).
The context encoding matches the format used by dex2oat with the addition
of checksums.
Temporarily assert that at decoding time we are operating on a
PathClassLoader until the checking logic covers all supported cases.
Also, bump the version of the oat file because the format of the classpath
key has changed.
This is a transition step to minimize the size of follow up changes.
Test: m test-art-host
Bug: 38138251
Change-Id: I9ec0cfe092ce1afccb741a36e737896880d5f1d2
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 5cbcd8f..2bed1d5 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -31,8 +31,9 @@
static constexpr char kDelegateLastClassLoaderString[] = "DLC";
static constexpr char kClassLoaderOpeningMark = '[';
static constexpr char kClassLoaderClosingMark = ']';
-static constexpr char kClassLoaderSep = ';';
-static constexpr char kClasspathSep = ':';
+static constexpr char kClassLoaderSeparator = ';';
+static constexpr char kClasspathSeparator = ':';
+static constexpr char kDexFileChecksumSeparator = '*';
ClassLoaderContext::ClassLoaderContext()
: special_shared_library_(false),
@@ -48,9 +49,11 @@
}
}
-// The expected format is: "ClassLoaderType1[ClasspathElem1:ClasspathElem2...]".
+// The expected format is: "ClassLoaderType1[ClasspathElem1*Checksum1:ClasspathElem2*Checksum2...]".
+// The checksum part of the format is expected only if parse_cheksums is true.
bool ClassLoaderContext::ParseClassLoaderSpec(const std::string& class_loader_spec,
- ClassLoaderType class_loader_type) {
+ ClassLoaderType class_loader_type,
+ bool parse_checksums) {
const char* class_loader_type_str = GetClassLoaderTypeName(class_loader_type);
size_t type_str_size = strlen(class_loader_type_str);
@@ -70,7 +73,26 @@
class_loader_spec.length() - type_str_size - 2);
class_loader_chain_.push_back(ClassLoaderInfo(class_loader_type));
- Split(classpath, kClasspathSep, &class_loader_chain_.back().classpath);
+
+ if (!parse_checksums) {
+ Split(classpath, kClasspathSeparator, &class_loader_chain_.back().classpath);
+ } else {
+ std::vector<std::string> classpath_elements;
+ Split(classpath, kClasspathSeparator, &classpath_elements);
+ for (const std::string& element : classpath_elements) {
+ std::vector<std::string> dex_file_with_checksum;
+ Split(element, kDexFileChecksumSeparator, &dex_file_with_checksum);
+ if (dex_file_with_checksum.size() != 2) {
+ return false;
+ }
+ uint32_t checksum = 0;
+ if (!ParseInt(dex_file_with_checksum[1].c_str(), &checksum)) {
+ return false;
+ }
+ class_loader_chain_.back().classpath.push_back(dex_file_with_checksum[0]);
+ class_loader_chain_.back().checksums.push_back(checksum);
+ }
+ }
return true;
}
@@ -93,11 +115,11 @@
// The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]...
// ClassLoaderType is either "PCL" (PathClassLoader) or "DLC" (DelegateLastClassLoader).
// ClasspathElem is the path of dex/jar/apk file.
-bool ClassLoaderContext::Parse(const std::string& spec) {
+bool ClassLoaderContext::Parse(const std::string& spec, bool parse_checksums) {
if (spec.empty()) {
- LOG(ERROR) << "Empty string passed to Parse";
- return false;
+ return true;
}
+
// Stop early if we detect the special shared library, which may be passed as the classpath
// for dex2oat when we want to skip the shared libraries check.
if (spec == OatFile::kSpecialSharedLibrary) {
@@ -107,7 +129,7 @@
}
std::vector<std::string> class_loaders;
- Split(spec, kClassLoaderSep, &class_loaders);
+ Split(spec, kClassLoaderSeparator, &class_loaders);
for (const std::string& class_loader : class_loaders) {
ClassLoaderType type = ExtractClassLoaderType(class_loader);
@@ -115,7 +137,7 @@
LOG(ERROR) << "Invalid class loader type: " << class_loader;
return false;
}
- if (!ParseClassLoaderSpec(class_loader, type)) {
+ if (!ParseClassLoaderSpec(class_loader, type, parse_checksums)) {
LOG(ERROR) << "Invalid class loader spec: " << class_loader;
return false;
}
@@ -219,12 +241,33 @@
return "";
}
- // TODO(calin): Transition period: assume we only have a classloader until
- // the oat file assistant implements the full class loader check.
- CHECK_EQ(1u, class_loader_chain_.size());
+ std::ostringstream out;
- return OatFile::EncodeDexFileDependencies(MakeNonOwningPointerVector(
- class_loader_chain_[0].opened_dex_files), base_dir);
+ for (size_t i = 0; i < class_loader_chain_.size(); i++) {
+ const ClassLoaderInfo& info = class_loader_chain_[i];
+ if (i > 0) {
+ out << kClassLoaderSeparator;
+ }
+ out << GetClassLoaderTypeName(info.type);
+ out << kClassLoaderOpeningMark;
+ 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];
+ const std::string& location = dex_file->GetLocation();
+ if (k > 0) {
+ out << kClasspathSeparator;
+ }
+ // Find paths that were relative and convert them back from absolute.
+ if (!base_dir.empty() && location.substr(0, base_dir.length()) == base_dir) {
+ out << location.substr(base_dir.length() + 1).c_str();
+ } else {
+ out << dex_file->GetLocation().c_str();
+ }
+ out << kDexFileChecksumSeparator;
+ out << dex_file->GetLocationChecksum();
+ }
+ out << kClassLoaderClosingMark;
+ }
+ return out.str();
}
jobject ClassLoaderContext::CreateClassLoader(
@@ -281,5 +324,37 @@
<< "Dex files were not successfully opened before the call to " << calling_method
<< "attempt=" << dex_files_open_attempted_ << ", result=" << dex_files_open_result_;
}
+
+bool ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
+ const std::string& context_spec,
+ std::vector<std::string>* out_classpath,
+ std::vector<uint32_t>* out_checksums,
+ bool* out_is_special_shared_library) {
+ ClassLoaderContext context;
+ if (!context.Parse(context_spec, /*parse_checksums*/ true)) {
+ LOG(ERROR) << "Invalid class loader context: " << context_spec;
+ return false;
+ }
+
+ *out_is_special_shared_library = context.special_shared_library_;
+ if (context.special_shared_library_) {
+ return true;
+ }
+
+ if (context.class_loader_chain_.empty()) {
+ return true;
+ }
+
+ // TODO(calin): assert that we only have a PathClassLoader until the logic for
+ // checking the context covers all case.
+ CHECK_EQ(1u, context.class_loader_chain_.size());
+ const ClassLoaderInfo& info = context.class_loader_chain_[0];
+ CHECK_EQ(kPathClassLoader, info.type);
+ DCHECK_EQ(info.classpath.size(), info.checksums.size());
+
+ *out_classpath = info.classpath;
+ *out_checksums = info.checksums;
+ return true;
+}
} // namespace art