brintos

brintos / llvm-project-archived public Read only

0
0
Text · 24.2 KiB · 441344b Raw
763 lines · cpp
1//===- LibraryResolverTest.cpp - Unit tests for LibraryResolver -===//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/LibraryResolver.h"10#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"11#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h"12#include "llvm/ObjectYAML/MachOYAML.h"13#include "llvm/ObjectYAML/yaml2obj.h"14#include "llvm/Support/FileSystem.h"15#include "llvm/Support/MemoryBuffer.h"16#include "llvm/Support/Path.h"17#include "llvm/Support/YAMLParser.h"18#include "llvm/Support/YAMLTraits.h"19#include "llvm/Support/raw_ostream.h"20 21#include "llvm/Testing/Support/SupportHelpers.h"22 23#include "gtest/gtest.h"24 25#include <algorithm>26#include <string>27#include <vector>28 29using namespace llvm;30using namespace llvm::orc;31 32#if defined(__APPLE__) || defined(__linux__)33// TODO: Add COFF (Windows) support for these tests.34// this facility also works correctly on Windows (COFF),35// so we should eventually enable and run these tests for that platform as well.36namespace {37 38#if defined(__APPLE__)39constexpr const char *ext = ".dylib";40#elif defined(_WIN32)41constexpr const char *ext = ".dll";42#else43constexpr const char *ext = ".so";44#endif45 46bool EnvReady = false;47 48Triple getTargetTriple() {49  auto JTMB = JITTargetMachineBuilder::detectHost();50  if (!JTMB) {51    consumeError(JTMB.takeError());52    return Triple();53  }54  return JTMB->getTargetTriple();55}56 57static bool CheckHostSupport() {58  auto Triple = getTargetTriple();59  // TODO: Extend support to COFF (Windows) once test setup and YAML conversion60  // are verified.61  if (!Triple.isOSBinFormatMachO() &&62      !(Triple.isOSBinFormatELF() && Triple.getArch() == Triple::x86_64))63    return false;64 65  return true;66}67 68std::string getYamlFilePlatformExt() {69  auto Triple = getTargetTriple();70  if (Triple.isOSBinFormatMachO())71    return "_macho";72  else if (Triple.isOSBinFormatELF())73    return "_linux";74 75  return "";76}77 78unsigned getYamlDocNum() {79  // auto Triple = getTargetTriple();80  // if (Triple.isOSBinFormatELF())81  //   return 1;82 83  return 1;84}85 86class LibraryTestEnvironment : public ::testing::Environment {87  std::vector<std::string> CreatedDylibsDir;88  std::vector<std::string> CreatedDylibs;89  SmallVector<char, 128> DirPath;90 91public:92  void SetUp() override {93    if (!CheckHostSupport()) {94      EnvReady = false;95      return;96    }97 98    StringRef ThisFile = __FILE__;99    SmallVector<char, 128> InputDirPath(ThisFile.begin(), ThisFile.end());100    sys::path::remove_filename(InputDirPath);101    sys::path::append(InputDirPath, "Inputs");102    if (!sys::fs::exists(InputDirPath))103      return;104 105    SmallString<512> ExecPath(sys::fs::getMainExecutable(nullptr, nullptr));106    sys::path::remove_filename(ExecPath);107    SmallString<128> UniqueDir(ExecPath);108    std::error_code EC = sys::fs::createUniqueDirectory(UniqueDir, DirPath);109 110    if (EC)111      return;112 113    // given yamlPath + DylibPath, validate + convert114    auto processYamlToDylib = [&](const SmallVector<char, 128> &YamlPath,115                                  const SmallVector<char, 128> &DylibPath,116                                  unsigned DocNum) -> bool {117      if (!sys::fs::exists(YamlPath)) {118        errs() << "YAML file missing: "119               << StringRef(YamlPath.data(), YamlPath.size()) << "\n";120        EnvReady = false;121        return false;122      }123 124      auto BufOrErr = MemoryBuffer::getFile(YamlPath);125      if (!BufOrErr) {126        errs() << "Failed to read "127               << StringRef(YamlPath.data(), YamlPath.size()) << ": "128               << BufOrErr.getError().message() << "\n";129        EnvReady = false;130        return false;131      }132 133      yaml::Input yin(BufOrErr->get()->getBuffer());134      std::error_code EC;135      raw_fd_ostream outFile(StringRef(DylibPath.data(), DylibPath.size()), EC,136                             sys::fs::OF_None);137 138      if (EC) {139        errs() << "Failed to open "140               << StringRef(DylibPath.data(), DylibPath.size())141               << " for writing: " << EC.message() << "\n";142        EnvReady = false;143        return false;144      }145 146      if (!yaml::convertYAML(147              yin, outFile,148              [](const Twine &M) {149                // Handle or ignore errors here150                errs() << "Yaml Error :" << M << "\n";151              },152              DocNum)) {153        errs() << "Failed to convert "154               << StringRef(YamlPath.data(), YamlPath.size()) << " to "155               << StringRef(DylibPath.data(), DylibPath.size()) << "\n";156        EnvReady = false;157        return false;158      }159 160      CreatedDylibsDir.push_back(std::string(sys::path::parent_path(161          StringRef(DylibPath.data(), DylibPath.size()))));162      CreatedDylibs.push_back(std::string(DylibPath.begin(), DylibPath.end()));163      return true;164    };165 166    std::vector<const char *> LibDirs = {"Z", "A", "B", "C"};167 168    unsigned DocNum = getYamlDocNum();169    std::string YamlPltExt = getYamlFilePlatformExt();170    for (const auto &LibdirName : LibDirs) {171      // YAML path172      SmallVector<char, 128> YamlPath(InputDirPath.begin(), InputDirPath.end());173      SmallVector<char, 128> YamlFileName;174      YamlFileName.append(LibdirName, LibdirName + strlen(LibdirName));175      YamlFileName.append(YamlPltExt.begin(), YamlPltExt.end());176      sys::path::append(YamlPath, LibdirName, YamlFileName);177      sys::path::replace_extension(YamlPath, ".yaml");178 179      // dylib path180      SmallVector<char, 128> DylibPath(DirPath.begin(), DirPath.end());181      SmallVector<char, 128> DylibFileName;182      StringRef prefix("lib");183      DylibFileName.append(prefix.begin(), prefix.end());184      DylibFileName.append(LibdirName, LibdirName + strlen(LibdirName));185 186      sys::path::append(DylibPath, LibdirName);187      if (!sys::fs::exists(DylibPath)) {188        auto EC = sys::fs::create_directory(DylibPath);189        if (EC)190          return;191      }192      sys::path::append(DylibPath, DylibFileName);193      sys::path::replace_extension(DylibPath, ext);194      if (!processYamlToDylib(YamlPath, DylibPath, DocNum))195        return;196    }197 198    EnvReady = true;199  }200 201  void TearDown() override { sys::fs::remove_directories(DirPath); }202 203  std::string getBaseDir() const {204    return std::string(DirPath.begin(), DirPath.end());205  }206 207  std::vector<std::string> getDylibPaths() const { return CreatedDylibs; }208};209 210static LibraryTestEnvironment *GlobalEnv =211    static_cast<LibraryTestEnvironment *>(212        ::testing::AddGlobalTestEnvironment(new LibraryTestEnvironment()));213 214inline std::string libPath(const std::string &BaseDir,215                           const std::string &name) {216#if defined(__APPLE__)217  return BaseDir + "/" + name + ".dylib";218#elif defined(_WIN32)219  return BaseDir + "/" + name + ".dll";220#else221  return BaseDir + "/" + name + ".so";222#endif223}224 225inline std::string withext(const std::string &lib) {226  SmallString<128> P(lib);227  sys::path::replace_extension(P, ext);228  return P.str().str();229}230 231inline std::string platformSymbolName(const std::string &name) {232#if defined(__APPLE__)233  return "_" + name; // macOS prepends underscore234#else235  return name;236#endif237}238 239struct TestLibrary {240  std::string path;241  std::vector<std::string> Syms;242};243 244class LibraryResolverIT : public ::testing::Test {245protected:246  std::string BaseDir;247  std::unordered_map<std::string, TestLibrary> libs;248 249  void addLib(const std::string &name) {250    SmallString<512> path;251    std::error_code EC =252        sys::fs::real_path(libPath(BaseDir, name + "/lib" + name), path);253    if (EC || path.empty() || !sys::fs::exists(path))254      GTEST_SKIP();255    libs[name] = {path.str().str(), {platformSymbolName("say" + name)}};256  }257 258  void SetUp() override {259    if (!EnvReady || GlobalEnv == nullptr)260      GTEST_SKIP() << "Skipping test: environment setup failed.";261 262    {263      SmallString<512> path;264      std::error_code EC = sys::fs::real_path(GlobalEnv->getBaseDir(), path);265      if (path.empty() || EC)266        GTEST_SKIP() << "Base directory resolution failed: " << EC.message();267      BaseDir = path.str().str();268    }269 270    for (const auto &P : GlobalEnv->getDylibPaths()) {271      if (!sys::fs::exists(P))272        GTEST_SKIP() << "Missing dylib path: " << P;273    }274 275    const std::vector<std::string> libNames = {"A", "B", "C", "Z"};276    for (const auto &name : libNames)277      addLib(name);278 279    if (!EnvReady)280      GTEST_SKIP() << "Skipping test: environment setup failed.";281  }282 283  const std::vector<std::string> &sym(const std::string &key) {284    return libs[key].Syms;285  }286  const std::string &lib(const std::string &key) { return libs[key].path; }287  const std::string libdir(const std::string &key) {288    SmallString<512> P(libs[key].path);289    sys::path::remove_filename(P);290    return P.str().str();291  }292  const std::string libname(const std::string &key) {293    return sys::path::filename(libs[key].path).str();294  }295};296 297// Helper: allow either "sayA" or "_sayA" depending on how your298// SymbolEnumerator reports.299static bool matchesEitherUnderscore(const std::string &got,300                                    const std::string &bare) {301  return got == bare || got == ("_" + bare);302}303 304// Helper: normalize path ending check (we only care that it resolved to the305// right dylib)306static bool endsWith(const std::string &s, const std::string &suffix) {307  if (s.size() < suffix.size())308    return false;309  return std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());310}311 312TEST_F(LibraryResolverIT, EnumerateSymbols_ExportsOnly_DefaultFlags) {313  const std::string libC = lib("C");314  SymbolEnumeratorOptions Opts = SymbolEnumeratorOptions::defaultOptions();315 316  std::vector<std::string> seen;317  auto onEach = [&](llvm::StringRef sym) -> EnumerateResult {318    seen.emplace_back(sym.str());319    return EnumerateResult::Continue;320  };321 322  ASSERT_TRUE(SymbolEnumerator::enumerateSymbols(libC, onEach, Opts));323 324  // sayC is exported, others are undefined → only sayC expected325  EXPECT_TRUE(any_of(seen, [&](const std::string &s) {326    return matchesEitherUnderscore(s, "sayC");327  }));328  EXPECT_FALSE(any_of(seen, [&](const std::string &s) {329    return matchesEitherUnderscore(s, "sayA");330  }));331  EXPECT_FALSE(any_of(seen, [&](const std::string &s) {332    return matchesEitherUnderscore(s, "sayB");333  }));334}335 336TEST_F(LibraryResolverIT, EnumerateSymbols_IncludesUndefineds) {337  const std::string libC = lib("C");338 339  SymbolEnumeratorOptions Opts;340  Opts.FilterFlags =341      SymbolEnumeratorOptions::IgnoreWeak |342      SymbolEnumeratorOptions::IgnoreIndirect; // no IgnoreUndefined343 344  std::vector<std::string> seen;345  auto onEach = [&](llvm::StringRef sym) -> EnumerateResult {346    seen.emplace_back(sym.str());347    return EnumerateResult::Continue;348  };349 350  ASSERT_TRUE(SymbolEnumerator::enumerateSymbols(libC, onEach, Opts));351 352  // Now we should see both sayC (export) and the undefined refs sayA, sayB,353  // sayZ354  EXPECT_TRUE(any_of(seen, [&](const std::string &s) {355    return matchesEitherUnderscore(s, "sayC");356  }));357  EXPECT_TRUE(any_of(seen, [&](const std::string &s) {358    return matchesEitherUnderscore(s, "sayA");359  }));360  EXPECT_TRUE(any_of(seen, [&](const std::string &s) {361    return matchesEitherUnderscore(s, "sayB");362  }));363}364 365// Full resolution via LibraryResolutionDriver/LibraryResolver ---366TEST_F(LibraryResolverIT, DriverResolvesSymbolsToCorrectLibraries) {367  // Create the resolver from real base paths (our fixtures dir)368  auto Stup = LibraryResolver::Setup::create({BaseDir});369 370  // Full system behavior: no mocks371  auto Driver = LibraryResolutionDriver::create(Stup);372  ASSERT_NE(Driver, nullptr);373 374  // Tell the Driver about the scan path kinds (User/System) as your375  // production code expects.376  Driver->addScanPath(libdir("A"), PathType::User);377  Driver->addScanPath(libdir("B"), PathType::User);378  Driver->addScanPath(libdir("Z"), PathType::User);379 380  // Symbols to resolve (bare names; class handles underscore differences381  // internally)382  std::vector<std::string> Syms = {platformSymbolName("sayA"),383                                   platformSymbolName("sayB"),384                                   platformSymbolName("sayZ")};385 386  bool CallbackRan = false;387  Driver->resolveSymbols(Syms, [&](SymbolQuery &Q) {388    CallbackRan = true;389 390    // sayA should resolve to A.dylib391    {392      auto lib = Q.getResolvedLib(platformSymbolName("sayA"));393      ASSERT_TRUE(lib.has_value()) << "sayA should be resolved";394      EXPECT_TRUE(endsWith(lib->str(), libname("A")))395          << "sayA resolved to: " << lib->str();396    }397 398    // sayB should resolve to B.dylib399    {400      auto lib = Q.getResolvedLib(platformSymbolName("sayB"));401      ASSERT_TRUE(lib.has_value()) << "sayB should be resolved";402      EXPECT_TRUE(endsWith(lib->str(), libname("B")))403          << "sayB resolved to: " << lib->str();404    }405 406    // sayZ should resolve to B.dylib407    {408      auto lib = Q.getResolvedLib(platformSymbolName("sayZ"));409      ASSERT_TRUE(lib.has_value()) << "sayZ should be resolved";410      EXPECT_TRUE(endsWith(lib->str(), libname("Z")))411          << "sayZ resolved to: " << lib->str();412    }413 414    EXPECT_TRUE(Q.allResolved());415  });416 417  EXPECT_TRUE(CallbackRan);418}419 420// stress SymbolQuery with the real resolve flow421// And resolve libC dependency libA, libB, libZ ---422TEST_F(LibraryResolverIT, ResolveManySymbols) {423  auto Stup = LibraryResolver::Setup::create({BaseDir});424  auto Driver = LibraryResolutionDriver::create(Stup);425  ASSERT_NE(Driver, nullptr);426  Driver->addScanPath(libdir("C"), PathType::User);427 428  // Many duplicates to provoke concurrent updates inside SymbolQuery429  std::vector<std::string> Syms = {430      platformSymbolName("sayA"), platformSymbolName("sayB"),431      platformSymbolName("sayA"), platformSymbolName("sayB"),432      platformSymbolName("sayZ"), platformSymbolName("sayZ"),433      platformSymbolName("sayZ"), platformSymbolName("sayZ"),434      platformSymbolName("sayA"), platformSymbolName("sayB"),435      platformSymbolName("sayA"), platformSymbolName("sayB")};436 437  bool CallbackRan = false;438  Driver->resolveSymbols(Syms, [&](SymbolQuery &Q) {439    CallbackRan = true;440    EXPECT_TRUE(Q.isResolved(platformSymbolName("sayA")));441    EXPECT_TRUE(Q.isResolved(platformSymbolName("sayB")));442    EXPECT_TRUE(Q.isResolved(platformSymbolName("sayZ")));443 444    auto A = Q.getResolvedLib(platformSymbolName("sayA"));445    auto B = Q.getResolvedLib(platformSymbolName("sayB"));446    auto Z = Q.getResolvedLib(platformSymbolName("sayZ"));447    ASSERT_TRUE(A.has_value());448    ASSERT_TRUE(B.has_value());449    ASSERT_TRUE(Z.has_value());450    EXPECT_TRUE(endsWith(A->str(), libname("A")));451    EXPECT_TRUE(endsWith(B->str(), libname("B")));452    EXPECT_TRUE(endsWith(Z->str(), libname("Z")));453    EXPECT_TRUE(Q.allResolved());454  });455 456  EXPECT_TRUE(CallbackRan);457}458 459TEST_F(LibraryResolverIT, ScanAndResolveDependencyGraph) {460  auto LibPathCache = std::make_shared<LibraryPathCache>();461  auto PResolver = std::make_shared<PathResolver>(LibPathCache);462  LibraryScanHelper ScanH({}, LibPathCache, PResolver);463 464  ScanH.addBasePath(libdir("C"), PathType::User);465 466  LibraryManager LibMgr;467  LibraryScanner Scanner(ScanH, LibMgr);468 469  Scanner.scanNext(PathType::User, 0);470 471  size_t numLibs = 0;472  LibMgr.forEachLibrary([&](const LibraryInfo &L) {473    numLibs++;474    return true;475  });476 477  EXPECT_GT(numLibs, 0u) << "Expected at least one library scanned";478 479  // Validate that each scanned library path is resolvable480  std::error_code EC;481  LibMgr.forEachLibrary([&](const LibraryInfo &L) {482    auto R = PResolver->resolve(L.getFullPath(), EC);483    EXPECT_TRUE(R.has_value());484    EXPECT_FALSE(EC);485    return true;486  });487}488 489TEST_F(LibraryResolverIT, ScanEmptyPath) {490  auto LibPathCache = std::make_shared<LibraryPathCache>();491  auto PResolver = std::make_shared<PathResolver>(LibPathCache);492  LibraryScanHelper ScanH({}, LibPathCache, PResolver);493 494  ScanH.addBasePath("/tmp/empty", PathType::User);495 496  LibraryManager LibMgr;497  LibraryScanner Scanner(ScanH, LibMgr);498 499  Scanner.scanNext(PathType::User, 0);500 501  size_t count = 0;502  LibMgr.forEachLibrary([&](const LibraryInfo &) {503    count++;504    return true;505  });506  EXPECT_EQ(count, 0u);507}508 509TEST_F(LibraryResolverIT, PathResolverResolvesKnownPaths) {510  auto LibPathCache = std::make_shared<LibraryPathCache>();511  auto PResolver = std::make_shared<PathResolver>(LibPathCache);512 513  std::error_code EC;514  auto Missing = PResolver->resolve("temp/foo/bar", EC);515  EXPECT_FALSE(Missing.has_value()) << "Unexpectedly resolved a bogus path";516  EXPECT_TRUE(EC) << "Expected error resolving path";517 518  auto DirPath = PResolver->resolve(BaseDir, EC);519  ASSERT_TRUE(DirPath.has_value());520  EXPECT_FALSE(EC) << "Expected no error resolving path";521  EXPECT_EQ(*DirPath, BaseDir);522 523  auto DylibPath = PResolver->resolve(lib("C"), EC);524  ASSERT_TRUE(DylibPath.has_value());525  EXPECT_FALSE(EC) << "Expected no error resolving path";526  EXPECT_EQ(*DylibPath, lib("C"));527}528 529TEST_F(LibraryResolverIT, PathResolverNormalizesDotAndDotDot) {530  auto LibPathCache = std::make_shared<LibraryPathCache>();531  auto PResolver = std::make_shared<PathResolver>(LibPathCache);532 533  std::error_code EC;534 535  // e.g. BaseDir + "/./C/../C/C.dylib" → BaseDir + "/C.dylib"536  std::string Messy = BaseDir + "/C/./../C/./libC" + ext;537  auto Resolved = PResolver->resolve(Messy, EC);538  ASSERT_TRUE(Resolved.has_value());539  EXPECT_FALSE(EC);540  EXPECT_EQ(*Resolved, lib("C")) << "Expected realpath to collapse . and ..";541}542 543#if !defined(_WIN32)544TEST_F(LibraryResolverIT, PathResolverFollowsSymlinks) {545  auto LibPathCache = std::make_shared<LibraryPathCache>();546  auto PResolver = std::make_shared<PathResolver>(LibPathCache);547 548  std::error_code EC;549 550  // Create a symlink temp -> BaseDir (only if filesystem allows it)551  std::string linkName = BaseDir + withext("/link_to_C");552  std::string target = lib("C");553  if (::symlink(target.c_str(), linkName.c_str()) != 0)554    GTEST_SKIP() << "Failed to create symlink: " << strerror(errno);555 556  auto resolved = PResolver->resolve(linkName, EC);557  ASSERT_TRUE(resolved.has_value());558  EXPECT_FALSE(EC);559  EXPECT_EQ(*resolved, target);560 561  (void)::unlink(linkName.c_str()); // cleanup562}563 564TEST_F(LibraryResolverIT, PathResolverCachesResults) {565  auto LibPathCache = std::make_shared<LibraryPathCache>();566  auto PResolver = std::make_shared<PathResolver>(LibPathCache);567 568  SmallString<128> TmpDylib;569  std::error_code EC;570  EC = sys::fs::createUniqueFile(withext("A-copy"), TmpDylib);571  if (EC)572    GTEST_SKIP() << "Failed to create temp dylib" << EC.message();573 574  EC = sys::fs::copy_file(lib("A"), TmpDylib);575  if (EC)576    GTEST_SKIP() << "Failed to copy libA: " << EC.message();577  EC.clear();578 579  // First resolve -> should populate LibPathCache580  auto first = PResolver->resolve(TmpDylib, EC);581  ASSERT_TRUE(first.has_value());582 583  // Forcefully remove the file from disk584  (void)::unlink(TmpDylib.c_str());585 586  // Second resolve -> should still succeed from LibPathCache587  auto second = PResolver->resolve(TmpDylib, EC);588  EXPECT_TRUE(second.has_value());589  EXPECT_EQ(*second, *first);590}591#endif592 593TEST_F(LibraryResolverIT, LoaderPathSubstitutionAndResolve) {594  auto LibPathCache = std::make_shared<LibraryPathCache>();595  auto PResolver = std::make_shared<PathResolver>(LibPathCache);596 597  DylibSubstitutor substitutor;598  substitutor.configure(libdir("C"));599#if defined(__APPLE__)600  // Substitute @loader_path with BaseDir601  std::string substituted =602      substitutor.substitute(withext("@loader_path/libC"));603#elif defined(__linux__)604  // Substitute $origin with BaseDir605  std::string substituted = substitutor.substitute(withext("$ORIGIN/libC"));606#endif607  ASSERT_FALSE(substituted.empty());608  EXPECT_EQ(substituted, lib("C"));609 610  // Now try resolving the substituted path611  std::error_code EC;612  auto resolved = PResolver->resolve(substituted, EC);613  ASSERT_TRUE(resolved.has_value()) << "Expected to resolve substituted dylib";614  EXPECT_EQ(*resolved, lib("C"));615  EXPECT_FALSE(EC) << "Expected no error resolving substituted dylib";616}617 618TEST_F(LibraryResolverIT, ResolveFromUsrOrSystemPaths) {619  auto LibPathCache = std::make_shared<LibraryPathCache>();620  auto PResolver = std::make_shared<PathResolver>(LibPathCache);621 622  DylibPathValidator validator(*PResolver);623 624  std::vector<std::string> Paths = {"/foo/bar/", "temp/foo",  libdir("C"),625                                    libdir("A"), libdir("B"), libdir("Z")};626 627  SmallVector<StringRef> P(Paths.begin(), Paths.end());628 629  DylibResolver Resolver(validator);630  Resolver.configure("", {{P, SearchPathType::UsrOrSys}});631 632  // Check "C"633  auto ValOptC = Resolver.resolve("libC", true);634  EXPECT_TRUE(ValOptC.has_value());635  EXPECT_EQ(*ValOptC, lib("C"));636 637  auto ValOptCdylib = Resolver.resolve(withext("libC"));638  EXPECT_TRUE(ValOptCdylib.has_value());639  EXPECT_EQ(*ValOptCdylib, lib("C"));640 641  // Check "A"642  auto ValOptA = Resolver.resolve("libA", true);643  EXPECT_TRUE(ValOptA.has_value());644  EXPECT_EQ(*ValOptA, lib("A"));645 646  auto ValOptAdylib = Resolver.resolve(withext("libA"));647  EXPECT_TRUE(ValOptAdylib.has_value());648  EXPECT_EQ(*ValOptAdylib, lib("A"));649 650  // Check "B"651  auto ValOptB = Resolver.resolve("libB", true);652  EXPECT_TRUE(ValOptB.has_value());653  EXPECT_EQ(*ValOptB, lib("B"));654 655  auto ValOptBdylib = Resolver.resolve(withext("libB"));656  EXPECT_TRUE(ValOptBdylib.has_value());657  EXPECT_EQ(*ValOptBdylib, lib("B"));658 659  // Check "Z"660  auto ValOptZ = Resolver.resolve("libZ", true);661  EXPECT_TRUE(ValOptZ.has_value());662  EXPECT_EQ(*ValOptZ, lib("Z"));663 664  auto ValOptZdylib = Resolver.resolve(withext("libZ"));665  EXPECT_TRUE(ValOptZdylib.has_value());666  EXPECT_EQ(*ValOptZdylib, lib("Z"));667}668 669#if defined(__APPLE__)670TEST_F(LibraryResolverIT, ResolveViaLoaderPathAndRPathSubstitution) {671  auto LibPathCache = std::make_shared<LibraryPathCache>();672  auto PResolver = std::make_shared<PathResolver>(LibPathCache);673 674  DylibPathValidator validator(*PResolver);675 676  std::vector<std::string> Paths = {"@loader_path/../A", "@loader_path/../B",677                                    "@loader_path/../C", "@loader_path/../Z"};678 679  SmallVector<StringRef> P(Paths.begin(), Paths.end());680 681  DylibResolver Resolver(validator);682 683  // Use only RPath config684  Resolver.configure(lib("C"), {{P, SearchPathType::RPath}});685 686  // --- Check A ---687  auto ValOptA = Resolver.resolve("@rpath/libA", true);688  EXPECT_TRUE(ValOptA.has_value());689  EXPECT_EQ(*ValOptA, lib("A"));690 691  auto ValOptAdylib = Resolver.resolve(withext("@rpath/libA"));692  EXPECT_TRUE(ValOptAdylib.has_value());693  EXPECT_EQ(*ValOptAdylib, lib("A"));694 695  // --- Check B ---696  auto ValOptB = Resolver.resolve("@rpath/libB", true);697  EXPECT_TRUE(ValOptB.has_value());698  EXPECT_EQ(*ValOptB, lib("B"));699 700  auto ValOptBdylib = Resolver.resolve(withext("@rpath/libB"));701  EXPECT_TRUE(ValOptBdylib.has_value());702  EXPECT_EQ(*ValOptBdylib, lib("B"));703 704  // --- Check Z ---705  auto ValOptZ = Resolver.resolve("@rpath/libZ", true);706  EXPECT_TRUE(ValOptZ.has_value());707  EXPECT_EQ(*ValOptZ, lib("Z"));708 709  auto ValOptZdylib = Resolver.resolve(withext("@rpath/libZ"));710  EXPECT_TRUE(ValOptZdylib.has_value());711  EXPECT_EQ(*ValOptZdylib, lib("Z"));712}713#endif714 715#if defined(__linux__)716TEST_F(LibraryResolverIT, ResolveViaOriginAndRPathSubstitution) {717  auto LibPathCache = std::make_shared<LibraryPathCache>();718  auto PResolver = std::make_shared<PathResolver>(LibPathCache);719 720  DylibPathValidator validator(*PResolver);721 722  // On Linux, $ORIGIN works like @loader_path723  std::vector<std::string> Paths = {"$ORIGIN/../A", "$ORIGIN/../B",724                                    "$ORIGIN/../C", "$ORIGIN/../Z"};725 726  SmallVector<StringRef> P(Paths.begin(), Paths.end());727 728  DylibResolver Resolver(validator);729 730  // Use only RPath config731  Resolver.configure(lib("C"), {{P, SearchPathType::RunPath}});732 733  // --- Check A ---734  auto ValOptA = Resolver.resolve("libA", true);735  EXPECT_TRUE(ValOptA.has_value());736  EXPECT_EQ(*ValOptA, lib("A"));737 738  auto valOptASO = Resolver.resolve(withext("libA"));739  EXPECT_TRUE(valOptASO.has_value());740  EXPECT_EQ(*valOptASO, lib("A"));741 742  // --- Check B ---743  auto ValOptB = Resolver.resolve("libB", true);744  EXPECT_TRUE(ValOptB.has_value());745  EXPECT_EQ(*ValOptB, lib("B"));746 747  auto valOptBSO = Resolver.resolve(withext("libB"));748  EXPECT_TRUE(valOptBSO.has_value());749  EXPECT_EQ(*valOptBSO, lib("B"));750 751  // --- Check Z ---752  auto ValOptZ = Resolver.resolve("libZ", true);753  EXPECT_TRUE(ValOptZ.has_value());754  EXPECT_EQ(*ValOptZ, lib("Z"));755 756  auto valOptZSO = Resolver.resolve(withext("libZ"));757  EXPECT_TRUE(valOptZSO.has_value());758  EXPECT_EQ(*valOptZSO, lib("Z"));759}760#endif761} // namespace762#endif // defined(__APPLE__)763