// Utility method to add parent and shared libraries of `info` into // the `work_list`. staticvoid AddToWorkList(ClassLoaderContext::ClassLoaderInfo* info,
std::vector<ClassLoaderContext::ClassLoaderInfo*>& work_list) { if (info->parent != nullptr) {
work_list.push_back(info->parent.get());
} for (size_t i = 0; i < info->shared_libraries.size(); ++i) {
work_list.push_back(info->shared_libraries[i].get());
} for (size_t i = 0; i < info->shared_libraries_after.size(); ++i) {
work_list.push_back(info->shared_libraries_after[i].get());
}
}
ClassLoaderContext::~ClassLoaderContext() { if (!owns_the_dex_files_ && class_loader_chain_ != nullptr) { // If the context does not own the dex/oat files release the unique pointers to // make sure we do not de-allocate them.
std::vector<ClassLoaderInfo*> work_list;
work_list.push_back(class_loader_chain_.get()); while (!work_list.empty()) {
ClassLoaderInfo* info = work_list.back();
work_list.pop_back(); for (std::unique_ptr<OatFile>& oat_file : info->opened_oat_files) {
oat_file.release(); // NOLINT b/117926937
} for (std::unique_ptr<const DexFile>& dex_file : info->opened_dex_files) {
dex_file.release(); // NOLINT b/117926937
}
AddToWorkList(info, work_list);
}
}
}
static size_t FindMatchingSharedLibraryCloseMarker(const std::string& spec,
size_t shared_library_open_index) { // Counter of opened shared library marker we've encountered so far.
uint32_t counter = 1; // The index at which we're operating in the loop.
uint32_t string_index = shared_library_open_index + 1;
size_t shared_library_close = std::string::npos; while (counter != 0) {
shared_library_close = spec.find_first_of(kClassLoaderSharedLibraryClosingMark, string_index);
size_t shared_library_open =
spec.find_first_of(kClassLoaderSharedLibraryOpeningMark, string_index); if (shared_library_close == std::string::npos) { // No matching closing marker. Return an error. break;
}
if ((shared_library_open == std::string::npos) ||
(shared_library_close < shared_library_open)) { // We have seen a closing marker. Decrement the counter.
--counter; // Move the search index forward.
string_index = shared_library_close + 1;
} else { // New nested opening marker. Increment the counter and move the search // index after the marker.
++counter;
string_index = shared_library_open + 1;
}
} return shared_library_close;
}
// The expected format is: // "ClassLoaderType1[ClasspathElem1*Checksum1:ClasspathElem2*Checksum2...]{ClassLoaderType2[...]}". // The checksum part of the format is expected only if parse_cheksums is true.
std::unique_ptr<ClassLoaderContext::ClassLoaderInfo> ClassLoaderContext::ParseClassLoaderSpec( const std::string& class_loader_spec, bool parse_checksums) {
ClassLoaderType class_loader_type = ExtractClassLoaderType(class_loader_spec); if (class_loader_type == kInvalidClassLoader) { return nullptr;
}
// InMemoryDexClassLoader's dex location is always bogus. Special-case it. if (class_loader_type == kInMemoryDexClassLoader) { if (parse_checksums) { // Make sure that OpenDexFiles() will never be attempted on this context // because the dex locations of IMC do not correspond to real files.
CHECK(dex_files_state_ == kDexFilesNotOpened || dex_files_state_ == kDexFilesOpenFailed)
<< "Parsing spec not supported when context created from a ClassLoader object: "
<< "dex_files_state_=" << dex_files_state_;
dex_files_state_ = kDexFilesOpenFailed;
} else { // Checksums are not provided and dex locations themselves have no meaning // (although we keep them in the spec to simplify parsing). Treat this as // an unknown class loader. // We can hit this case if dex2oat is invoked with a spec containing IMC. // Because the dex file data is only available at runtime, we cannot proceed. return nullptr;
}
}
// At this point we know the format is ok; continue and extract the classpath. // Note that class loaders with an empty class path are allowed.
std::string classpath =
class_loader_spec.substr(type_str_size + 1, closing_index - type_str_size - 1);
// Extracts the class loader type from the given spec. // Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not // recognized.
ClassLoaderContext::ClassLoaderType ClassLoaderContext::ExtractClassLoaderType( const std::string& class_loader_spec) { const ClassLoaderType kValidTypes[] = {
kPathClassLoader, kDelegateLastClassLoader, kInMemoryDexClassLoader}; for (const ClassLoaderType& type : kValidTypes) { constchar* type_str = GetClassLoaderTypeName(type); if (class_loader_spec.compare(0, strlen(type_str), type_str) == 0) { return type;
}
} return kInvalidClassLoader;
}
// 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 parse_checksums) { if (spec.empty()) { // By default we load the dex files in a PathClassLoader. // So an empty spec is equivalent to an empty PathClassLoader (this happens when running // tests)
class_loader_chain_.reset(new ClassLoaderInfo(kPathClassLoader)); returntrue;
}
std::unique_ptr<ClassLoaderInfo> info =
ParseClassLoaderSpec(class_loader_spec, parse_checksums); if (info == nullptr) {
LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec; return nullptr;
} if (first == nullptr) {
first = std::move(info);
previous_iteration = first.get();
} else {
CHECK(previous_iteration != nullptr);
previous_iteration->parent = std::move(info);
previous_iteration = previous_iteration->parent.get();
}
} return first.release();
}
// 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(const std::string& classpath_dir, const std::vector<int>& fds, bool only_read_checksums) { switch (dex_files_state_) { case kDexFilesNotOpened: break; // files not opened, continue. case kDexFilesOpenFailed: returnfalse; // previous attempt failed. case kDexFilesOpened: returntrue; // previous attempt succeed. case kDexFilesChecksumsRead: if (only_read_checksums) { returntrue; // we already read the checksums.
} else { break; // we already read the checksums but have to open the dex files; continue.
}
}
// Assume we can open the files. If not, we will adjust as we go.
dex_files_state_ = only_read_checksums ? kDexFilesChecksumsRead : kDexFilesOpened;
// Note that we try to open all dex files even if some fail. // We may get resource-only apks which we cannot load. // 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...
std::vector<ClassLoaderInfo*> work_list; if (class_loader_chain_ == nullptr) { returntrue;
}
work_list.push_back(class_loader_chain_.get());
size_t dex_file_index = 0; while (!work_list.empty()) {
ClassLoaderInfo* info = work_list.back();
work_list.pop_back();
DCHECK(info->type != kInMemoryDexClassLoader) << __FUNCTION__ << " not supported for IMC";
// Holds the dex locations for the classpath files we've opened.
std::vector<std::string> dex_locations; // Holds the checksums for the classpath files we've opened.
std::vector<uint32_t> dex_checksums;
for (const std::string& cp_elem : info->classpath) { // If path is relative, append it to the provided base directory.
std::string location = cp_elem; if (location[0] != '/' && !classpath_dir.empty()) {
location =
ART_FORMAT("{}{}{}", classpath_dir, classpath_dir.back() == '/' ? "" : "/", location);
}
// If file descriptors were provided for the class loader context dex paths, // get the descriptor which corresponds to this dex path. We assume the `fds` // vector follows the same order as a flattened class loader context.
File file; if (!fds.empty()) { if (dex_file_index >= fds.size()) {
LOG(WARNING) << "Number of FDs is smaller than number of dex files in the context";
dex_files_state_ = kDexFilesOpenFailed; returnfalse;
}
std::string error_msg;
std::optional<uint32_t> dex_checksum; if (only_read_checksums) { bool zip_file_only_contains_uncompress_dex;
ArtDexFileLoader dex_file_loader(&file, location); if (!dex_file_loader.GetMultiDexChecksum(
&dex_checksum, &error_msg, &zip_file_only_contains_uncompress_dex)) {
LOG(WARNING) << "Could not get dex checksums for location " << location
<< ", fd=" << file.Fd();
dex_files_state_ = kDexFilesOpenFailed;
}
file.Release(); // Don't close the file yet (we have only read the checksum).
} else { // When opening the dex files from the context we expect their checksum to match their // contents. So pass true to verify_checksum. // We don't need to do structural dex file verification, we only need to // check the checksum, so pass false to verify.
size_t i = info->opened_dex_files.size();
ArtDexFileLoader dex_file_loader(&file, location); if (!dex_file_loader.Open(/*verify=*/false, /*verify_checksum=*/true,
&error_msg,
&info->opened_dex_files)) {
LOG(WARNING) << "Could not open dex files for location " << location
<< ", fd=" << file.Fd();
dex_files_state_ = kDexFilesOpenFailed;
} else {
dex_checksum = DexFileLoader::GetMultiDexChecksum(info->opened_dex_files, &i);
DCHECK_EQ(i, info->opened_dex_files.size());
}
} if (dex_checksum.has_value()) {
dex_locations.push_back(location);
dex_checksums.push_back(dex_checksum.value());
}
}
// 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->original_classpath = std::move(info->classpath);
DCHECK(dex_locations.size() == dex_checksums.size());
info->classpath = std::move(dex_locations);
info->checksums = std::move(dex_checksums);
AddToWorkList(info, work_list);
}
// Check that if file descriptors were provided, there were exactly as many // as we have encountered while iterating over this class loader context. if (dex_file_index != fds.size()) {
LOG(WARNING) << fds.size() << " FDs provided but only " << dex_file_index
<< " dex files are in the class loader context";
dex_files_state_ = kDexFilesOpenFailed;
}
return dex_files_state_ != kDexFilesOpenFailed;
}
bool ClassLoaderContext::RemoveLocationsFromClassPaths( const dchecked_vector<std::string>& locations) {
CHECK_EQ(dex_files_state_, kDexFilesNotOpened)
<< "RemoveLocationsFromClasspaths cannot be call after OpenDexFiles";
if (class_loader_chain_ == nullptr) { returnfalse;
}
std::set<std::string> seen_locations; for (const std::string& path : class_loader_chain_->classpath) { // The classpath will contain multiple entries for multidex files, so make sure this is the // first time we're seeing this file. const std::string base_location(DexFileLoader::GetBaseLocation(path)); if (!seen_locations.insert(base_location).second) { continue;
}
if (stored_context != nullptr) {
DCHECK_EQ(GetParentChainSize(), stored_context->GetParentChainSize());
}
std::ostringstream out; if (class_loader_chain_ == nullptr) { // We can get in this situation if the context was created with a class path containing the // source dex files which were later removed (happens during run-tests).
out << GetClassLoaderTypeName(kPathClassLoader) << kClassLoaderOpeningMark
<< kClassLoaderClosingMark; return out.str();
}
void ClassLoaderContext::EncodeClassPath(const std::string& base_dir, const std::vector<std::string>& dex_locations, const std::vector<uint32_t>& checksums,
ClassLoaderType type,
std::ostringstream& out) const {
CHECK(checksums.empty() || dex_locations.size() == checksums.size());
out << GetClassLoaderTypeName(type);
out << kClassLoaderOpeningMark; const size_t len = dex_locations.size(); for (size_t k = 0; k < len; k++) { const std::string& location = dex_locations[k]; if (k > 0) {
out << kClasspathSeparator;
} if (type == kInMemoryDexClassLoader) {
out << kInMemoryDexClassLoaderDexLocationMagic;
} elseif (!base_dir.empty() && location.substr(0, base_dir.length()) == base_dir) { // Find paths that were relative and convert them back from absolute.
out << location.substr(base_dir.length() + 1).c_str();
} else {
out << location.c_str();
} if (!checksums.empty()) {
out << kDexFileChecksumSeparator;
out << checksums[k];
}
}
out << kClassLoaderClosingMark;
}
void ClassLoaderContext::EncodeContextInternal(const ClassLoaderInfo& info, const std::string& base_dir, bool for_dex2oat,
ClassLoaderInfo* stored_info,
std::ostringstream& out) const {
std::vector<std::string> locations;
std::vector<uint32_t> checksums;
std::set<std::string> seen_locations;
SafeMap<std::string, std::string> remap; if (stored_info != nullptr) { for (size_t k = 0; k < info.original_classpath.size(); ++k) { // Note that we don't care if the same name appears twice.
remap.Overwrite(info.original_classpath[k], stored_info->classpath[k]);
}
}
DCHECK_EQ(info.classpath.size(), info.checksums.size()); for (size_t i = 0; i < info.classpath.size(); i++) { if (for_dex2oat) { // De-duplicate locations. bool new_insert = seen_locations.insert(info.classpath[i]).second; if (!new_insert) { continue;
}
}
std::string location = info.classpath[i]; // If there is a stored class loader remap, fix up the multidex strings. if (!remap.empty()) { auto it = remap.find(location);
CHECK(it != remap.end()) << location;
location = it->second;
}
locations.emplace_back(std::move(location));
// dex2oat does not need the checksums. if (!for_dex2oat) {
checksums.push_back(info.checksums[i]);
}
}
EncodeClassPath(base_dir, locations, checksums, info.type, out);
EncodeSharedLibAndParent(info, base_dir, for_dex2oat, stored_info, out);
}
void ClassLoaderContext::EncodeSharedLibAndParent(const ClassLoaderInfo& info, const std::string& base_dir, bool for_dex2oat,
ClassLoaderInfo* stored_info,
std::ostringstream& out) const { if (!info.shared_libraries.empty() || !info.shared_libraries_after.empty()) {
out << kClassLoaderSharedLibraryOpeningMark; for (uint32_t i = 0; i < info.shared_libraries.size(); ++i) { if (i > 0) {
out << kClassLoaderSharedLibrarySeparator;
}
EncodeContextInternal(
*info.shared_libraries[i].get(),
base_dir,
for_dex2oat,
(stored_info == nullptr ? nullptr : stored_info->shared_libraries[i].get()),
out);
}
for (uint32_t i = 0; i < info.shared_libraries_after.size(); ++i) { if (i > 0 || !info.shared_libraries.empty()) {
out << kClassLoaderSharedLibrarySeparator;
}
out << kClassLoaderSharedLibraryAfterSeparator;
EncodeContextInternal(
*info.shared_libraries_after[i].get(),
base_dir,
for_dex2oat,
(stored_info == nullptr ? nullptr : stored_info->shared_libraries_after[i].get()),
out);
}
out << kClassLoaderSharedLibraryClosingMark;
} if (info.parent != nullptr) {
out << kClassLoaderSeparator;
EncodeContextInternal(*info.parent.get(),
base_dir,
for_dex2oat,
(stored_info == nullptr ? nullptr : stored_info->parent.get()),
out);
}
}
// Returns the WellKnownClass for the given class loader type. static ObjPtr<mirror::Class> GetClassLoaderClass(ClassLoaderContext::ClassLoaderType type)
REQUIRES_SHARED(Locks::mutator_lock_) { switch (type) { case ClassLoaderContext::kPathClassLoader: return WellKnownClasses::dalvik_system_PathClassLoader.Get(); case ClassLoaderContext::kDelegateLastClassLoader: return WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(); case ClassLoaderContext::kInMemoryDexClassLoader: return WellKnownClasses::dalvik_system_InMemoryDexClassLoader.Get(); case ClassLoaderContext::kInvalidClassLoader: break; // will fail after the switch.
}
LOG(FATAL) << "Invalid class loader type " << type;
UNREACHABLE();
}
if (!info.shared_libraries.empty()) {
libraries.Assign(mirror::ObjectArray<mirror::ClassLoader>::Alloc(
self,
GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(),
info.shared_libraries.size())); for (uint32_t i = 0; i < info.shared_libraries.size(); ++i) { // We should only add the compilation sources to the first class loader.
libraries->Set(i,
CreateClassLoaderInternal(self,
soa,
*info.shared_libraries[i].get(), /* for_shared_library= */ true,
map_scope,
canonicalized_libraries, /* add_compilation_sources= */ false,
compilation_sources));
}
}
if (!info.shared_libraries_after.empty()) {
libraries_after.Assign(mirror::ObjectArray<mirror::ClassLoader>::Alloc(
self,
GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(),
info.shared_libraries_after.size())); for (uint32_t i = 0; i < info.shared_libraries_after.size(); ++i) { // We should only add the compilation sources to the first class loader.
libraries_after->Set(i,
CreateClassLoaderInternal(self,
soa,
*info.shared_libraries_after[i].get(), /* for_shared_library= */ true,
map_scope,
canonicalized_libraries, /* add_compilation_sources= */ false,
compilation_sources));
}
}
MutableHandle<mirror::ClassLoader> parent = hs.NewHandle<mirror::ClassLoader>(nullptr); if (info.parent != nullptr) { // We should only add the compilation sources to the first class loader.
parent.Assign(CreateClassLoaderInternal(self,
soa,
*info.parent.get(), /* for_shared_library= */ false,
map_scope,
canonicalized_libraries, /* add_compilation_sources= */ false,
compilation_sources));
}
std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(info.opened_dex_files); if (add_compilation_sources) { // For the first class loader, its classpath comes first, followed by compilation sources. // This ensures that whenever we need to resolve classes from it the classpath elements // come first.
class_path_files.insert(
class_path_files.end(), compilation_sources.begin(), compilation_sources.end());
}
Handle<mirror::Class> loader_class = hs.NewHandle<mirror::Class>(GetClassLoaderClass(info.type));
ObjPtr<mirror::ClassLoader> loader =
Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
self, class_path_files, loader_class, parent, libraries, libraries_after); if (for_shared_library) {
canonicalized_libraries[FlattenClasspath(info.classpath)] =
map_scope.NewHandle<mirror::ClassLoader>(loader);
} return loader;
}
// Create a map of canonicalized shared libraries. As we're holding objects, // we're creating a variable size handle scope to put handles in the map.
VariableSizedHandleScope map_scope(self);
std::map<std::string, Handle<mirror::ClassLoader>> canonicalized_libraries;
// Create the class loader.
ObjPtr<mirror::ClassLoader> loader =
CreateClassLoaderInternal(self,
soa,
*class_loader_chain_.get(), /* for_shared_library= */ false,
map_scope,
canonicalized_libraries, /* add_compilation_sources= */ true,
compilation_sources); // Make it a global ref and return.
ScopedLocalRef<jobject> local_ref(soa.Env(), soa.Env()->AddLocalReference<jobject>(loader)); return soa.Env()->NewGlobalRef(local_ref.get());
}
if (class_loader_chain_ == nullptr) { return result;
}
std::vector<ClassLoaderInfo*> work_list;
work_list.push_back(class_loader_chain_.get()); while (!work_list.empty()) {
ClassLoaderInfo* info = work_list.back();
work_list.pop_back(); for (const std::string& dex_path : info->classpath) {
result.push_back(dex_path);
}
AddToWorkList(info, work_list);
} return result;
}
constchar* ClassLoaderContext::GetClassLoaderTypeName(ClassLoaderType type) { switch (type) { case kPathClassLoader: return kPathClassLoaderString; case kDelegateLastClassLoader: return kDelegateLastClassLoaderString; case kInMemoryDexClassLoader: return kInMemoryDexClassLoaderString; default:
LOG(FATAL) << "Invalid class loader type " << type;
UNREACHABLE();
}
}
void ClassLoaderContext::CheckDexFilesOpened(const std::string& calling_method) const {
CHECK_NE(dex_files_state_, kDexFilesNotOpened)
<< "Dex files were not successfully opened before the call to " << calling_method
<< "status=" << dex_files_state_;
}
// Collects the dex files from the give Java dex_file object. Only the dex files with // at least 1 class are collected. If a null java_dex_file is passed this method does nothing. staticbool CollectDexFilesFromJavaDexFile(ObjPtr<mirror::Object> java_dex_file,
ArtField* const cookie_field,
std::vector<const DexFile*>* out_dex_files)
REQUIRES_SHARED(Locks::mutator_lock_) { if (java_dex_file == nullptr) { returntrue;
} // On the Java side, the dex files are stored in the cookie field.
ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(java_dex_file)->AsLongArray(); if (long_array == nullptr) { // This should never happen so log a warning.
LOG(ERROR) << "Unexpected null cookie"; returnfalse;
}
int32_t long_array_size = long_array->GetLength(); // Index 0 from the long array stores the oat file. The dex files start at index 1. for (int32_t j = 1; j < long_array_size; ++j) { const DexFile* cp_dex_file =
reinterpret_cast64<const DexFile*>(long_array->GetWithoutChecks(j)); if (cp_dex_file != nullptr && cp_dex_file->NumClassDefs() > 0) { // TODO(calin): It's unclear why the dex files with no classes are skipped here and when // cp_dex_file can be null.
out_dex_files->push_back(cp_dex_file);
}
} returntrue;
}
// Collects all the dex files loaded by the given class loader. // Returns true for success or false if an unexpected state is discovered (e.g. a null dex cookie, // a null list of dex elements or a null dex element). staticbool CollectDexFilesFromSupportedClassLoader(Thread* self,
Handle<mirror::ClassLoader> class_loader,
std::vector<const DexFile*>* out_dex_files)
REQUIRES_SHARED(Locks::mutator_lock_) {
CHECK(IsInstanceOfBaseDexClassLoader(class_loader));
// All supported class loaders inherit from BaseDexClassLoader. // We need to get the DexPathList and loop through it.
ArtField* const cookie_field = WellKnownClasses::dalvik_system_DexFile_cookie;
ArtField* const dex_file_field = WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
ObjPtr<mirror::Object> dex_path_list =
WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList->GetObject(class_loader.Get());
CHECK(cookie_field != nullptr);
CHECK(dex_file_field != nullptr); if (dex_path_list == nullptr) { // This may be null if the current class loader is under construction and it does not // have its fields setup yet. returntrue;
} // DexPathList has an array dexElements of Elements[] which each contain a dex file.
ObjPtr<mirror::Object> dex_elements_obj =
WellKnownClasses::dalvik_system_DexPathList_dexElements->GetObject(dex_path_list); // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look // at the mCookie which is a DexFile vector. if (dex_elements_obj == nullptr) { // TODO(calin): It's unclear if we should just assert here. For now be prepared for the worse // and assume we have no elements. returntrue;
} else {
StackHandleScope<1> hs(self);
Handle<mirror::ObjectArray<mirror::Object>> dex_elements(
hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>())); for (auto element : dex_elements.Iterate<mirror::Object>()) { if (element == nullptr) { // Should never happen, log an error and break. // TODO(calin): It's unclear if we should just assert here. // This code was propagated to oat_file_manager from the class linker where it would // throw a NPE. For now, return false which will mark this class loader as unsupported.
LOG(ERROR) << "Unexpected null in the dex element list"; returnfalse;
}
ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element); if (!CollectDexFilesFromJavaDexFile(dex_file, cookie_field, out_dex_files)) { returnfalse;
}
}
}
for (auto element : dex_elements.Iterate<mirror::Object>()) { // We can hit a null element here because this is invoked with a partially filled dex_elements // array from DexPathList. DexPathList will open each dex sequentially, each time passing the // list of dex files which were opened before. if (element == nullptr) { continue;
}
// We support this being dalvik.system.DexPathList$Element and dalvik.system.DexFile. // TODO(calin): Code caried over oat_file_manager: supporting both classes seem to be // a historical glitch. All the java code opens dex files using an array of Elements.
ObjPtr<mirror::Object> dex_file; if (element_class == element->GetClass()) {
dex_file = dex_file_field->GetObject(element);
} elseif (dexfile_class == element->GetClass()) {
dex_file = element;
} else {
LOG(ERROR) << "Unsupported element in dex_elements: "
<< mirror::Class::PrettyClass(element->GetClass()); returnfalse;
}
if (!CollectDexFilesFromJavaDexFile(dex_file, cookie_field, out_dex_files)) { returnfalse;
}
} returntrue;
}
// Adds the `class_loader` info to the `context`. // The dex file present in `dex_elements` array (if not null) will be added at the end of // the classpath. // This method is recursive (w.r.t. the class loader parent) and will stop once it reaches the // BootClassLoader. Note that the class loader chain is expected to be short. bool ClassLoaderContext::CreateInfoFromClassLoader(
ScopedObjectAccessAlreadyRunnable& soa,
Handle<mirror::ClassLoader> class_loader,
Handle<mirror::ObjectArray<mirror::Object>> dex_elements,
ClassLoaderInfo* child_info, bool is_shared_library, bool is_after) REQUIRES_SHARED(Locks::mutator_lock_) { if (ClassLinker::IsBootClassLoader(class_loader.Get())) { // Nothing to do for the boot class loader as we don't add its dex files to the context. returntrue;
}
ClassLoaderContext::ClassLoaderType type; if (IsPathOrDexClassLoader(class_loader)) {
type = kPathClassLoader;
} elseif (IsDelegateLastClassLoader(class_loader)) {
type = kDelegateLastClassLoader;
} elseif (IsInMemoryDexClassLoader(class_loader)) {
type = kInMemoryDexClassLoader;
} else {
LOG(WARNING) << "Unsupported class loader: "
<< mirror::Class::PrettyClass(class_loader->GetClass()); returnfalse;
}
// Inspect the class loader for its dex files.
std::vector<const DexFile*> dex_files_loaded;
CollectDexFilesFromSupportedClassLoader(soa.Self(), class_loader, &dex_files_loaded);
// If we have a dex_elements array extract its dex elements now. // This is used in two situations: // 1) when a new ClassLoader is created DexPathList will open each dex file sequentially // passing the list of already open dex files each time. This ensures that we see the // correct context even if the ClassLoader under construction is not fully build. // 2) when apk splits are loaded on the fly, the framework will load their dex files by // appending them to the current class loader. When the new code paths are loaded in // BaseDexClassLoader, the paths already present in the class loader will be passed // in the dex_elements array. if (dex_elements != nullptr) {
GetDexFilesFromDexElementsArray(dex_elements, &dex_files_loaded);
}
ClassLoaderInfo* info = new ClassLoaderContext::ClassLoaderInfo(type); // Attach the `ClassLoaderInfo` now, before populating dex files, as only the // `ClassLoaderContext` knows whether these dex files should be deleted or not. if (child_info == nullptr) {
class_loader_chain_.reset(info);
} elseif (is_shared_library) { if (is_after) {
child_info->shared_libraries_after.push_back(std::unique_ptr<ClassLoaderInfo>(info));
} else {
child_info->shared_libraries.push_back(std::unique_ptr<ClassLoaderInfo>(info));
}
} else {
child_info->parent.reset(info);
}
// Now that `info` is in the chain, populate dex files. for (size_t i = 0; i < dex_files_loaded.size();) { const DexFile* dex_file = dex_files_loaded[i];
uint32_t checksum = DexFileLoader::GetMultiDexChecksum(dex_files_loaded, &i); // Dex location of dex files loaded with InMemoryDexClassLoader is always bogus. // Use a magic value for the classpath instead.
info->classpath.push_back((type == kInMemoryDexClassLoader) ?
kInMemoryDexClassLoaderDexLocationMagic :
dex_file->GetLocation());
info->checksums.push_back(checksum);
} for (auto* dex_file : dex_files_loaded) {
info->opened_dex_files.emplace_back(dex_file);
}
// Note that dex_elements array is null here. The elements are considered to be part of the // current class loader and are not passed to the parents.
ScopedNullHandle<mirror::ObjectArray<mirror::Object>> null_dex_elements;
// We created the ClassLoaderInfo for the current loader. Move on to its parent.
Handle<mirror::ClassLoader> parent = hs.NewHandle(class_loader->GetParent()); if (!CreateInfoFromClassLoader(soa,
parent,
null_dex_elements,
info, /*is_shared_library=*/false, /*is_after=*/false)) { returnfalse;
} returntrue;
}
// Returns true if absolute `path` ends with relative `suffix` starting at // a directory name boundary, i.e. after a '/'. For example, "foo/bar" // is a valid suffix of "/data/foo/bar" but not "/data-foo/bar". staticinlinebool AbsolutePathHasRelativeSuffix(const std::string& path, const std::string& suffix) {
DCHECK(IsAbsoluteLocation(path));
DCHECK(!IsAbsoluteLocation(suffix)); return (path.size() > suffix.size()) && (path[path.size() - suffix.size() - 1u] == '/') &&
(std::string_view(path).substr(/*pos*/ path.size() - suffix.size()) == suffix);
}
// Resolves symlinks and returns the canonicalized absolute path. Returns relative path as is. static std::string ResolveIfAbsolutePath(const std::string& path) { if (!IsAbsoluteLocation(path)) { return path;
}
auto [filename, index] = DexFileLoader::SplitMultiDexLocation(path);
std::string resolved_filename; if (!android::base::Realpath(std::string(filename), &resolved_filename)) {
PLOG(ERROR) << "Unable to resolve path '" << path << "'"; return path;
}
// Returns true if the given dex names are mathing, false otherwise. staticbool AreDexNameMatching(const std::string& actual_dex_name, const std::string& expected_dex_name) { // Compute the dex location that must be compared. // We shouldn't do a naive comparison `actual_dex_name == expected_dex_name` // because even if they refer to the same file, one could be encoded as a relative location // and the other as an absolute one. bool is_dex_name_absolute = IsAbsoluteLocation(actual_dex_name); bool is_expected_dex_name_absolute = IsAbsoluteLocation(expected_dex_name); bool dex_names_match = false;
std::string resolved_actual_dex_name = ResolveIfAbsolutePath(actual_dex_name);
std::string resolved_expected_dex_name = ResolveIfAbsolutePath(expected_dex_name);
if (is_dex_name_absolute == is_expected_dex_name_absolute) { // If both locations are absolute or relative then compare them as they are. // This is usually the case for: shared libraries and secondary dex files.
dex_names_match = (resolved_actual_dex_name == resolved_expected_dex_name);
} elseif (is_dex_name_absolute) { // The runtime name is absolute but the compiled name (the expected one) is relative. // This is the case for split apks which depend on base or on other splits.
dex_names_match =
AbsolutePathHasRelativeSuffix(resolved_actual_dex_name, resolved_expected_dex_name);
} elseif (is_expected_dex_name_absolute) { // The runtime name is relative but the compiled name is absolute. // There is no expected use case that would end up here as dex files are always loaded // with their absolute location. However, be tolerant and do the best effort (in case // there are unexpected new use case...).
dex_names_match =
AbsolutePathHasRelativeSuffix(resolved_expected_dex_name, resolved_actual_dex_name);
} else { // Both locations are relative. In this case there's not much we can be sure about // except that the names are the same. The checksum will ensure that the files are // are same. This should not happen outside testing and manual invocations.
dex_names_match = (resolved_actual_dex_name == resolved_expected_dex_name);
}
if (verify_checksums) {
DCHECK_EQ(info.classpath.size(), info.checksums.size());
DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size());
}
if (verify_names) { for (size_t k = 0; k < info.classpath.size(); k++) { bool dex_names_match = AreDexNameMatching(info.classpath[k], expected_info.classpath[k]);
// If the chain is null there's nothing we can check, return an empty list. // The class loader chain can be null if there were issues when creating the // class loader context (e.g. tests). if (class_loader_chain_ == nullptr) { return result;
}
// We only check the current Class Loader which the first one in the chain. // Cross class-loader duplicates may be a valid scenario (though unlikely // in the Android world) - and as such we decide not to warn on them.
ClassLoaderInfo* info = class_loader_chain_.get(); for (size_t k = 0; k < info->classpath.size(); k++) { for (size_t i = 0; i < dex_files_to_check.size();) { const DexFile* dex_file = dex_files_to_check[i];
uint32_t checksum = DexFileLoader::GetMultiDexChecksum(dex_files_to_check, &i); if (info->checksums[k] == checksum &&
AreDexNameMatching(info->classpath[k], dex_file->GetLocation())) {
result.insert(dex_file);
}
}
}
return result;
}
} // namespace art
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.44 Sekunden
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.