diff options
Diffstat (limited to 'llvm/lib/ExecutionEngine/Orc/TargetProcess')
4 files changed, 1536 insertions, 1 deletions
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt index 9275586..ca8192b 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt @@ -16,9 +16,11 @@ add_llvm_component_library(LLVMOrcTargetProcess    ExecutorSharedMemoryMapperService.cpp    DefaultHostBootstrapValues.cpp    ExecutorResolver.cpp +  LibraryResolver.cpp    JITLoaderGDB.cpp    JITLoaderPerf.cpp    JITLoaderVTune.cpp +  LibraryScanner.cpp    OrcRTBootstrap.cpp    RegisterEHFrames.cpp    SimpleExecutorDylibManager.cpp @@ -36,6 +38,8 @@ add_llvm_component_library(LLVMOrcTargetProcess    LINK_COMPONENTS    ${intel_jit_profiling} +  BinaryFormat +  Object    OrcShared    Support    TargetParser diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderPerf.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderPerf.cpp index 1a61d31..e609a7d 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderPerf.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderPerf.cpp @@ -55,7 +55,7 @@ struct PerfState {    std::unique_ptr<raw_fd_ostream> Dumpstream;    // perf mmap marker -  void *MarkerAddr = NULL; +  void *MarkerAddr = nullptr;  };  // prevent concurrent dumps from messing up the output file diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryResolver.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryResolver.cpp new file mode 100644 index 0000000..35da82a --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryResolver.cpp @@ -0,0 +1,370 @@ +//===- LibraryResolver.cpp - Library Resolution of Unresolved Symbols ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Library resolution impl for unresolved symbols +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h" + +#include "llvm/ADT/StringSet.h" + +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/ELF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Error.h" + +#include <mutex> +#include <thread> + +#define DEBUG_TYPE "orc-resolver" + +namespace llvm::orc { + +LibraryResolver::LibraryResolver(const LibraryResolver::Setup &S) +    : LibPathCache(S.Cache ? S.Cache : std::make_shared<LibraryPathCache>()), +      LibPathResolver(S.PResolver +                          ? S.PResolver +                          : std::make_shared<PathResolver>(LibPathCache)), +      ScanHelper(S.BasePaths, LibPathCache, LibPathResolver), +      FB(S.FilterBuilder), LibMgr(), +      ShouldScanCall(S.ShouldScanCall ? S.ShouldScanCall +                                      : [](StringRef) -> bool { return true; }), +      scanBatchSize(S.ScanBatchSize) { + +  if (ScanHelper.getAllUnits().empty()) { +    LLVM_DEBUG(dbgs() << "Warning: No base paths provided for scanning.\n"); +  } +} + +std::unique_ptr<LibraryResolutionDriver> +LibraryResolutionDriver::create(const LibraryResolver::Setup &S) { +  auto LR = std::make_unique<LibraryResolver>(S); +  return std::unique_ptr<LibraryResolutionDriver>( +      new LibraryResolutionDriver(std::move(LR))); +} + +void LibraryResolutionDriver::addScanPath(const std::string &Path, PathType K) { +  LR->ScanHelper.addBasePath(Path, K); +} + +bool LibraryResolutionDriver::markLibraryLoaded(StringRef Path) { +  auto Lib = LR->LibMgr.getLibrary(Path); +  if (!Lib) +    return false; + +  Lib->setState(LibraryManager::LibState::Loaded); + +  return true; +} + +bool LibraryResolutionDriver::markLibraryUnLoaded(StringRef Path) { +  auto Lib = LR->LibMgr.getLibrary(Path); +  if (!Lib) +    return false; + +  Lib->setState(LibraryManager::LibState::Unloaded); + +  return true; +} + +void LibraryResolutionDriver::resolveSymbols( +    std::vector<std::string> Syms, +    LibraryResolver::OnSearchComplete OnCompletion, +    const SearchConfig &Config) { +  LR->searchSymbolsInLibraries(Syms, std::move(OnCompletion), Config); +} + +static bool shouldIgnoreSymbol(const object::SymbolRef &Sym, +                               uint32_t IgnoreFlags) { +  Expected<uint32_t> FlagsOrErr = Sym.getFlags(); +  if (!FlagsOrErr) { +    consumeError(FlagsOrErr.takeError()); +    return true; +  } + +  uint32_t Flags = *FlagsOrErr; + +  using Filter = SymbolEnumeratorOptions; +  if ((IgnoreFlags & Filter::IgnoreUndefined) && +      (Flags & object::SymbolRef::SF_Undefined)) +    return true; +  if ((IgnoreFlags & Filter::IgnoreIndirect) && +      (Flags & object::SymbolRef::SF_Indirect)) +    return true; +  if ((IgnoreFlags & Filter::IgnoreWeak) && +      (Flags & object::SymbolRef::SF_Weak)) +    return true; + +  return false; +} + +bool SymbolEnumerator::enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach, +                                        const SymbolEnumeratorOptions &Opts) { +  if (Path.empty()) +    return false; + +  ObjectFileLoader ObjLoader(Path); + +  auto ObjOrErr = ObjLoader.getObjectFile(); +  if (!ObjOrErr) { +    std::string ErrMsg; +    handleAllErrors(ObjOrErr.takeError(), +                    [&](const ErrorInfoBase &EIB) { ErrMsg = EIB.message(); }); +    LLVM_DEBUG(dbgs() << "Failed loading object file: " << Path +                      << "\nError: " << ErrMsg << "\n"); +    return false; +  } + +  object::ObjectFile *Obj = &ObjOrErr.get(); + +  auto processSymbolRange = +      [&](object::ObjectFile::symbol_iterator_range Range) -> EnumerateResult { +    for (const auto &Sym : Range) { +      if (shouldIgnoreSymbol(Sym, Opts.FilterFlags)) +        continue; + +      auto NameOrErr = Sym.getName(); +      if (!NameOrErr) { +        consumeError(NameOrErr.takeError()); +        continue; +      } + +      StringRef Name = *NameOrErr; +      if (Name.empty()) +        continue; + +      EnumerateResult Res = OnEach(Name); +      if (Res != EnumerateResult::Continue) +        return Res; +    } +    return EnumerateResult::Continue; +  }; + +  EnumerateResult Res = processSymbolRange(Obj->symbols()); +  if (Res != EnumerateResult::Continue) +    return Res == EnumerateResult::Stop; + +  if (Obj->isELF()) { +    const auto *ElfObj = cast<object::ELFObjectFileBase>(Obj); +    Res = processSymbolRange(ElfObj->getDynamicSymbolIterators()); +    if (Res != EnumerateResult::Continue) +      return Res == EnumerateResult::Stop; +  } else if (Obj->isCOFF()) { +    const auto *CoffObj = cast<object::COFFObjectFile>(Obj); +    for (auto I = CoffObj->export_directory_begin(), +              E = CoffObj->export_directory_end(); +         I != E; ++I) { +      StringRef Name; +      if (I->getSymbolName(Name)) +        continue; +      if (Name.empty()) +        continue; + +      EnumerateResult Res = OnEach(Name); +      if (Res != EnumerateResult::Continue) +        return Res == EnumerateResult::Stop; +    } +  } else if (Obj->isMachO()) { +  } + +  return true; +} + +class SymbolSearchContext { +public: +  SymbolSearchContext(SymbolQuery &Q) : Q(Q) {} + +  bool hasSearched(LibraryInfo *Lib) const { return Searched.count(Lib); } + +  void markSearched(LibraryInfo *Lib) { Searched.insert(Lib); } + +  inline bool allResolved() const { return Q.allResolved(); } + +  SymbolQuery &query() { return Q; } + +private: +  SymbolQuery &Q; +  DenseSet<LibraryInfo *> Searched; +}; + +void LibraryResolver::resolveSymbolsInLibrary( +    LibraryInfo &Lib, SymbolQuery &UnresolvedSymbols, +    const SymbolEnumeratorOptions &Opts) { +  LLVM_DEBUG(dbgs() << "Checking unresolved symbols " +                    << " in library : " << Lib.getFileName() << "\n";); +  StringSet<> DiscoveredSymbols; + +  if (!UnresolvedSymbols.hasUnresolved()) { +    LLVM_DEBUG(dbgs() << "Skipping library: " << Lib.getFullPath() +                      << " — unresolved symbols exist.\n";); +    return; +  } + +  bool HasEnumerated = false; +  auto enumerateSymbolsIfNeeded = [&]() { +    if (HasEnumerated) +      return; + +    HasEnumerated = true; + +    LLVM_DEBUG(dbgs() << "Enumerating symbols in library: " << Lib.getFullPath() +                      << "\n";); +    SymbolEnumerator::enumerateSymbols( +        Lib.getFullPath(), +        [&](StringRef sym) { +          DiscoveredSymbols.insert(sym); +          return EnumerateResult::Continue; +        }, +        Opts); + +    if (DiscoveredSymbols.empty()) { +      LLVM_DEBUG(dbgs() << "  No symbols and remove library : " +                        << Lib.getFullPath() << "\n";); +      LibMgr.removeLibrary(Lib.getFullPath()); +      return; +    } +  }; + +  if (!Lib.hasFilter()) { +    LLVM_DEBUG(dbgs() << "Building filter for library: " << Lib.getFullPath() +                      << "\n";); +    enumerateSymbolsIfNeeded(); +    SmallVector<StringRef> SymbolVec; +    SymbolVec.reserve(DiscoveredSymbols.size()); +    for (const auto &KV : DiscoveredSymbols) +      SymbolVec.push_back(KV.first()); + +    Lib.ensureFilterBuilt(FB, SymbolVec); +    LLVM_DEBUG({ +      dbgs() << "DiscoveredSymbols : " << DiscoveredSymbols.size() << "\n"; +      for (const auto &KV : DiscoveredSymbols) +        dbgs() << "DiscoveredSymbols : " << KV.first() << "\n"; +    }); +  } + +  const auto &Unresolved = UnresolvedSymbols.getUnresolvedSymbols(); +  bool HadAnySym = false; +  LLVM_DEBUG(dbgs() << "Total unresolved symbols : " << Unresolved.size() +                    << "\n";); +  for (const auto &Sym : Unresolved) { +    if (Lib.mayContain(Sym)) { +      LLVM_DEBUG(dbgs() << "Checking symbol '" << Sym +                        << "' in library: " << Lib.getFullPath() << "\n";); +      enumerateSymbolsIfNeeded(); +      if (DiscoveredSymbols.count(Sym) > 0) { +        LLVM_DEBUG(dbgs() << "  Resolved symbol: " << Sym +                          << " in library: " << Lib.getFullPath() << "\n";); +        UnresolvedSymbols.resolve(Sym, Lib.getFullPath()); +        HadAnySym = true; +      } +    } +  } + +  using LibraryState = LibraryManager::LibState; +  if (HadAnySym && Lib.getState() != LibraryState::Loaded) +    Lib.setState(LibraryState::Queried); +} + +void LibraryResolver::searchSymbolsInLibraries( +    std::vector<std::string> &SymbolList, OnSearchComplete OnComplete, +    const SearchConfig &Config) { +  SymbolQuery Q(SymbolList); + +  using LibraryState = LibraryManager::LibState; +  using LibraryType = PathType; +  auto tryResolveFrom = [&](LibraryState S, LibraryType K) { +    LLVM_DEBUG(dbgs() << "Trying resolve from state=" << static_cast<int>(S) +                      << " type=" << static_cast<int>(K) << "\n";); + +    SymbolSearchContext Ctx(Q); +    while (!Ctx.allResolved()) { + +      for (auto &Lib : LibMgr.getView(S, K)) { +        if (Ctx.hasSearched(Lib.get())) +          continue; + +        // can use Async here? +        resolveSymbolsInLibrary(*Lib, Ctx.query(), Config.Options); +        Ctx.markSearched(Lib.get()); + +        if (Ctx.allResolved()) +          return; +      } + +      if (Ctx.allResolved()) +        return; + +      if (!scanLibrariesIfNeeded(K, scanBatchSize)) +        break; // no more new libs to scan +    } +  }; + +  for (const auto &[St, Ty] : Config.Policy.Plan) { +    tryResolveFrom(St, Ty); +    if (Q.allResolved()) +      break; +  } + +  // done: +  LLVM_DEBUG({ +    dbgs() << "Search complete.\n"; +    for (const auto &r : Q.getAllResults()) +      dbgs() << "Resolved Symbol:" << r->Name << " -> " << r->ResolvedLibPath +             << "\n"; +  }); + +  OnComplete(Q); +} + +bool LibraryResolver::scanLibrariesIfNeeded(PathType PK, size_t BatchSize) { +  LLVM_DEBUG(dbgs() << "LibraryResolver::scanLibrariesIfNeeded: Scanning for " +                    << (PK == PathType::User ? "User" : "System") +                    << " libraries\n";); +  if (!ScanHelper.leftToScan(PK)) +    return false; + +  LibraryScanner Scanner(ScanHelper, LibMgr, ShouldScanCall); +  Scanner.scanNext(PK, BatchSize); +  return true; +} + +bool LibraryResolver::symbolExistsInLibrary(const LibraryInfo &Lib, +                                            StringRef SymName, +                                            std::vector<std::string> *AllSyms) { +  SymbolEnumeratorOptions Opts; +  return symbolExistsInLibrary(Lib, SymName, AllSyms, Opts); +} + +bool LibraryResolver::symbolExistsInLibrary( +    const LibraryInfo &Lib, StringRef SymName, +    std::vector<std::string> *AllSyms, const SymbolEnumeratorOptions &Opts) { +  bool Found = false; + +  SymbolEnumerator::enumerateSymbols( +      Lib.getFullPath(), +      [&](StringRef Sym) { +        if (AllSyms) +          AllSyms->emplace_back(Sym.str()); + +        if (Sym == SymName) { +          Found = true; +        } + +        return EnumerateResult::Continue; +      }, +      Opts); + +  return Found; +} + +} // end namespace llvm::orc diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp new file mode 100644 index 0000000..d93f686 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/LibraryScanner.cpp @@ -0,0 +1,1161 @@ +//===- LibraryScanner.cpp - Provide Library Scanning Implementation ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/ELF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/TargetParser/Host.h" +#include "llvm/TargetParser/Triple.h" + +#ifdef LLVM_ON_UNIX +#include <sys/stat.h> +#include <unistd.h> +#endif // LLVM_ON_UNIX + +#ifdef __APPLE__ +#include <sys/stat.h> +#undef LC_LOAD_DYLIB +#undef LC_RPATH +#endif // __APPLE__ + +#define DEBUG_TYPE "orc-scanner" + +namespace llvm::orc { + +void handleError(Error Err, StringRef context = "") { +  consumeError(handleErrors(std::move(Err), [&](const ErrorInfoBase &EIB) { +    dbgs() << "LLVM Error"; +    if (!context.empty()) +      dbgs() << " [" << context << "]"; +    dbgs() << ": " << EIB.message() << "\n"; +  })); +} + +bool ObjectFileLoader::isArchitectureCompatible(const object::ObjectFile &Obj) { +  Triple HostTriple(sys::getDefaultTargetTriple()); +  Triple ObjTriple = Obj.makeTriple(); + +  LLVM_DEBUG({ +    dbgs() << "Host triple: " << HostTriple.str() +           << ", Object triple: " << ObjTriple.str() << "\n"; +  }); + +  if (ObjTriple.getArch() != Triple::UnknownArch && +      HostTriple.getArch() != ObjTriple.getArch()) +    return false; + +  if (ObjTriple.getOS() != Triple::UnknownOS && +      HostTriple.getOS() != ObjTriple.getOS()) +    return false; + +  if (ObjTriple.getEnvironment() != Triple::UnknownEnvironment && +      HostTriple.getEnvironment() != Triple::UnknownEnvironment && +      HostTriple.getEnvironment() != ObjTriple.getEnvironment()) +    return false; + +  return true; +} + +Expected<object::OwningBinary<object::ObjectFile>> +ObjectFileLoader::loadObjectFileWithOwnership(StringRef FilePath) { +  LLVM_DEBUG(dbgs() << "ObjectFileLoader: Attempting to open file " << FilePath +                    << "\n";); +  auto BinOrErr = object::createBinary(FilePath); +  if (!BinOrErr) { +    LLVM_DEBUG(dbgs() << "ObjectFileLoader: Failed to open file " << FilePath +                      << "\n";); +    return BinOrErr.takeError(); +  } + +  LLVM_DEBUG(dbgs() << "ObjectFileLoader: Successfully opened file " << FilePath +                    << "\n";); + +  auto OwningBin = BinOrErr->takeBinary(); +  object::Binary *Bin = OwningBin.first.get(); + +  if (Bin->isArchive()) { +    LLVM_DEBUG(dbgs() << "ObjectFileLoader: File is an archive, not supported: " +                      << FilePath << "\n";); +    return createStringError(std::errc::invalid_argument, +                             "Archive files are not supported: %s", +                             FilePath.str().c_str()); +  } + +#if defined(__APPLE__) +  if (auto *UB = dyn_cast<object::MachOUniversalBinary>(Bin)) { +    LLVM_DEBUG(dbgs() << "ObjectFileLoader: Detected Mach-O universal binary: " +                      << FilePath << "\n";); +    for (auto ObjForArch : UB->objects()) { +      auto ObjOrErr = ObjForArch.getAsObjectFile(); +      if (!ObjOrErr) { +        LLVM_DEBUG( +            dbgs() +                << "ObjectFileLoader: Skipping invalid architecture slice\n";); + +        consumeError(ObjOrErr.takeError()); +        continue; +      } + +      std::unique_ptr<object::ObjectFile> Obj = std::move(ObjOrErr.get()); +      if (isArchitectureCompatible(*Obj)) { +        LLVM_DEBUG( +            dbgs() << "ObjectFileLoader: Found compatible object slice\n";); + +        return object::OwningBinary<object::ObjectFile>( +            std::move(Obj), std::move(OwningBin.second)); + +      } else { +        LLVM_DEBUG(dbgs() << "ObjectFileLoader: Incompatible architecture " +                             "slice skipped\n";); +      } +    } +    LLVM_DEBUG(dbgs() << "ObjectFileLoader: No compatible slices found in " +                         "universal binary\n";); +    return createStringError(inconvertibleErrorCode(), +                             "No compatible object found in fat binary: %s", +                             FilePath.str().c_str()); +  } +#endif + +  auto ObjOrErr = +      object::ObjectFile::createObjectFile(Bin->getMemoryBufferRef()); +  if (!ObjOrErr) { +    LLVM_DEBUG(dbgs() << "ObjectFileLoader: Failed to create object file\n";); +    return ObjOrErr.takeError(); +  } +  LLVM_DEBUG(dbgs() << "ObjectFileLoader: Detected object file\n";); + +  std::unique_ptr<object::ObjectFile> Obj = std::move(*ObjOrErr); +  if (!isArchitectureCompatible(*Obj)) { +    LLVM_DEBUG(dbgs() << "ObjectFileLoader: Incompatible architecture: " +                      << FilePath << "\n";); +    return createStringError(inconvertibleErrorCode(), +                             "Incompatible object file: %s", +                             FilePath.str().c_str()); +  } + +  LLVM_DEBUG(dbgs() << "ObjectFileLoader: Object file is compatible\n";); + +  return object::OwningBinary<object::ObjectFile>(std::move(Obj), +                                                  std::move(OwningBin.second)); +} + +template <class ELFT> +bool isELFSharedLibrary(const object::ELFFile<ELFT> &ELFObj) { +  if (ELFObj.getHeader().e_type != ELF::ET_DYN) +    return false; + +  auto PHOrErr = ELFObj.program_headers(); +  if (!PHOrErr) { +    consumeError(PHOrErr.takeError()); +    return true; +  } + +  for (auto Phdr : *PHOrErr) { +    if (Phdr.p_type == ELF::PT_INTERP) +      return false; +  } + +  return true; +} + +bool isSharedLibraryObject(object::ObjectFile &Obj) { +  if (Obj.isELF()) { +    if (auto *ELF32LE = dyn_cast<object::ELF32LEObjectFile>(&Obj)) +      return isELFSharedLibrary(ELF32LE->getELFFile()); +    if (auto *ELF64LE = dyn_cast<object::ELF64LEObjectFile>(&Obj)) +      return isELFSharedLibrary(ELF64LE->getELFFile()); +    if (auto *ELF32BE = dyn_cast<object::ELF32BEObjectFile>(&Obj)) +      return isELFSharedLibrary(ELF32BE->getELFFile()); +    if (auto *ELF64BE = dyn_cast<object::ELF64BEObjectFile>(&Obj)) +      return isELFSharedLibrary(ELF64BE->getELFFile()); +  } else if (Obj.isMachO()) { +    const object::MachOObjectFile *MachO = +        dyn_cast<object::MachOObjectFile>(&Obj); +    if (!MachO) { +      LLVM_DEBUG(dbgs() << "Failed to cast to MachOObjectFile.\n";); +      return false; +    } +    LLVM_DEBUG({ +      bool Result = +          MachO->getHeader().filetype == MachO::HeaderFileType::MH_DYLIB; +      dbgs() << "Mach-O filetype: " << MachO->getHeader().filetype +             << " (MH_DYLIB == " << MachO::HeaderFileType::MH_DYLIB +             << "), shared: " << Result << "\n"; +    }); + +    return MachO->getHeader().filetype == MachO::HeaderFileType::MH_DYLIB; +  } else if (Obj.isCOFF()) { +    const object::COFFObjectFile *coff = dyn_cast<object::COFFObjectFile>(&Obj); +    if (!coff) +      return false; +    return coff->getCharacteristics() & COFF::IMAGE_FILE_DLL; +  } else { +    LLVM_DEBUG(dbgs() << "Binary is not an ObjectFile.\n";); +  } + +  return false; +} + +bool DylibPathValidator::isSharedLibrary(StringRef Path) { +  LLVM_DEBUG(dbgs() << "Checking if path is a shared library: " << Path +                    << "\n";); + +  auto FileType = sys::fs::get_file_type(Path, /*Follow*/ true); +  if (FileType != sys::fs::file_type::regular_file) { +    LLVM_DEBUG(dbgs() << "File type is not a regular file for path: " << Path +                      << "\n";); +    return false; +  } + +  file_magic MagicCode; +  identify_magic(Path, MagicCode); + +  // Skip archives. +  if (MagicCode == file_magic::archive) +    return false; + +  // Universal binary handling. +#if defined(__APPLE__) +  if (MagicCode == file_magic::macho_universal_binary) { +    ObjectFileLoader ObjLoader(Path); +    auto ObjOrErr = ObjLoader.getObjectFile(); +    if (!ObjOrErr) { +      consumeError(ObjOrErr.takeError()); +      return false; +    } +    return isSharedLibraryObject(ObjOrErr.get()); +  } +#endif + +  // Object file inspection for PE/COFF, ELF, and Mach-O +  bool NeedsObjectInspection = +#if defined(_WIN32) +      (MagicCode == file_magic::pecoff_executable); +#elif defined(__APPLE__) +      (MagicCode == file_magic::macho_fixed_virtual_memory_shared_lib || +       MagicCode == file_magic::macho_dynamically_linked_shared_lib || +       MagicCode == file_magic::macho_dynamically_linked_shared_lib_stub); +#elif defined(LLVM_ON_UNIX) +#ifdef __CYGWIN__ +      (MagicCode == file_magic::pecoff_executable); +#else +      (MagicCode == file_magic::elf_shared_object); +#endif +#else +#error "Unsupported platform." +#endif + +  if (NeedsObjectInspection) { +    ObjectFileLoader ObjLoader(Path); +    auto ObjOrErr = ObjLoader.getObjectFile(); +    if (!ObjOrErr) { +      consumeError(ObjOrErr.takeError()); +      return false; +    } +    return isSharedLibraryObject(ObjOrErr.get()); +  } + +  LLVM_DEBUG(dbgs() << "Path is not identified as a shared library: " << Path +                    << "\n";); +  return false; +} + +void DylibSubstitutor::configure(StringRef LoaderPath) { +  SmallString<512> ExecPath(sys::fs::getMainExecutable(nullptr, nullptr)); +  sys::path::remove_filename(ExecPath); + +  SmallString<512> LoaderDir; +  if (LoaderPath.empty()) { +    LoaderDir = ExecPath; +  } else { +    LoaderDir = LoaderPath.str(); +    if (!sys::fs::is_directory(LoaderPath)) +      sys::path::remove_filename(LoaderDir); +  } + +#ifdef __APPLE__ +  Placeholders["@loader_path"] = std::string(LoaderDir); +  Placeholders["@executable_path"] = std::string(ExecPath); +#else +  Placeholders["$origin"] = std::string(LoaderDir); +#endif +} + +std::optional<std::string> +SearchPathResolver::resolve(StringRef Stem, const DylibSubstitutor &Subst, +                            DylibPathValidator &Validator) const { +  for (const auto &SP : Paths) { +    std::string Base = Subst.substitute(SP); + +    SmallString<512> FullPath(Base); +    if (!PlaceholderPrefix.empty() && +        Stem.starts_with_insensitive(PlaceholderPrefix)) +      FullPath.append(Stem.drop_front(PlaceholderPrefix.size())); +    else +      sys::path::append(FullPath, Stem); + +    LLVM_DEBUG(dbgs() << "SearchPathResolver::resolve FullPath = " << FullPath +                      << "\n";); + +    if (auto Valid = Validator.validate(FullPath.str())) +      return Valid; +  } + +  return std::nullopt; +} + +std::optional<std::string> +DylibResolverImpl::tryWithExtensions(StringRef LibStem) const { +  LLVM_DEBUG(dbgs() << "tryWithExtensions: baseName = " << LibStem << "\n";); +  SmallVector<SmallString<256>, 8> Candidates; + +  // Add extensions by platform +#if defined(__APPLE__) +  Candidates.emplace_back(LibStem); +  Candidates.back() += ".dylib"; +#elif defined(_WIN32) +  Candidates.emplace_back(LibStem); +  Candidates.back() += ".dll"; +#else +  Candidates.emplace_back(LibStem); +  Candidates.back() += ".so"; +#endif + +  // Optionally try "lib" prefix if not already there +  StringRef FileName = sys::path::filename(LibStem); +  StringRef Base = sys::path::parent_path(LibStem); +  if (!FileName.starts_with("lib")) { +    SmallString<256> WithPrefix(Base); +    if (!WithPrefix.empty()) +      sys::path::append(WithPrefix, ""); // ensure separator if needed +    WithPrefix += "lib"; +    WithPrefix += FileName; + +#if defined(__APPLE__) +    WithPrefix += ".dylib"; +#elif defined(_WIN32) +    WithPrefix += ".dll"; +#else +    WithPrefix += ".so"; +#endif + +    Candidates.push_back(std::move(WithPrefix)); +  } + +  LLVM_DEBUG({ +    dbgs() << "  Candidates to try:\n"; +    for (const auto &C : Candidates) +      dbgs() << "    " << C << "\n"; +  }); + +  // Try all variants using tryAllPaths +  for (const auto &Name : Candidates) { + +    LLVM_DEBUG(dbgs() << "  Trying candidate: " << Name << "\n";); + +    for (const auto &R : Resolvers) { +      if (auto Res = R.resolve(Name, Substitutor, Validator)) +        return Res; +    } +  } + +  LLVM_DEBUG(dbgs() << "  -> No candidate Resolved.\n";); + +  return std::nullopt; +} + +std::optional<std::string> +DylibResolverImpl::resolve(StringRef LibStem, bool VariateLibStem) const { +  LLVM_DEBUG(dbgs() << "Resolving library stem: " << LibStem << "\n";); + +  // If it is an absolute path, don't try iterate over the paths. +  if (sys::path::is_absolute(LibStem)) { +    LLVM_DEBUG(dbgs() << "  -> Absolute path detected.\n";); +    return Validator.validate(LibStem); +  } + +  if (!LibStem.starts_with_insensitive("@rpath")) { +    if (auto norm = Validator.validate(Substitutor.substitute(LibStem))) { +      LLVM_DEBUG(dbgs() << "  -> Resolved after substitution: " << *norm +                        << "\n";); + +      return norm; +    } +  } + +  for (const auto &R : Resolvers) { +    LLVM_DEBUG(dbgs() << "  -> Resolving via search path ... \n";); +    if (auto Result = R.resolve(LibStem, Substitutor, Validator)) { +      LLVM_DEBUG(dbgs() << "  -> Resolved via search path: " << *Result +                        << "\n";); + +      return Result; +    } +  } + +  // Expand libStem with paths, extensions, etc. +  // std::string foundName; +  if (VariateLibStem) { +    LLVM_DEBUG(dbgs() << "  -> Trying with extensions...\n";); + +    if (auto Norm = tryWithExtensions(LibStem)) { +      LLVM_DEBUG(dbgs() << "  -> Resolved via tryWithExtensions: " << *Norm +                        << "\n";); + +      return Norm; +    } +  } + +  LLVM_DEBUG(dbgs() << "  -> Could not resolve: " << LibStem << "\n";); + +  return std::nullopt; +} + +#ifndef _WIN32 +mode_t PathResolver::lstatCached(StringRef Path) { +  // If already cached - retun cached result +  if (auto Cache = LibPathCache->read_lstat(Path)) +    return *Cache; + +  // Not cached: perform lstat and store +  struct stat buf{}; +  mode_t st_mode = (lstat(Path.str().c_str(), &buf) == -1) ? 0 : buf.st_mode; + +  LibPathCache->insert_lstat(Path, st_mode); + +  return st_mode; +} + +std::optional<std::string> PathResolver::readlinkCached(StringRef Path) { +  // If already cached - retun cached result +  if (auto Cache = LibPathCache->read_link(Path)) +    return Cache; + +  // If result not in cache - call system function and cache result +  char buf[PATH_MAX]; +  ssize_t len; +  if ((len = readlink(Path.str().c_str(), buf, sizeof(buf))) != -1) { +    buf[len] = '\0'; +    std::string s(buf); +    LibPathCache->insert_link(Path, s); +    return s; +  } +  return std::nullopt; +} + +void createComponent(StringRef Path, StringRef BasePath, bool BaseIsResolved, +                     SmallVector<StringRef, 16> &Component) { +  StringRef Separator = sys::path::get_separator(); +  if (!BaseIsResolved) { +    if (Path[0] == '~' && +        (Path.size() == 1 || sys::path::is_separator(Path[1]))) { +      static SmallString<128> HomeP; +      if (HomeP.str().empty()) +        sys::path::home_directory(HomeP); +      StringRef(HomeP).split(Component, Separator, /*MaxSplit*/ -1, +                             /*KeepEmpty*/ false); +    } else if (BasePath.empty()) { +      static SmallString<256> CurrentPath; +      if (CurrentPath.str().empty()) +        sys::fs::current_path(CurrentPath); +      StringRef(CurrentPath) +          .split(Component, Separator, /*MaxSplit*/ -1, /*KeepEmpty*/ false); +    } else { +      BasePath.split(Component, Separator, /*MaxSplit*/ -1, +                     /*KeepEmpty*/ false); +    } +  } + +  Path.split(Component, Separator, /*MaxSplit*/ -1, /*KeepEmpty*/ false); +} + +void normalizePathSegments(SmallVector<StringRef, 16> &PathParts) { +  SmallVector<StringRef, 16> NormalizedPath; +  for (auto &Part : PathParts) { +    if (Part == ".") { +      continue; +    } else if (Part == "..") { +      if (!NormalizedPath.empty() && NormalizedPath.back() != "..") { +        NormalizedPath.pop_back(); +      } else { +        NormalizedPath.push_back(".."); +      } +    } else { +      NormalizedPath.push_back(Part); +    } +  } +  PathParts.swap(NormalizedPath); +} +#endif + +std::optional<std::string> PathResolver::realpathCached(StringRef Path, +                                                        std::error_code &EC, +                                                        StringRef Base, +                                                        bool BaseIsResolved, +                                                        long SymLoopLevel) { +  EC.clear(); + +  if (Path.empty()) { +    EC = std::make_error_code(std::errc::no_such_file_or_directory); +    LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Empty path\n";); + +    return std::nullopt; +  } + +  if (SymLoopLevel <= 0) { +    EC = std::make_error_code(std::errc::too_many_symbolic_link_levels); +    LLVM_DEBUG( +        dbgs() << "PathResolver::realpathCached: Too many Symlink levels: " +               << Path << "\n";); + +    return std::nullopt; +  } + +  // If already cached - retun cached result +  bool isRelative = sys::path::is_relative(Path); +  if (!isRelative) { +    if (auto Cached = LibPathCache->read_realpath(Path)) { +      EC = Cached->ErrnoCode; +      if (EC) { +        LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Cached (error) for " +                          << Path << "\n";); +      } else { +        LLVM_DEBUG( +            dbgs() << "PathResolver::realpathCached: Cached (success) for " +                   << Path << " => " << Cached->canonicalPath << "\n";); +      } +      return Cached->canonicalPath.empty() +                 ? std::nullopt +                 : std::make_optional(Cached->canonicalPath); +    } +  } + +  LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Resolving path: " << Path +                    << "\n";); + +  // If result not in cache - call system function and cache result + +  StringRef Separator(sys::path::get_separator()); +  SmallString<256> Resolved(Separator); +#ifndef _WIN32 +  SmallVector<StringRef, 16> Components; + +  if (isRelative) { +    if (BaseIsResolved) { +      Resolved.assign(Base); +      LLVM_DEBUG(dbgs() << "  Using Resolved base: " << Base << "\n";); +    } +    createComponent(Path, Base, BaseIsResolved, Components); +  } else { +    Path.split(Components, Separator, /*MaxSplit*/ -1, /*KeepEmpty*/ false); +  } + +  normalizePathSegments(Components); +  LLVM_DEBUG({ +    for (auto &C : Components) +      dbgs() << " " << C << " "; + +    dbgs() << "\n"; +  }); + +  // Handle path list items +  for (const auto &Component : Components) { +    if (Component == ".") +      continue; +    if (Component == "..") { +      // collapse "a/b/../c" to "a/c" +      size_t S = Resolved.rfind(Separator); +      if (S != llvm::StringRef::npos) +        Resolved.resize(S); +      if (Resolved.empty()) +        Resolved = Separator; +      continue; +    } + +    size_t oldSize = Resolved.size(); +    sys::path::append(Resolved, Component); +    const char *ResolvedPath = Resolved.c_str(); +    LLVM_DEBUG(dbgs() << "  Processing Component: " << Component << " => " +                      << ResolvedPath << "\n";); +    mode_t st_mode = lstatCached(ResolvedPath); + +    if (S_ISLNK(st_mode)) { +      LLVM_DEBUG(dbgs() << "    Found symlink: " << ResolvedPath << "\n";); + +      auto SymlinkOpt = readlinkCached(ResolvedPath); +      if (!SymlinkOpt) { +        EC = std::make_error_code(std::errc::no_such_file_or_directory); +        LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{"", EC}); +        LLVM_DEBUG(dbgs() << "    Failed to read symlink: " << ResolvedPath +                          << "\n";); + +        return std::nullopt; +      } + +      StringRef Symlink = *SymlinkOpt; +      LLVM_DEBUG(dbgs() << "    Symlink points to: " << Symlink << "\n";); + +      std::string resolvedBase = ""; +      if (sys::path::is_relative(Symlink)) { +        Resolved.resize(oldSize); +        resolvedBase = Resolved.str().str(); +      } + +      auto RealSymlink = +          realpathCached(Symlink, EC, resolvedBase, +                         /*BaseIsResolved=*/true, SymLoopLevel - 1); +      if (!RealSymlink) { +        LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{"", EC}); +        LLVM_DEBUG(dbgs() << "    Failed to resolve symlink target: " << Symlink +                          << "\n";); + +        return std::nullopt; +      } + +      Resolved.assign(*RealSymlink); +      LLVM_DEBUG(dbgs() << "    Symlink Resolved to: " << Resolved << "\n";); + +    } else if (st_mode == 0) { +      EC = std::make_error_code(std::errc::no_such_file_or_directory); +      LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{"", EC}); +      LLVM_DEBUG(dbgs() << "    Component does not exist: " << ResolvedPath +                        << "\n";); + +      return std::nullopt; +    } +  } +#else +  EC = sys::fs::real_path(Path, Resolved); // Windows fallback +#endif + +  std::string Canonical = Resolved.str().str(); +  { +    LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{ +                                            Canonical, +                                            std::error_code() // success +                                        }); +  } +  LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Final Resolved: " << Path +                    << " => " << Canonical << "\n";); +  return Canonical; +} + +void LibraryScanHelper::addBasePath(const std::string &Path, PathType K) { +  std::error_code EC; +  std::string Canon = resolveCanonical(Path, EC); +  if (EC) { +    LLVM_DEBUG( +        dbgs() +            << "LibraryScanHelper::addBasePath: Failed to canonicalize path: " +            << Path << "\n";); +    return; +  } +  std::unique_lock<std::shared_mutex> Lock(Mtx); +  if (LibSearchPaths.count(Canon)) { +    LLVM_DEBUG(dbgs() << "LibraryScanHelper::addBasePath: Already added: " +                      << Canon << "\n";); +    return; +  } +  K = K == PathType::Unknown ? classifyKind(Canon) : K; +  auto SP = std::make_shared<LibrarySearchPath>(Canon, K); +  LibSearchPaths[Canon] = SP; + +  if (K == PathType::User) { +    LLVM_DEBUG(dbgs() << "LibraryScanHelper::addBasePath: Added User path: " +                      << Canon << "\n";); +    UnscannedUsr.push_back(StringRef(SP->BasePath)); +  } else { +    LLVM_DEBUG(dbgs() << "LibraryScanHelper::addBasePath: Added System path: " +                      << Canon << "\n";); +    UnscannedSys.push_back(StringRef(SP->BasePath)); +  } +} + +std::vector<std::shared_ptr<LibrarySearchPath>> +LibraryScanHelper::getNextBatch(PathType K, size_t BatchSize) { +  std::vector<std::shared_ptr<LibrarySearchPath>> Result; +  auto &Queue = (K == PathType::User) ? UnscannedUsr : UnscannedSys; + +  std::unique_lock<std::shared_mutex> Lock(Mtx); + +  while (!Queue.empty() && (BatchSize == 0 || Result.size() < BatchSize)) { +    StringRef Base = Queue.front(); +    auto It = LibSearchPaths.find(Base); +    if (It != LibSearchPaths.end()) { +      auto &SP = It->second; +      ScanState Expected = ScanState::NotScanned; +      if (SP->State.compare_exchange_strong(Expected, ScanState::Scanning)) { +        Result.push_back(SP); +      } +    } +    Queue.pop_front(); +  } + +  return Result; +} + +bool LibraryScanHelper::isTrackedBasePath(StringRef Path) const { +  std::error_code EC; +  std::string Canon = resolveCanonical(Path, EC); +  if (EC) +    return false; + +  std::shared_lock<std::shared_mutex> Lock(Mtx); +  return LibSearchPaths.count(Canon) > 0; +} + +bool LibraryScanHelper::leftToScan(PathType K) const { +  std::shared_lock<std::shared_mutex> Lock(Mtx); +  for (const auto &KV : LibSearchPaths) { +    const auto &SP = KV.second; +    if (SP->Kind == K && SP->State == ScanState::NotScanned) +      return true; +  } +  return false; +} + +void LibraryScanHelper::resetToScan() { +  std::shared_lock<std::shared_mutex> Lock(Mtx); + +  for (auto &[_, SP] : LibSearchPaths) { +    ScanState Expected = ScanState::Scanned; + +    if (!SP->State.compare_exchange_strong(Expected, ScanState::NotScanned)) +      continue; + +    auto &TargetList = +        (SP->Kind == PathType::User) ? UnscannedUsr : UnscannedSys; +    TargetList.emplace_back(SP->BasePath); +  } +} + +std::vector<std::shared_ptr<LibrarySearchPath>> +LibraryScanHelper::getAllUnits() const { +  std::shared_lock<std::shared_mutex> Lock(Mtx); +  std::vector<std::shared_ptr<LibrarySearchPath>> Result; +  Result.reserve(LibSearchPaths.size()); +  for (const auto &[_, SP] : LibSearchPaths) { +    Result.push_back(SP); +  } +  return Result; +} + +std::string LibraryScanHelper::resolveCanonical(StringRef Path, +                                                std::error_code &EC) const { +  auto Canon = LibPathResolver->resolve(Path, EC); +  return EC ? Path.str() : *Canon; +} + +PathType LibraryScanHelper::classifyKind(StringRef Path) const { +  // Detect home directory +  const char *Home = getenv("HOME"); +  if (Home && Path.find(Home) == 0) +    return PathType::User; + +  static const std::array<std::string, 5> UserPrefixes = { +      "/usr/local",    // often used by users for manual installs +      "/opt/homebrew", // common on macOS +      "/opt/local",    // MacPorts +      "/home",         // Linux home dirs +      "/Users",        // macOS user dirs +  }; + +  for (const auto &Prefix : UserPrefixes) { +    if (Path.find(Prefix) == 0) +      return PathType::User; +  } + +  return PathType::System; +} + +Expected<LibraryDepsInfo> parseMachODeps(const object::MachOObjectFile &Obj) { +  LibraryDepsInfo Libdeps; +  LLVM_DEBUG(dbgs() << "Parsing Mach-O dependencies...\n";); +  for (const auto &Command : Obj.load_commands()) { +    switch (Command.C.cmd) { +    case MachO::LC_LOAD_DYLIB: { +      MachO::dylib_command dylibCmd = Obj.getDylibIDLoadCommand(Command); +      const char *name = Command.Ptr + dylibCmd.dylib.name; +      Libdeps.addDep(name); +      LLVM_DEBUG(dbgs() << "  Found LC_LOAD_DYLIB: " << name << "\n";); +    } break; +    case MachO::LC_LOAD_WEAK_DYLIB: +    case MachO::LC_REEXPORT_DYLIB: +    case MachO::LC_LOAD_UPWARD_DYLIB: +    case MachO::LC_LAZY_LOAD_DYLIB: +      break; +    case MachO::LC_RPATH: { +      // Extract RPATH +      MachO::rpath_command rpathCmd = Obj.getRpathCommand(Command); +      const char *rpath = Command.Ptr + rpathCmd.path; +      LLVM_DEBUG(dbgs() << "  Found LC_RPATH: " << rpath << "\n";); + +      SmallVector<StringRef, 4> RawPaths; +      SplitString(StringRef(rpath), RawPaths, +                  sys::EnvPathSeparator == ':' ? ":" : ";"); + +      for (const auto &raw : RawPaths) { +        Libdeps.addRPath(raw.str()); // Convert to std::string +        LLVM_DEBUG(dbgs() << "    Parsed RPATH entry: " << raw << "\n";); +      } +      break; +    } +    } +  } + +  return Expected<LibraryDepsInfo>(std::move(Libdeps)); +} + +template <class ELFT> +static Expected<StringRef> getDynamicStrTab(const object::ELFFile<ELFT> &Elf) { +  auto DynamicEntriesOrError = Elf.dynamicEntries(); +  if (!DynamicEntriesOrError) +    return DynamicEntriesOrError.takeError(); + +  for (const typename ELFT::Dyn &Dyn : *DynamicEntriesOrError) { +    if (Dyn.d_tag == ELF::DT_STRTAB) { +      auto MappedAddrOrError = Elf.toMappedAddr(Dyn.getPtr()); +      if (!MappedAddrOrError) +        return MappedAddrOrError.takeError(); +      return StringRef(reinterpret_cast<const char *>(*MappedAddrOrError)); +    } +  } + +  // If the dynamic segment is not present, we fall back on the sections. +  auto SectionsOrError = Elf.sections(); +  if (!SectionsOrError) +    return SectionsOrError.takeError(); + +  for (const typename ELFT::Shdr &Sec : *SectionsOrError) { +    if (Sec.sh_type == ELF::SHT_DYNSYM) +      return Elf.getStringTableForSymtab(Sec); +  } + +  return make_error<StringError>("dynamic string table not found", +                                 inconvertibleErrorCode()); +} + +template <typename ELFT> +Expected<LibraryDepsInfo> parseELF(const object::ELFFile<ELFT> &Elf) { +  LibraryDepsInfo Deps; +  Expected<StringRef> StrTabOrErr = getDynamicStrTab(Elf); +  if (!StrTabOrErr) +    return StrTabOrErr.takeError(); + +  const char *Data = StrTabOrErr->data(); + +  auto DynamicEntriesOrError = Elf.dynamicEntries(); +  if (!DynamicEntriesOrError) { +    return DynamicEntriesOrError.takeError(); +  } + +  for (const typename ELFT::Dyn &Dyn : *DynamicEntriesOrError) { +    switch (Dyn.d_tag) { +    case ELF::DT_NEEDED: +      Deps.addDep(Data + Dyn.d_un.d_val); +      break; +    case ELF::DT_RPATH: { +      SmallVector<StringRef, 4> RawPaths; +      SplitString(Data + Dyn.d_un.d_val, RawPaths, +                  sys::EnvPathSeparator == ':' ? ":" : ";"); +      for (const auto &raw : RawPaths) +        Deps.addRPath(raw.str()); +      break; +    } +    case ELF::DT_RUNPATH: { +      SmallVector<StringRef, 4> RawPaths; +      SplitString(Data + Dyn.d_un.d_val, RawPaths, +                  sys::EnvPathSeparator == ':' ? ":" : ";"); +      for (const auto &raw : RawPaths) +        Deps.addRunPath(raw.str()); +      break; +    } +    case ELF::DT_FLAGS_1: +      // Check if this is not a pie executable. +      if (Dyn.d_un.d_val & ELF::DF_1_PIE) +        Deps.isPIE = true; +      break; +      // (Dyn.d_tag == ELF::DT_NULL) continue; +      // (Dyn.d_tag == ELF::DT_AUXILIARY || Dyn.d_tag == ELF::DT_FILTER) +    default: +      break; +    } +  } + +  return Expected<LibraryDepsInfo>(std::move(Deps)); +} + +Expected<LibraryDepsInfo> parseELFDeps(const object::ELFObjectFileBase &Obj) { +  using namespace object; +  LLVM_DEBUG(dbgs() << "parseELFDeps: Detected ELF object\n";); +  if (const auto *ELF = dyn_cast<ELF32LEObjectFile>(&Obj)) +    return parseELF(ELF->getELFFile()); +  else if (const auto *ELF = dyn_cast<ELF32BEObjectFile>(&Obj)) +    return parseELF(ELF->getELFFile()); +  else if (const auto *ELF = dyn_cast<ELF64LEObjectFile>(&Obj)) +    return parseELF(ELF->getELFFile()); +  else if (const auto *ELF = dyn_cast<ELF64BEObjectFile>(&Obj)) +    return parseELF(ELF->getELFFile()); + +  LLVM_DEBUG(dbgs() << "parseELFDeps: Unknown ELF format\n";); +  return createStringError(std::errc::not_supported, "Unknown ELF format"); +} + +Expected<LibraryDepsInfo> LibraryScanner::extractDeps(StringRef FilePath) { +  LLVM_DEBUG(dbgs() << "extractDeps: Attempting to open file " << FilePath +                    << "\n";); + +  ObjectFileLoader ObjLoader(FilePath); +  auto ObjOrErr = ObjLoader.getObjectFile(); +  if (!ObjOrErr) { +    LLVM_DEBUG(dbgs() << "extractDeps: Failed to open " << FilePath << "\n";); +    return ObjOrErr.takeError(); +  } + +  object::ObjectFile *Obj = &ObjOrErr.get(); + +  if (auto *elfObj = dyn_cast<object::ELFObjectFileBase>(Obj)) { +    LLVM_DEBUG(dbgs() << "extractDeps: File " << FilePath +                      << " is an ELF object\n";); + +    return parseELFDeps(*elfObj); +  } + +  if (auto *macho = dyn_cast<object::MachOObjectFile>(Obj)) { +    LLVM_DEBUG(dbgs() << "extractDeps: File " << FilePath +                      << " is a Mach-O object\n";); +    return parseMachODeps(*macho); +  } + +  if (Obj->isCOFF()) { +    // TODO: COFF support +    return LibraryDepsInfo(); +  } + +  LLVM_DEBUG(dbgs() << "extractDeps: Unsupported binary format for file " +                    << FilePath << "\n";); +  return createStringError(inconvertibleErrorCode(), +                           "Unsupported binary format: %s", +                           FilePath.str().c_str()); +} + +std::optional<std::string> LibraryScanner::shouldScan(StringRef FilePath) { +  std::error_code EC; + +  LLVM_DEBUG(dbgs() << "[shouldScan] Checking: " << FilePath << "\n";); + +  // [1] Check file existence early +  if (!sys::fs::exists(FilePath)) { +    LLVM_DEBUG(dbgs() << "  -> Skipped: file does not exist.\n";); + +    return std::nullopt; +  } + +  // [2] Resolve to canonical path +  auto CanonicalPathOpt = ScanHelper.resolve(FilePath, EC); +  if (EC || !CanonicalPathOpt) { +    LLVM_DEBUG(dbgs() << "  -> Skipped: failed to resolve path (EC=" +                      << EC.message() << ").\n";); + +    return std::nullopt; +  } + +  const std::string &CanonicalPath = *CanonicalPathOpt; +  LLVM_DEBUG(dbgs() << "  -> Canonical path: " << CanonicalPath << "\n"); + +  // [3] Check if it's a directory — skip directories +  if (sys::fs::is_directory(CanonicalPath)) { +    LLVM_DEBUG(dbgs() << "  -> Skipped: path is a directory.\n";); + +    return std::nullopt; +  } + +  // [4] Skip if it's not a shared library. +  if (!DylibPathValidator::isSharedLibrary(CanonicalPath)) { +    LLVM_DEBUG(dbgs() << "  -> Skipped: not a shared library.\n";); +    return std::nullopt; +  } + +  // [5] Skip if we've already seen this path (via cache) +  if (ScanHelper.hasSeenOrMark(CanonicalPath)) { +    LLVM_DEBUG(dbgs() << "  -> Skipped: already seen.\n";); + +    return std::nullopt; +  } + +  // [6] Already tracked in LibraryManager? +  if (LibMgr.hasLibrary(CanonicalPath)) { +    LLVM_DEBUG(dbgs() << "  -> Skipped: already tracked by LibraryManager.\n";); + +    return std::nullopt; +  } + +  // [7] Run user-defined hook (default: always true) +  if (!ShouldScanCall(CanonicalPath)) { +    LLVM_DEBUG(dbgs() << "  -> Skipped: user-defined hook rejected.\n";); + +    return std::nullopt; +  } + +  LLVM_DEBUG(dbgs() << "  -> Accepted: ready to scan " << CanonicalPath +                    << "\n";); +  return CanonicalPath; +} + +void LibraryScanner::handleLibrary(StringRef FilePath, PathType K, int level) { +  LLVM_DEBUG(dbgs() << "LibraryScanner::handleLibrary: Scanning: " << FilePath +                    << ", level=" << level << "\n";); +  auto CanonPathOpt = shouldScan(FilePath); +  if (!CanonPathOpt) { +    LLVM_DEBUG(dbgs() << "  Skipped (shouldScan returned false): " << FilePath +                      << "\n";); + +    return; +  } +  const std::string CanonicalPath = *CanonPathOpt; + +  auto DepsOrErr = extractDeps(CanonicalPath); +  if (!DepsOrErr) { +    LLVM_DEBUG(dbgs() << "  Failed to extract deps for: " << CanonicalPath +                      << "\n";); +    handleError(DepsOrErr.takeError()); +    return; +  } + +  LibraryDepsInfo &Deps = *DepsOrErr; + +  LLVM_DEBUG({ +    dbgs() << "    Found deps : \n"; +    for (const auto &dep : Deps.deps) +      dbgs() << "        : " << dep << "\n"; +    dbgs() << "    Found @rpath : " << Deps.rpath.size() << "\n"; +    for (const auto &r : Deps.rpath) +      dbgs() << "     : " << r << "\n"; +    dbgs() << "    Found @runpath : \n"; +    for (const auto &r : Deps.runPath) +      dbgs() << "     : " << r << "\n"; +  }); + +  if (Deps.isPIE && level == 0) { +    LLVM_DEBUG(dbgs() << "  Skipped PIE executable at top level: " +                      << CanonicalPath << "\n";); + +    return; +  } + +  bool Added = LibMgr.addLibrary(CanonicalPath, K); +  if (!Added) { +    LLVM_DEBUG(dbgs() << "  Already added: " << CanonicalPath << "\n";); +    return; +  } + +  // Heuristic 1: No RPATH/RUNPATH, skip deps +  if (Deps.rpath.empty() && Deps.runPath.empty()) { +    LLVM_DEBUG( +        dbgs() << "LibraryScanner::handleLibrary: Skipping deps (Heuristic1): " +               << CanonicalPath << "\n";); +    return; +  } + +  // Heuristic 2: All RPATH and RUNPATH already tracked +  auto allTracked = [&](const auto &Paths) { +    LLVM_DEBUG(dbgs() << "   Checking : " << Paths.size() << "\n";); +    return std::all_of(Paths.begin(), Paths.end(), [&](StringRef P) { +      LLVM_DEBUG(dbgs() << "      Checking isTrackedBasePath : " << P << "\n";); +      return ScanHelper.isTrackedBasePath( +          DylibResolver::resolvelinkerFlag(P, CanonicalPath)); +    }); +  }; + +  if (allTracked(Deps.rpath) && allTracked(Deps.runPath)) { +    LLVM_DEBUG( +        dbgs() << "LibraryScanner::handleLibrary: Skipping deps (Heuristic2): " +               << CanonicalPath << "\n";); +    return; +  } + +  DylibPathValidator Validator(ScanHelper.getPathResolver()); +  DylibResolver Resolver(Validator); +  Resolver.configure(CanonicalPath, +                     {{Deps.rpath, SearchPathType::RPath}, +                      {ScanHelper.getSearchPaths(), SearchPathType::UsrOrSys}, +                      {Deps.runPath, SearchPathType::RunPath}}); +  for (StringRef Dep : Deps.deps) { +    LLVM_DEBUG(dbgs() << "  Resolving dep: " << Dep << "\n";); +    auto DepFullOpt = Resolver.resolve(Dep); +    if (!DepFullOpt) { +      LLVM_DEBUG(dbgs() << "    Failed to resolve dep: " << Dep << "\n";); + +      continue; +    } +    LLVM_DEBUG(dbgs() << "    Resolved dep to: " << *DepFullOpt << "\n";); + +    handleLibrary(*DepFullOpt, K, level + 1); +  } +} + +void LibraryScanner::scanBaseDir(std::shared_ptr<LibrarySearchPath> SP) { +  if (!sys::fs::is_directory(SP->BasePath) || SP->BasePath.empty()) { +    LLVM_DEBUG( +        dbgs() << "LibraryScanner::scanBaseDir: Invalid or empty basePath: " +               << SP->BasePath << "\n";); +    return; +  } + +  LLVM_DEBUG(dbgs() << "LibraryScanner::scanBaseDir: Scanning directory: " +                    << SP->BasePath << "\n";); +  std::error_code EC; + +  SP->State.store(ScanState::Scanning); + +  for (sys::fs::directory_iterator It(SP->BasePath, EC), end; It != end && !EC; +       It.increment(EC)) { +    auto Entry = *It; +    if (!Entry.status()) +      continue; + +    auto Status = *Entry.status(); +    if (sys::fs::is_regular_file(Status) || sys::fs::is_symlink_file(Status)) { +      LLVM_DEBUG(dbgs() << "  Found file: " << Entry.path() << "\n";); +      // async support ? +      handleLibrary(Entry.path(), SP->Kind); +    } +  } + +  SP->State.store(ScanState::Scanned); +} + +void LibraryScanner::scanNext(PathType K, size_t BatchSize) { +  LLVM_DEBUG(dbgs() << "LibraryScanner::scanNext: Scanning next batch of size " +                    << BatchSize << " for kind " +                    << (K == PathType::User ? "User" : "System") << "\n";); + +  auto SearchPaths = ScanHelper.getNextBatch(K, BatchSize); +  for (auto &SP : SearchPaths) { +    LLVM_DEBUG(dbgs() << "  Scanning unit with basePath: " << SP->BasePath +                      << "\n";); + +    scanBaseDir(SP); +  } +} + +} // end namespace llvm::orc  | 
