brintos

brintos / llvm-project-archived public Read only

0
0
Text · 36.6 KiB · 96f0b66 Raw
1162 lines · cpp
1//===- LibraryScanner.cpp - Provide Library Scanning Implementation ----===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h"10#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h"11 12#include "llvm/ADT/StringExtras.h"13#include "llvm/Object/COFF.h"14#include "llvm/Object/ELF.h"15#include "llvm/Object/ELFObjectFile.h"16#include "llvm/Object/ELFTypes.h"17#include "llvm/Object/MachO.h"18#include "llvm/Object/MachOUniversal.h"19#include "llvm/Object/ObjectFile.h"20#include "llvm/Support/Error.h"21#include "llvm/Support/FileSystem.h"22#include "llvm/Support/MemoryBuffer.h"23#include "llvm/Support/Path.h"24#include "llvm/Support/Program.h"25#include "llvm/TargetParser/Host.h"26#include "llvm/TargetParser/Triple.h"27 28#ifdef LLVM_ON_UNIX29#include <sys/stat.h>30#include <unistd.h>31#endif // LLVM_ON_UNIX32 33#ifdef __APPLE__34#include <sys/stat.h>35#undef LC_LOAD_DYLIB36#undef LC_RPATH37#endif // __APPLE__38 39#define DEBUG_TYPE "orc-scanner"40 41namespace llvm::orc {42 43void handleError(Error Err, StringRef context = "") {44  consumeError(handleErrors(std::move(Err), [&](const ErrorInfoBase &EIB) {45    dbgs() << "LLVM Error";46    if (!context.empty())47      dbgs() << " [" << context << "]";48    dbgs() << ": " << EIB.message() << "\n";49  }));50}51 52bool ObjectFileLoader::isArchitectureCompatible(const object::ObjectFile &Obj) {53  Triple HostTriple(sys::getProcessTriple());54  Triple ObjTriple = Obj.makeTriple();55 56  LLVM_DEBUG({57    dbgs() << "Host triple: " << HostTriple.str()58           << ", Object triple: " << ObjTriple.str() << "\n";59  });60 61  if (ObjTriple.getArch() != Triple::UnknownArch &&62      HostTriple.getArch() != ObjTriple.getArch())63    return false;64 65  if (ObjTriple.getOS() != Triple::UnknownOS &&66      HostTriple.getOS() != ObjTriple.getOS())67    return false;68 69  if (ObjTriple.getEnvironment() != Triple::UnknownEnvironment &&70      HostTriple.getEnvironment() != Triple::UnknownEnvironment &&71      HostTriple.getEnvironment() != ObjTriple.getEnvironment())72    return false;73 74  return true;75}76 77Expected<object::OwningBinary<object::ObjectFile>>78ObjectFileLoader::loadObjectFileWithOwnership(StringRef FilePath) {79  LLVM_DEBUG(dbgs() << "ObjectFileLoader: Attempting to open file " << FilePath80                    << "\n";);81  auto BinOrErr = object::createBinary(FilePath);82  if (!BinOrErr) {83    LLVM_DEBUG(dbgs() << "ObjectFileLoader: Failed to open file " << FilePath84                      << "\n";);85    return BinOrErr.takeError();86  }87 88  LLVM_DEBUG(dbgs() << "ObjectFileLoader: Successfully opened file " << FilePath89                    << "\n";);90 91  auto OwningBin = BinOrErr->takeBinary();92  object::Binary *Bin = OwningBin.first.get();93 94  if (Bin->isArchive()) {95    LLVM_DEBUG(dbgs() << "ObjectFileLoader: File is an archive, not supported: "96                      << FilePath << "\n";);97    return createStringError(std::errc::invalid_argument,98                             "Archive files are not supported: %s",99                             FilePath.str().c_str());100  }101 102#if defined(__APPLE__)103  if (auto *UB = dyn_cast<object::MachOUniversalBinary>(Bin)) {104    LLVM_DEBUG(dbgs() << "ObjectFileLoader: Detected Mach-O universal binary: "105                      << FilePath << "\n";);106    for (auto ObjForArch : UB->objects()) {107      auto ObjOrErr = ObjForArch.getAsObjectFile();108      if (!ObjOrErr) {109        LLVM_DEBUG(110            dbgs()111                << "ObjectFileLoader: Skipping invalid architecture slice\n";);112 113        consumeError(ObjOrErr.takeError());114        continue;115      }116 117      std::unique_ptr<object::ObjectFile> Obj = std::move(ObjOrErr.get());118      if (isArchitectureCompatible(*Obj)) {119        LLVM_DEBUG(120            dbgs() << "ObjectFileLoader: Found compatible object slice\n";);121 122        return object::OwningBinary<object::ObjectFile>(123            std::move(Obj), std::move(OwningBin.second));124 125      } else {126        LLVM_DEBUG(dbgs() << "ObjectFileLoader: Incompatible architecture "127                             "slice skipped\n";);128      }129    }130    LLVM_DEBUG(dbgs() << "ObjectFileLoader: No compatible slices found in "131                         "universal binary\n";);132    return createStringError(inconvertibleErrorCode(),133                             "No compatible object found in fat binary: %s",134                             FilePath.str().c_str());135  }136#endif137 138  auto ObjOrErr =139      object::ObjectFile::createObjectFile(Bin->getMemoryBufferRef());140  if (!ObjOrErr) {141    LLVM_DEBUG(dbgs() << "ObjectFileLoader: Failed to create object file\n";);142    return ObjOrErr.takeError();143  }144  LLVM_DEBUG(dbgs() << "ObjectFileLoader: Detected object file\n";);145 146  std::unique_ptr<object::ObjectFile> Obj = std::move(*ObjOrErr);147  if (!isArchitectureCompatible(*Obj)) {148    LLVM_DEBUG(dbgs() << "ObjectFileLoader: Incompatible architecture: "149                      << FilePath << "\n";);150    return createStringError(inconvertibleErrorCode(),151                             "Incompatible object file: %s",152                             FilePath.str().c_str());153  }154 155  LLVM_DEBUG(dbgs() << "ObjectFileLoader: Object file is compatible\n";);156 157  return object::OwningBinary<object::ObjectFile>(std::move(Obj),158                                                  std::move(OwningBin.second));159}160 161template <class ELFT>162bool isELFSharedLibrary(const object::ELFFile<ELFT> &ELFObj) {163  if (ELFObj.getHeader().e_type != ELF::ET_DYN)164    return false;165 166  auto PHOrErr = ELFObj.program_headers();167  if (!PHOrErr) {168    consumeError(PHOrErr.takeError());169    return true;170  }171 172  for (auto Phdr : *PHOrErr) {173    if (Phdr.p_type == ELF::PT_INTERP)174      return false;175  }176 177  return true;178}179 180bool isSharedLibraryObject(object::ObjectFile &Obj) {181  if (Obj.isELF()) {182    if (auto *ELF32LE = dyn_cast<object::ELF32LEObjectFile>(&Obj))183      return isELFSharedLibrary(ELF32LE->getELFFile());184    if (auto *ELF64LE = dyn_cast<object::ELF64LEObjectFile>(&Obj))185      return isELFSharedLibrary(ELF64LE->getELFFile());186    if (auto *ELF32BE = dyn_cast<object::ELF32BEObjectFile>(&Obj))187      return isELFSharedLibrary(ELF32BE->getELFFile());188    if (auto *ELF64BE = dyn_cast<object::ELF64BEObjectFile>(&Obj))189      return isELFSharedLibrary(ELF64BE->getELFFile());190  } else if (Obj.isMachO()) {191    const object::MachOObjectFile *MachO =192        dyn_cast<object::MachOObjectFile>(&Obj);193    if (!MachO) {194      LLVM_DEBUG(dbgs() << "Failed to cast to MachOObjectFile.\n";);195      return false;196    }197    LLVM_DEBUG({198      bool Result =199          MachO->getHeader().filetype == MachO::HeaderFileType::MH_DYLIB;200      dbgs() << "Mach-O filetype: " << MachO->getHeader().filetype201             << " (MH_DYLIB == " << MachO::HeaderFileType::MH_DYLIB202             << "), shared: " << Result << "\n";203    });204 205    return MachO->getHeader().filetype == MachO::HeaderFileType::MH_DYLIB;206  } else if (Obj.isCOFF()) {207    const object::COFFObjectFile *coff = dyn_cast<object::COFFObjectFile>(&Obj);208    if (!coff)209      return false;210    return coff->getCharacteristics() & COFF::IMAGE_FILE_DLL;211  } else {212    LLVM_DEBUG(dbgs() << "Binary is not an ObjectFile.\n";);213  }214 215  return false;216}217 218bool DylibPathValidator::isSharedLibrary(StringRef Path) {219  LLVM_DEBUG(dbgs() << "Checking if path is a shared library: " << Path220                    << "\n";);221 222  auto FileType = sys::fs::get_file_type(Path, /*Follow*/ true);223  if (FileType != sys::fs::file_type::regular_file) {224    LLVM_DEBUG(dbgs() << "File type is not a regular file for path: " << Path225                      << "\n";);226    return false;227  }228 229  file_magic MagicCode;230  identify_magic(Path, MagicCode);231 232  // Skip archives.233  if (MagicCode == file_magic::archive)234    return false;235 236  // Universal binary handling.237#if defined(__APPLE__)238  if (MagicCode == file_magic::macho_universal_binary) {239    ObjectFileLoader ObjLoader(Path);240    auto ObjOrErr = ObjLoader.getObjectFile();241    if (!ObjOrErr) {242      consumeError(ObjOrErr.takeError());243      return false;244    }245    return isSharedLibraryObject(ObjOrErr.get());246  }247#endif248 249  // Object file inspection for PE/COFF, ELF, and Mach-O250  bool NeedsObjectInspection =251#if defined(_WIN32)252      (MagicCode == file_magic::pecoff_executable);253#elif defined(__APPLE__)254      (MagicCode == file_magic::macho_fixed_virtual_memory_shared_lib ||255       MagicCode == file_magic::macho_dynamically_linked_shared_lib ||256       MagicCode == file_magic::macho_dynamically_linked_shared_lib_stub);257#elif defined(LLVM_ON_UNIX)258#ifdef __CYGWIN__259      (MagicCode == file_magic::pecoff_executable);260#else261      (MagicCode == file_magic::elf_shared_object);262#endif263#else264#error "Unsupported platform."265#endif266 267  if (NeedsObjectInspection) {268    ObjectFileLoader ObjLoader(Path);269    auto ObjOrErr = ObjLoader.getObjectFile();270    if (!ObjOrErr) {271      consumeError(ObjOrErr.takeError());272      return false;273    }274    return isSharedLibraryObject(ObjOrErr.get());275  }276 277  LLVM_DEBUG(dbgs() << "Path is not identified as a shared library: " << Path278                    << "\n";);279  return false;280}281 282void DylibSubstitutor::configure(StringRef LoaderPath) {283  SmallString<512> ExecPath(sys::fs::getMainExecutable(nullptr, nullptr));284  sys::path::remove_filename(ExecPath);285 286  SmallString<512> LoaderDir;287  if (LoaderPath.empty()) {288    LoaderDir = ExecPath;289  } else {290    LoaderDir = LoaderPath.str();291    if (!sys::fs::is_directory(LoaderPath))292      sys::path::remove_filename(LoaderDir);293  }294 295#ifdef __APPLE__296  Placeholders["@loader_path"] = std::string(LoaderDir);297  Placeholders["@executable_path"] = std::string(ExecPath);298#else299  Placeholders["$origin"] = std::string(LoaderDir);300#endif301}302 303std::optional<std::string>304SearchPathResolver::resolve(StringRef Stem, const DylibSubstitutor &Subst,305                            DylibPathValidator &Validator) const {306  for (const auto &SP : Paths) {307    std::string Base = Subst.substitute(SP);308 309    SmallString<512> FullPath(Base);310    if (!PlaceholderPrefix.empty() &&311        Stem.starts_with_insensitive(PlaceholderPrefix))312      FullPath.append(Stem.drop_front(PlaceholderPrefix.size()));313    else314      sys::path::append(FullPath, Stem);315 316    LLVM_DEBUG(dbgs() << "SearchPathResolver::resolve FullPath = " << FullPath317                      << "\n";);318 319    if (auto Valid = Validator.validate(FullPath.str()))320      return Valid;321  }322 323  return std::nullopt;324}325 326std::optional<std::string>327DylibResolverImpl::tryWithExtensions(StringRef LibStem) const {328  LLVM_DEBUG(dbgs() << "tryWithExtensions: baseName = " << LibStem << "\n";);329  SmallVector<SmallString<256>, 8> Candidates;330 331  // Add extensions by platform332#if defined(__APPLE__)333  Candidates.emplace_back(LibStem);334  Candidates.back() += ".dylib";335#elif defined(_WIN32)336  Candidates.emplace_back(LibStem);337  Candidates.back() += ".dll";338#else339  Candidates.emplace_back(LibStem);340  Candidates.back() += ".so";341#endif342 343  // Optionally try "lib" prefix if not already there344  StringRef FileName = sys::path::filename(LibStem);345  StringRef Base = sys::path::parent_path(LibStem);346  if (!FileName.starts_with("lib")) {347    SmallString<256> WithPrefix(Base);348    if (!WithPrefix.empty())349      sys::path::append(WithPrefix, ""); // ensure separator if needed350    WithPrefix += "lib";351    WithPrefix += FileName;352 353#if defined(__APPLE__)354    WithPrefix += ".dylib";355#elif defined(_WIN32)356    WithPrefix += ".dll";357#else358    WithPrefix += ".so";359#endif360 361    Candidates.push_back(std::move(WithPrefix));362  }363 364  LLVM_DEBUG({365    dbgs() << "  Candidates to try:\n";366    for (const auto &C : Candidates)367      dbgs() << "    " << C << "\n";368  });369 370  // Try all variants using tryAllPaths371  for (const auto &Name : Candidates) {372 373    LLVM_DEBUG(dbgs() << "  Trying candidate: " << Name << "\n";);374 375    for (const auto &R : Resolvers) {376      if (auto Res = R.resolve(Name, Substitutor, Validator))377        return Res;378    }379  }380 381  LLVM_DEBUG(dbgs() << "  -> No candidate Resolved.\n";);382 383  return std::nullopt;384}385 386std::optional<std::string>387DylibResolverImpl::resolve(StringRef LibStem, bool VariateLibStem) const {388  LLVM_DEBUG(dbgs() << "Resolving library stem: " << LibStem << "\n";);389 390  // If it is an absolute path, don't try iterate over the paths.391  if (sys::path::is_absolute(LibStem)) {392    LLVM_DEBUG(dbgs() << "  -> Absolute path detected.\n";);393    return Validator.validate(LibStem);394  }395 396  if (!LibStem.starts_with_insensitive("@rpath")) {397    if (auto norm = Validator.validate(Substitutor.substitute(LibStem))) {398      LLVM_DEBUG(dbgs() << "  -> Resolved after substitution: " << *norm399                        << "\n";);400 401      return norm;402    }403  }404 405  for (const auto &R : Resolvers) {406    LLVM_DEBUG(dbgs() << "  -> Resolving via search path ... \n";);407    if (auto Result = R.resolve(LibStem, Substitutor, Validator)) {408      LLVM_DEBUG(dbgs() << "  -> Resolved via search path: " << *Result409                        << "\n";);410 411      return Result;412    }413  }414 415  // Expand libStem with paths, extensions, etc.416  // std::string foundName;417  if (VariateLibStem) {418    LLVM_DEBUG(dbgs() << "  -> Trying with extensions...\n";);419 420    if (auto Norm = tryWithExtensions(LibStem)) {421      LLVM_DEBUG(dbgs() << "  -> Resolved via tryWithExtensions: " << *Norm422                        << "\n";);423 424      return Norm;425    }426  }427 428  LLVM_DEBUG(dbgs() << "  -> Could not resolve: " << LibStem << "\n";);429 430  return std::nullopt;431}432 433#ifndef _WIN32434mode_t PathResolver::lstatCached(StringRef Path) {435  // If already cached - retun cached result436  if (auto Cache = LibPathCache->read_lstat(Path))437    return *Cache;438 439  // Not cached: perform lstat and store440  struct stat buf{};441  mode_t st_mode = (lstat(Path.str().c_str(), &buf) == -1) ? 0 : buf.st_mode;442 443  LibPathCache->insert_lstat(Path, st_mode);444 445  return st_mode;446}447 448std::optional<std::string> PathResolver::readlinkCached(StringRef Path) {449  // If already cached - retun cached result450  if (auto Cache = LibPathCache->read_link(Path))451    return Cache;452 453  // If result not in cache - call system function and cache result454  char buf[PATH_MAX];455  ssize_t len;456  if ((len = readlink(Path.str().c_str(), buf, sizeof(buf))) != -1) {457    buf[len] = '\0';458    std::string s(buf);459    LibPathCache->insert_link(Path, s);460    return s;461  }462  return std::nullopt;463}464 465void createComponent(StringRef Path, StringRef BasePath, bool BaseIsResolved,466                     SmallVector<StringRef, 16> &Component) {467  StringRef Separator = sys::path::get_separator();468  if (!BaseIsResolved) {469    if (Path[0] == '~' &&470        (Path.size() == 1 || sys::path::is_separator(Path[1]))) {471      static SmallString<128> HomeP;472      if (HomeP.str().empty())473        sys::path::home_directory(HomeP);474      StringRef(HomeP).split(Component, Separator, /*MaxSplit*/ -1,475                             /*KeepEmpty*/ false);476    } else if (BasePath.empty()) {477      static SmallString<256> CurrentPath;478      if (CurrentPath.str().empty())479        sys::fs::current_path(CurrentPath);480      StringRef(CurrentPath)481          .split(Component, Separator, /*MaxSplit*/ -1, /*KeepEmpty*/ false);482    } else {483      BasePath.split(Component, Separator, /*MaxSplit*/ -1,484                     /*KeepEmpty*/ false);485    }486  }487 488  Path.split(Component, Separator, /*MaxSplit*/ -1, /*KeepEmpty*/ false);489}490 491void normalizePathSegments(SmallVector<StringRef, 16> &PathParts) {492  SmallVector<StringRef, 16> NormalizedPath;493  for (auto &Part : PathParts) {494    if (Part == ".") {495      continue;496    } else if (Part == "..") {497      if (!NormalizedPath.empty() && NormalizedPath.back() != "..") {498        NormalizedPath.pop_back();499      } else {500        NormalizedPath.push_back("..");501      }502    } else {503      NormalizedPath.push_back(Part);504    }505  }506  PathParts.swap(NormalizedPath);507}508#endif509 510std::optional<std::string> PathResolver::realpathCached(StringRef Path,511                                                        std::error_code &EC,512                                                        StringRef Base,513                                                        bool BaseIsResolved,514                                                        long SymLoopLevel) {515  EC.clear();516 517  if (Path.empty()) {518    EC = std::make_error_code(std::errc::no_such_file_or_directory);519    LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Empty path\n";);520 521    return std::nullopt;522  }523 524  if (SymLoopLevel <= 0) {525    EC = std::make_error_code(std::errc::too_many_symbolic_link_levels);526    LLVM_DEBUG(527        dbgs() << "PathResolver::realpathCached: Too many Symlink levels: "528               << Path << "\n";);529 530    return std::nullopt;531  }532 533  // If already cached - retun cached result534  bool isRelative = sys::path::is_relative(Path);535  if (!isRelative) {536    if (auto Cached = LibPathCache->read_realpath(Path)) {537      EC = Cached->ErrnoCode;538      if (EC) {539        LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Cached (error) for "540                          << Path << "\n";);541      } else {542        LLVM_DEBUG(543            dbgs() << "PathResolver::realpathCached: Cached (success) for "544                   << Path << " => " << Cached->canonicalPath << "\n";);545      }546      return Cached->canonicalPath.empty()547                 ? std::nullopt548                 : std::make_optional(Cached->canonicalPath);549    }550  }551 552  LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Resolving path: " << Path553                    << "\n";);554 555  // If result not in cache - call system function and cache result556 557  StringRef Separator(sys::path::get_separator());558  SmallString<256> Resolved(Separator);559#ifndef _WIN32560  SmallVector<StringRef, 16> Components;561 562  if (isRelative) {563    if (BaseIsResolved) {564      Resolved.assign(Base);565      LLVM_DEBUG(dbgs() << "  Using Resolved base: " << Base << "\n";);566    }567    createComponent(Path, Base, BaseIsResolved, Components);568  } else {569    Path.split(Components, Separator, /*MaxSplit*/ -1, /*KeepEmpty*/ false);570  }571 572  normalizePathSegments(Components);573  LLVM_DEBUG({574    for (auto &C : Components)575      dbgs() << " " << C << " ";576 577    dbgs() << "\n";578  });579 580  // Handle path list items581  for (const auto &Component : Components) {582    if (Component == ".")583      continue;584    if (Component == "..") {585      // collapse "a/b/../c" to "a/c"586      size_t S = Resolved.rfind(Separator);587      if (S != llvm::StringRef::npos)588        Resolved.resize(S);589      if (Resolved.empty())590        Resolved = Separator;591      continue;592    }593 594    size_t oldSize = Resolved.size();595    sys::path::append(Resolved, Component);596    const char *ResolvedPath = Resolved.c_str();597    LLVM_DEBUG(dbgs() << "  Processing Component: " << Component << " => "598                      << ResolvedPath << "\n";);599    mode_t st_mode = lstatCached(ResolvedPath);600 601    if (S_ISLNK(st_mode)) {602      LLVM_DEBUG(dbgs() << "    Found symlink: " << ResolvedPath << "\n";);603 604      auto SymlinkOpt = readlinkCached(ResolvedPath);605      if (!SymlinkOpt) {606        EC = std::make_error_code(std::errc::no_such_file_or_directory);607        LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{"", EC});608        LLVM_DEBUG(dbgs() << "    Failed to read symlink: " << ResolvedPath609                          << "\n";);610 611        return std::nullopt;612      }613 614      StringRef Symlink = *SymlinkOpt;615      LLVM_DEBUG(dbgs() << "    Symlink points to: " << Symlink << "\n";);616 617      std::string resolvedBase = "";618      if (sys::path::is_relative(Symlink)) {619        Resolved.resize(oldSize);620        resolvedBase = Resolved.str().str();621      }622 623      auto RealSymlink =624          realpathCached(Symlink, EC, resolvedBase,625                         /*BaseIsResolved=*/true, SymLoopLevel - 1);626      if (!RealSymlink) {627        LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{"", EC});628        LLVM_DEBUG(dbgs() << "    Failed to resolve symlink target: " << Symlink629                          << "\n";);630 631        return std::nullopt;632      }633 634      Resolved.assign(*RealSymlink);635      LLVM_DEBUG(dbgs() << "    Symlink Resolved to: " << Resolved << "\n";);636 637    } else if (st_mode == 0) {638      EC = std::make_error_code(std::errc::no_such_file_or_directory);639      LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{"", EC});640      LLVM_DEBUG(dbgs() << "    Component does not exist: " << ResolvedPath641                        << "\n";);642 643      return std::nullopt;644    }645  }646#else647  EC = sys::fs::real_path(Path, Resolved); // Windows fallback648#endif649 650  std::string Canonical = Resolved.str().str();651  {652    LibPathCache->insert_realpath(Path, LibraryPathCache::PathInfo{653                                            Canonical,654                                            std::error_code() // success655                                        });656  }657  LLVM_DEBUG(dbgs() << "PathResolver::realpathCached: Final Resolved: " << Path658                    << " => " << Canonical << "\n";);659  return Canonical;660}661 662void LibraryScanHelper::addBasePath(const std::string &Path, PathType K) {663  std::error_code EC;664  std::string Canon = resolveCanonical(Path, EC);665  if (EC) {666    LLVM_DEBUG(667        dbgs()668            << "LibraryScanHelper::addBasePath: Failed to canonicalize path: "669            << Path << "\n";);670    return;671  }672  std::unique_lock<std::shared_mutex> Lock(Mtx);673  if (LibSearchPaths.count(Canon)) {674    LLVM_DEBUG(dbgs() << "LibraryScanHelper::addBasePath: Already added: "675                      << Canon << "\n";);676    return;677  }678  K = K == PathType::Unknown ? classifyKind(Canon) : K;679  auto SP = std::make_shared<LibrarySearchPath>(Canon, K);680  LibSearchPaths[Canon] = SP;681 682  if (K == PathType::User) {683    LLVM_DEBUG(dbgs() << "LibraryScanHelper::addBasePath: Added User path: "684                      << Canon << "\n";);685    UnscannedUsr.push_back(StringRef(SP->BasePath));686  } else {687    LLVM_DEBUG(dbgs() << "LibraryScanHelper::addBasePath: Added System path: "688                      << Canon << "\n";);689    UnscannedSys.push_back(StringRef(SP->BasePath));690  }691}692 693std::vector<std::shared_ptr<LibrarySearchPath>>694LibraryScanHelper::getNextBatch(PathType K, size_t BatchSize) {695  std::vector<std::shared_ptr<LibrarySearchPath>> Result;696  auto &Queue = (K == PathType::User) ? UnscannedUsr : UnscannedSys;697 698  std::unique_lock<std::shared_mutex> Lock(Mtx);699 700  while (!Queue.empty() && (BatchSize == 0 || Result.size() < BatchSize)) {701    StringRef Base = Queue.front();702    auto It = LibSearchPaths.find(Base);703    if (It != LibSearchPaths.end()) {704      auto &SP = It->second;705      ScanState Expected = ScanState::NotScanned;706      if (SP->State.compare_exchange_strong(Expected, ScanState::Scanning)) {707        Result.push_back(SP);708      }709    }710    Queue.pop_front();711  }712 713  return Result;714}715 716bool LibraryScanHelper::isTrackedBasePath(StringRef Path) const {717  std::error_code EC;718  std::string Canon = resolveCanonical(Path, EC);719  if (EC)720    return false;721 722  std::shared_lock<std::shared_mutex> Lock(Mtx);723  return LibSearchPaths.count(Canon) > 0;724}725 726bool LibraryScanHelper::leftToScan(PathType K) const {727  std::shared_lock<std::shared_mutex> Lock(Mtx);728  for (const auto &KV : LibSearchPaths) {729    const auto &SP = KV.second;730    if (SP->Kind == K && SP->State == ScanState::NotScanned)731      return true;732  }733  return false;734}735 736void LibraryScanHelper::resetToScan() {737  std::shared_lock<std::shared_mutex> Lock(Mtx);738 739  for (auto &[_, SP] : LibSearchPaths) {740    ScanState Expected = ScanState::Scanned;741 742    if (!SP->State.compare_exchange_strong(Expected, ScanState::NotScanned))743      continue;744 745    auto &TargetList =746        (SP->Kind == PathType::User) ? UnscannedUsr : UnscannedSys;747    TargetList.emplace_back(SP->BasePath);748  }749}750 751std::vector<std::shared_ptr<LibrarySearchPath>>752LibraryScanHelper::getAllUnits() const {753  std::shared_lock<std::shared_mutex> Lock(Mtx);754  std::vector<std::shared_ptr<LibrarySearchPath>> Result;755  Result.reserve(LibSearchPaths.size());756  for (const auto &[_, SP] : LibSearchPaths) {757    Result.push_back(SP);758  }759  return Result;760}761 762std::string LibraryScanHelper::resolveCanonical(StringRef Path,763                                                std::error_code &EC) const {764  auto Canon = LibPathResolver->resolve(Path, EC);765  return EC ? Path.str() : *Canon;766}767 768PathType LibraryScanHelper::classifyKind(StringRef Path) const {769  // Detect home directory770  const char *Home = getenv("HOME");771  if (Home && Path.starts_with(Home))772    return PathType::User;773 774  static const std::array<std::string, 5> UserPrefixes = {775      "/usr/local",    // often used by users for manual installs776      "/opt/homebrew", // common on macOS777      "/opt/local",    // MacPorts778      "/home",         // Linux home dirs779      "/Users",        // macOS user dirs780  };781 782  for (const auto &Prefix : UserPrefixes) {783    if (Path.starts_with(Prefix))784      return PathType::User;785  }786 787  return PathType::System;788}789 790Expected<LibraryDepsInfo> parseMachODeps(const object::MachOObjectFile &Obj) {791  LibraryDepsInfo Libdeps;792  LLVM_DEBUG(dbgs() << "Parsing Mach-O dependencies...\n";);793  for (const auto &Command : Obj.load_commands()) {794    switch (Command.C.cmd) {795    case MachO::LC_LOAD_DYLIB: {796      MachO::dylib_command dylibCmd = Obj.getDylibIDLoadCommand(Command);797      const char *name = Command.Ptr + dylibCmd.dylib.name;798      Libdeps.addDep(name);799      LLVM_DEBUG(dbgs() << "  Found LC_LOAD_DYLIB: " << name << "\n";);800    } break;801    case MachO::LC_LOAD_WEAK_DYLIB:802    case MachO::LC_REEXPORT_DYLIB:803    case MachO::LC_LOAD_UPWARD_DYLIB:804    case MachO::LC_LAZY_LOAD_DYLIB:805      break;806    case MachO::LC_RPATH: {807      // Extract RPATH808      MachO::rpath_command rpathCmd = Obj.getRpathCommand(Command);809      const char *rpath = Command.Ptr + rpathCmd.path;810      LLVM_DEBUG(dbgs() << "  Found LC_RPATH: " << rpath << "\n";);811 812      SmallVector<StringRef, 4> RawPaths;813      SplitString(StringRef(rpath), RawPaths,814                  sys::EnvPathSeparator == ':' ? ":" : ";");815 816      for (const auto &raw : RawPaths) {817        Libdeps.addRPath(raw.str()); // Convert to std::string818        LLVM_DEBUG(dbgs() << "    Parsed RPATH entry: " << raw << "\n";);819      }820      break;821    }822    }823  }824 825  return Expected<LibraryDepsInfo>(std::move(Libdeps));826}827 828template <class ELFT>829static Expected<StringRef> getDynamicStrTab(const object::ELFFile<ELFT> &Elf) {830  auto DynamicEntriesOrError = Elf.dynamicEntries();831  if (!DynamicEntriesOrError)832    return DynamicEntriesOrError.takeError();833 834  for (const typename ELFT::Dyn &Dyn : *DynamicEntriesOrError) {835    if (Dyn.d_tag == ELF::DT_STRTAB) {836      auto MappedAddrOrError = Elf.toMappedAddr(Dyn.getPtr());837      if (!MappedAddrOrError)838        return MappedAddrOrError.takeError();839      return StringRef(reinterpret_cast<const char *>(*MappedAddrOrError));840    }841  }842 843  // If the dynamic segment is not present, we fall back on the sections.844  auto SectionsOrError = Elf.sections();845  if (!SectionsOrError)846    return SectionsOrError.takeError();847 848  for (const typename ELFT::Shdr &Sec : *SectionsOrError) {849    if (Sec.sh_type == ELF::SHT_DYNSYM)850      return Elf.getStringTableForSymtab(Sec);851  }852 853  return make_error<StringError>("dynamic string table not found",854                                 inconvertibleErrorCode());855}856 857template <typename ELFT>858Expected<LibraryDepsInfo> parseELF(const object::ELFFile<ELFT> &Elf) {859  LibraryDepsInfo Deps;860  Expected<StringRef> StrTabOrErr = getDynamicStrTab(Elf);861  if (!StrTabOrErr)862    return StrTabOrErr.takeError();863 864  const char *Data = StrTabOrErr->data();865 866  auto DynamicEntriesOrError = Elf.dynamicEntries();867  if (!DynamicEntriesOrError) {868    return DynamicEntriesOrError.takeError();869  }870 871  for (const typename ELFT::Dyn &Dyn : *DynamicEntriesOrError) {872    switch (Dyn.d_tag) {873    case ELF::DT_NEEDED:874      Deps.addDep(Data + Dyn.d_un.d_val);875      break;876    case ELF::DT_RPATH: {877      SmallVector<StringRef, 4> RawPaths;878      SplitString(Data + Dyn.d_un.d_val, RawPaths,879                  sys::EnvPathSeparator == ':' ? ":" : ";");880      for (const auto &raw : RawPaths)881        Deps.addRPath(raw.str());882      break;883    }884    case ELF::DT_RUNPATH: {885      SmallVector<StringRef, 4> RawPaths;886      SplitString(Data + Dyn.d_un.d_val, RawPaths,887                  sys::EnvPathSeparator == ':' ? ":" : ";");888      for (const auto &raw : RawPaths)889        Deps.addRunPath(raw.str());890      break;891    }892    case ELF::DT_FLAGS_1:893      // Check if this is not a pie executable.894      if (Dyn.d_un.d_val & ELF::DF_1_PIE)895        Deps.isPIE = true;896      break;897      // (Dyn.d_tag == ELF::DT_NULL) continue;898      // (Dyn.d_tag == ELF::DT_AUXILIARY || Dyn.d_tag == ELF::DT_FILTER)899    default:900      break;901    }902  }903 904  return Expected<LibraryDepsInfo>(std::move(Deps));905}906 907Expected<LibraryDepsInfo> parseELFDeps(const object::ELFObjectFileBase &Obj) {908  using namespace object;909  LLVM_DEBUG(dbgs() << "parseELFDeps: Detected ELF object\n";);910  if (const auto *ELF = dyn_cast<ELF32LEObjectFile>(&Obj))911    return parseELF(ELF->getELFFile());912  else if (const auto *ELF = dyn_cast<ELF32BEObjectFile>(&Obj))913    return parseELF(ELF->getELFFile());914  else if (const auto *ELF = dyn_cast<ELF64LEObjectFile>(&Obj))915    return parseELF(ELF->getELFFile());916  else if (const auto *ELF = dyn_cast<ELF64BEObjectFile>(&Obj))917    return parseELF(ELF->getELFFile());918 919  LLVM_DEBUG(dbgs() << "parseELFDeps: Unknown ELF format\n";);920  return createStringError(std::errc::not_supported, "Unknown ELF format");921}922 923Expected<LibraryDepsInfo> LibraryScanner::extractDeps(StringRef FilePath) {924  LLVM_DEBUG(dbgs() << "extractDeps: Attempting to open file " << FilePath925                    << "\n";);926 927  ObjectFileLoader ObjLoader(FilePath);928  auto ObjOrErr = ObjLoader.getObjectFile();929  if (!ObjOrErr) {930    LLVM_DEBUG(dbgs() << "extractDeps: Failed to open " << FilePath << "\n";);931    return ObjOrErr.takeError();932  }933 934  object::ObjectFile *Obj = &ObjOrErr.get();935 936  if (auto *elfObj = dyn_cast<object::ELFObjectFileBase>(Obj)) {937    LLVM_DEBUG(dbgs() << "extractDeps: File " << FilePath938                      << " is an ELF object\n";);939 940    return parseELFDeps(*elfObj);941  }942 943  if (auto *macho = dyn_cast<object::MachOObjectFile>(Obj)) {944    LLVM_DEBUG(dbgs() << "extractDeps: File " << FilePath945                      << " is a Mach-O object\n";);946    return parseMachODeps(*macho);947  }948 949  if (Obj->isCOFF()) {950    // TODO: COFF support951    return LibraryDepsInfo();952  }953 954  LLVM_DEBUG(dbgs() << "extractDeps: Unsupported binary format for file "955                    << FilePath << "\n";);956  return createStringError(inconvertibleErrorCode(),957                           "Unsupported binary format: %s",958                           FilePath.str().c_str());959}960 961std::optional<std::string> LibraryScanner::shouldScan(StringRef FilePath) {962  std::error_code EC;963 964  LLVM_DEBUG(dbgs() << "[shouldScan] Checking: " << FilePath << "\n";);965 966  // [1] Check file existence early967  if (!sys::fs::exists(FilePath)) {968    LLVM_DEBUG(dbgs() << "  -> Skipped: file does not exist.\n";);969 970    return std::nullopt;971  }972 973  // [2] Resolve to canonical path974  auto CanonicalPathOpt = ScanHelper.resolve(FilePath, EC);975  if (EC || !CanonicalPathOpt) {976    LLVM_DEBUG(dbgs() << "  -> Skipped: failed to resolve path (EC="977                      << EC.message() << ").\n";);978 979    return std::nullopt;980  }981 982  const std::string &CanonicalPath = *CanonicalPathOpt;983  LLVM_DEBUG(dbgs() << "  -> Canonical path: " << CanonicalPath << "\n");984 985  // [3] Check if it's a directory — skip directories986  if (sys::fs::is_directory(CanonicalPath)) {987    LLVM_DEBUG(dbgs() << "  -> Skipped: path is a directory.\n";);988 989    return std::nullopt;990  }991 992  // [4] Skip if it's not a shared library.993  if (!DylibPathValidator::isSharedLibrary(CanonicalPath)) {994    LLVM_DEBUG(dbgs() << "  -> Skipped: not a shared library.\n";);995    return std::nullopt;996  }997 998  // [5] Skip if we've already seen this path (via cache)999  if (ScanHelper.hasSeenOrMark(CanonicalPath)) {1000    LLVM_DEBUG(dbgs() << "  -> Skipped: already seen.\n";);1001 1002    return std::nullopt;1003  }1004 1005  // [6] Already tracked in LibraryManager?1006  if (LibMgr.hasLibrary(CanonicalPath)) {1007    LLVM_DEBUG(dbgs() << "  -> Skipped: already tracked by LibraryManager.\n";);1008 1009    return std::nullopt;1010  }1011 1012  // [7] Run user-defined hook (default: always true)1013  if (!ShouldScanCall(CanonicalPath)) {1014    LLVM_DEBUG(dbgs() << "  -> Skipped: user-defined hook rejected.\n";);1015 1016    return std::nullopt;1017  }1018 1019  LLVM_DEBUG(dbgs() << "  -> Accepted: ready to scan " << CanonicalPath1020                    << "\n";);1021  return CanonicalPath;1022}1023 1024void LibraryScanner::handleLibrary(StringRef FilePath, PathType K, int level) {1025  LLVM_DEBUG(dbgs() << "LibraryScanner::handleLibrary: Scanning: " << FilePath1026                    << ", level=" << level << "\n";);1027  auto CanonPathOpt = shouldScan(FilePath);1028  if (!CanonPathOpt) {1029    LLVM_DEBUG(dbgs() << "  Skipped (shouldScan returned false): " << FilePath1030                      << "\n";);1031 1032    return;1033  }1034  const std::string CanonicalPath = *CanonPathOpt;1035 1036  auto DepsOrErr = extractDeps(CanonicalPath);1037  if (!DepsOrErr) {1038    LLVM_DEBUG(dbgs() << "  Failed to extract deps for: " << CanonicalPath1039                      << "\n";);1040    handleError(DepsOrErr.takeError());1041    return;1042  }1043 1044  LibraryDepsInfo &Deps = *DepsOrErr;1045 1046  LLVM_DEBUG({1047    dbgs() << "    Found deps : \n";1048    for (const auto &dep : Deps.deps)1049      dbgs() << "        : " << dep << "\n";1050    dbgs() << "    Found @rpath : " << Deps.rpath.size() << "\n";1051    for (const auto &r : Deps.rpath)1052      dbgs() << "     : " << r << "\n";1053    dbgs() << "    Found @runpath : \n";1054    for (const auto &r : Deps.runPath)1055      dbgs() << "     : " << r << "\n";1056  });1057 1058  if (Deps.isPIE && level == 0) {1059    LLVM_DEBUG(dbgs() << "  Skipped PIE executable at top level: "1060                      << CanonicalPath << "\n";);1061 1062    return;1063  }1064 1065  bool Added = LibMgr.addLibrary(CanonicalPath, K);1066  if (!Added) {1067    LLVM_DEBUG(dbgs() << "  Already added: " << CanonicalPath << "\n";);1068    return;1069  }1070 1071  // Heuristic 1: No RPATH/RUNPATH, skip deps1072  if (Deps.rpath.empty() && Deps.runPath.empty()) {1073    LLVM_DEBUG(1074        dbgs() << "LibraryScanner::handleLibrary: Skipping deps (Heuristic1): "1075               << CanonicalPath << "\n";);1076    return;1077  }1078 1079  // Heuristic 2: All RPATH and RUNPATH already tracked1080  auto allTracked = [&](const auto &Paths) {1081    LLVM_DEBUG(dbgs() << "   Checking : " << Paths.size() << "\n";);1082    return std::all_of(Paths.begin(), Paths.end(), [&](StringRef P) {1083      LLVM_DEBUG(dbgs() << "      Checking isTrackedBasePath : " << P << "\n";);1084      return ScanHelper.isTrackedBasePath(1085          DylibResolver::resolvelinkerFlag(P, CanonicalPath));1086    });1087  };1088 1089  if (allTracked(Deps.rpath) && allTracked(Deps.runPath)) {1090    LLVM_DEBUG(1091        dbgs() << "LibraryScanner::handleLibrary: Skipping deps (Heuristic2): "1092               << CanonicalPath << "\n";);1093    return;1094  }1095 1096  DylibPathValidator Validator(ScanHelper.getPathResolver());1097  DylibResolver Resolver(Validator);1098  Resolver.configure(CanonicalPath,1099                     {{Deps.rpath, SearchPathType::RPath},1100                      {ScanHelper.getSearchPaths(), SearchPathType::UsrOrSys},1101                      {Deps.runPath, SearchPathType::RunPath}});1102  for (StringRef Dep : Deps.deps) {1103    LLVM_DEBUG(dbgs() << "  Resolving dep: " << Dep << "\n";);1104    auto DepFullOpt = Resolver.resolve(Dep);1105    if (!DepFullOpt) {1106      LLVM_DEBUG(dbgs() << "    Failed to resolve dep: " << Dep << "\n";);1107 1108      continue;1109    }1110    LLVM_DEBUG(dbgs() << "    Resolved dep to: " << *DepFullOpt << "\n";);1111 1112    handleLibrary(*DepFullOpt, K, level + 1);1113  }1114}1115 1116void LibraryScanner::scanBaseDir(std::shared_ptr<LibrarySearchPath> SP) {1117  if (!sys::fs::is_directory(SP->BasePath) || SP->BasePath.empty()) {1118    LLVM_DEBUG(1119        dbgs() << "LibraryScanner::scanBaseDir: Invalid or empty basePath: "1120               << SP->BasePath << "\n";);1121    return;1122  }1123 1124  LLVM_DEBUG(dbgs() << "LibraryScanner::scanBaseDir: Scanning directory: "1125                    << SP->BasePath << "\n";);1126  std::error_code EC;1127 1128  SP->State.store(ScanState::Scanning);1129 1130  for (sys::fs::directory_iterator It(SP->BasePath, EC), end; It != end && !EC;1131       It.increment(EC)) {1132    auto Entry = *It;1133    if (!Entry.status())1134      continue;1135 1136    auto Status = *Entry.status();1137    if (sys::fs::is_regular_file(Status) || sys::fs::is_symlink_file(Status)) {1138      LLVM_DEBUG(dbgs() << "  Found file: " << Entry.path() << "\n";);1139      // async support ?1140      handleLibrary(Entry.path(), SP->Kind);1141    }1142  }1143 1144  SP->State.store(ScanState::Scanned);1145}1146 1147void LibraryScanner::scanNext(PathType K, size_t BatchSize) {1148  LLVM_DEBUG(dbgs() << "LibraryScanner::scanNext: Scanning next batch of size "1149                    << BatchSize << " for kind "1150                    << (K == PathType::User ? "User" : "System") << "\n";);1151 1152  auto SearchPaths = ScanHelper.getNextBatch(K, BatchSize);1153  for (auto &SP : SearchPaths) {1154    LLVM_DEBUG(dbgs() << "  Scanning unit with basePath: " << SP->BasePath1155                      << "\n";);1156 1157    scanBaseDir(SP);1158  }1159}1160 1161} // end namespace llvm::orc1162