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