#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include "llvm/ADT/STLExtras.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "revng/Support/Configuration.h" #include "revng/Support/Debug.h" #include "revng/Support/FileSystem.h" #include "revng/Support/LDDTree.h" template class LDDTreeObjectFileTraits; template class Queue { public: struct Entry { /// Identifier of the library to lookup std::string ToImport; /// Identifier of the library importing this library std::string ImporterIdentifier; /// The canonical path of the importer std::string ImporterCanonicalPath; /// Image format-specific information for resolve D Dependency; }; private: std::queue Queue; public: bool empty() const { return Queue.empty(); } public: void enqueue(std::vector &&Entries) { for (Entry &NewEntry : Entries) Queue.push(std::move(NewEntry)); } Entry pop() { Entry Result = Queue.front(); Queue.pop(); return Result; } }; template using ResolutionInfo = typename LDDTreeObjectFileTraits::ResolutionInfo; template using QueueEntry = typename Queue>::Entry; template::Entry> concept LDDTreeObjectFileTraitsConcept = requires(T Trait, ObjectFile Object, llvm::StringRef String, QueueEntry Entry, const revng::RootEntry &Root) { typename T::ResolutionInfo; { T::getExportedSymbols(Object) } -> std::same_as>>; { T::getDependencies(Root, String, String, Object) } -> std::same_as>; { T::computeSearchPaths(Root, Entry) } -> std::same_as>; { T::isValidObjectFor(Entry, Object) } -> std::same_as; }; template std::optional resolve(const revng::RootEntry &Root, QueueEntry &Entry) { using namespace llvm; using Traits = LDDTreeObjectFileTraits; revng_log(LDDTreeLog, "Looking for " << Entry.ToImport); LoggerIndent Indent(LDDTreeLog); auto &Dependency = Entry.Dependency; // TODO: computeSearchPaths could be a generator SmallVector SearchPaths = Traits::computeSearchPaths(Root, Entry); revng_assert(llvm::none_of(SearchPaths, [](const std::string &Path) { return Path.empty(); })); for (const auto &Path : SearchPaths) { llvm::SmallString<128> Candidate; llvm::sys::path::append(Candidate, Path, Entry.ToImport); auto MaybeFullPath = Root.getExistingPath(Candidate); if (not MaybeFullPath.has_value()) continue; // Parse the binary auto MaybeBinary = object::createBinary(*MaybeFullPath); if (auto Error = MaybeBinary.takeError()) { revng_log(LDDTreeLog, "Can't create binary: " << Error); llvm::consumeError(std::move(Error)); continue; } // Ensure it's an object of the right type using namespace object; auto *Binary = dyn_cast(MaybeBinary->getBinary()); if (Binary == nullptr) { revng_log(LDDTreeLog, "Found " << Candidate.str() << " but it's not the right type of object file."); continue; } if (not Traits::isValidObjectFor(Entry, *Binary)) { continue; } revng_log(LDDTreeLog, "Found: " << Candidate.str()); return LDDTree::DependencyFile(std::move(*MaybeBinary), std::move(*MaybeFullPath), Candidate.str().str()); } revng_log(LDDTreeLog, Entry.ToImport << " not found"); if (LDDTreeLog.isEnabled()) { LDDTreeLog << "List of searched paths:\n"; for (const auto &Path : SearchPaths) LDDTreeLog << " " << Path << "\n"; LDDTreeLog << DoLog; } return std::nullopt; } /// \p MissingSymbols if specified, the function will try to solve all the // symbols. /// If not specified, the function will try to identify all the libraries, /// recursively. template const LDDTree LDDTree::fromPath(const revng::RootEntry &Root, llvm::StringRef CanonicalPath, const T &Binary, std::optional Symbols) { revng_log(LDDTreeLog, "Computing dependencies of " << CanonicalPath); LoggerIndent Indent(LDDTreeLog); if (Symbols.has_value()) revng_log(LDDTreeLog, "We need to resolve " << Symbols->size() << " symbols"); using ObjectFileTraits = LDDTreeObjectFileTraits; static_assert(LDDTreeObjectFileTraitsConcept); LDDTree Result(&Root); bool SymbolsRequested = Symbols.has_value(); if (SymbolsRequested) Result.MissingSymbols = std::move(*Symbols); Queue Queue; std::set Visited; Queue.enqueue(ObjectFileTraits::getDependencies(Root, "", CanonicalPath, Binary)); while (not Queue.empty()) { auto Entry = Queue.pop(); revng_log(LDDTreeLog, "Considering dependency " << Entry.ToImport); LoggerIndent Indent(LDDTreeLog); if (Visited.contains(Entry.ToImport) or Result.Dependencies.contains(Entry.ToImport)) { revng_log(LDDTreeLog, "Skipping, we already have this library."); continue; } Visited.insert(Entry.ToImport); // Resolve the path of the entry in the queue auto MaybeDependencyFile = resolve(Root, Entry); if (not MaybeDependencyFile.has_value()) { revng_log(LDDTreeLog, "Couldn't resolve the dependency."); continue; } revng_log(LDDTreeLog, "Found at " << MaybeDependencyFile->fullPathForExternalTools()); auto *Binary = llvm::dyn_cast(&MaybeDependencyFile->objectFile()); if (Binary == nullptr) { revng_log(LDDTreeLog, "We found a binary, but it is not of the expected type"); continue; } LDDTree::SymbolAddressMap ProvidedSymbols; // Purge from MissingSymbols those exported if (SymbolsRequested) { for (auto &[SymbolName, SymbolAddress] : ObjectFileTraits::getExportedSymbols(*Binary)) { if (Result.MissingSymbols->contains(SymbolName)) { Result.MissingSymbols->erase(SymbolName); ProvidedSymbols[SymbolName] = SymbolAddress; } } } if (not SymbolsRequested or ProvidedSymbols.size() > 0) { revng_log(LDDTreeLog, "Registering the library as a dependency"); if (SymbolsRequested) { if (LDDTreeLog.isEnabled()) { LDDTreeLog << "The library provides " << ProvidedSymbols.size() << " symbols:\n"; for (auto &Symbol : ProvidedSymbols) { LDDTreeLog << " " << Symbol.first << "\n"; } LDDTreeLog << DoLog; } } // We fulfilled at least a symbol, record it as a dependency LDDTree::Dependency NewDependency(std::move(*MaybeDependencyFile), std::move(ProvidedSymbols), Entry.ImporterIdentifier); Result.Dependencies.emplace(Entry.ToImport, std::move(NewDependency)); } else { revng_log(LDDTreeLog, "Ignoring this library"); } // Check if completed the quest. If so, no need to proceed any further! if (SymbolsRequested and Result.MissingSymbols->size() == 0) { revng_log(LDDTreeLog, "We found all the symbols!"); return Result; } // Enqueue new entries, once for each dependency revng_log(LDDTreeLog, "Collecting dependencies"); LoggerIndent Indent2(LDDTreeLog); auto DependencyCanonicalPath = MaybeDependencyFile->canonicalPath(); Queue.enqueue(ObjectFileTraits::getDependencies(Root, Entry.ToImport, DependencyCanonicalPath, *Binary)); } return Result; }