[llvm] [ORC] Add Auto-Loading DyLib Feature with Symbol Resolution (PR #109913)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 24 23:15:46 PDT 2024
https://github.com/SahilPatidar created https://github.com/llvm/llvm-project/pull/109913
With the help of @lhames, this commit introduces an auto-loading dynamic library feature for ORC JIT, aimed at improving the efficiency of symbol resolution for both loaded and unloaded libraries.
The key changes lies in the introduction of a global bloom filter, which is used to filter out symbols that are unlikely to be present. This helps reduce unnecessary search attempts and speeds up the lookup process. The `tryToResolve` function has also been optimized to make RPC calls to the executor, allowing it to resolve symbols from currently loaded libraries and retrieve their addresses.
If a symbol cannot be resolved from the loaded libraries, the executor proceeds to search through unloaded libraries. Every time a library is scanned, the global bloom filter is updated to optimize future lookups. Additionally, any symbols that are marked as "may contain" by the bloom filter but are not resolvable are added to a global exclusion set, preventing redundant resolution attempts in the future.
>From 7d069b153cecba3939a70a284ab9fe5773b5d1ba Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Thu, 19 Sep 2024 17:16:45 +0530
Subject: [PATCH 1/2] [ORC] Add Auto-Loading DyLib Feature with Symbol
Resolution
---
.../Orc/EPCDynamicLibrarySearchGenerator.h | 42 +
.../Orc/EPCGenericDylibManager.h | 12 +
.../Orc/ExecutorProcessControl.h | 15 +
.../Orc/Shared/AutoLoadDylibUtils.h | 212 +++
.../ExecutionEngine/Orc/Shared/OrcRTBridge.h | 7 +
.../ExecutionEngine/Orc/SimpleRemoteEPC.h | 3 +
.../Orc/TargetProcess/AutoLoadDylibLookup.h | 155 ++
.../SimpleExecutorDylibManager.h | 8 +
.../Orc/EPCDynamicLibrarySearchGenerator.cpp | 101 +-
.../Orc/EPCGenericDylibManager.cpp | 35 +-
.../Orc/ExecutorProcessControl.cpp | 5 +
.../Orc/Shared/OrcRTBridge.cpp | 2 +
.../ExecutionEngine/Orc/SimpleRemoteEPC.cpp | 37 +-
.../Orc/TargetProcess/AutoLoadDyLoader.cpp | 1306 +++++++++++++++++
.../Orc/TargetProcess/AutoLoadDylibLookUp.cpp | 575 ++++++++
.../Orc/TargetProcess/CMakeLists.txt | 3 +
.../SimpleExecutorDylibManager.cpp | 86 ++
17 files changed, 2598 insertions(+), 6 deletions(-)
create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h
create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.h
create mode 100644 llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp
create mode 100644 llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h
index e56afe4fe656b3..901e5d05bbeb18 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h
@@ -15,6 +15,7 @@
#define LLVM_EXECUTIONENGINE_ORC_EPCDYNAMICLIBRARYSEARCHGENERATOR_H
#include "llvm/ADT/FunctionExtras.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/ExecutionEngine/Orc/Core.h"
namespace llvm {
@@ -71,6 +72,47 @@ class EPCDynamicLibrarySearchGenerator : public DefinitionGenerator {
AddAbsoluteSymbolsFn AddAbsoluteSymbols;
};
+class AutoLoadDynamicLibrarySearchGenerator : public DefinitionGenerator {
+public:
+ using AddAbsoluteSymbolsFn = unique_function<Error(JITDylib &, SymbolMap)>;
+
+ /// Creates an AutoLoadDynamicLibrarySearchGenerator that searches for symbols
+ /// across all currently loaded libraries. If a symbol is not found, it scans
+ /// all potential dynamic libraries (dylibs), and if the symbol is located,
+ /// the corresponding library is loaded, and the symbol's definition is
+ /// returned.
+ ///
+ /// If \p AddAbsoluteSymbols is provided, it is used to add the symbols to the
+ /// \c JITDylib; otherwise it uses JD.define(absoluteSymbols(...)).
+ AutoLoadDynamicLibrarySearchGenerator(
+ ExecutionSession &ES, AddAbsoluteSymbolsFn AddAbsoluteSymbols = nullptr)
+ : EPC(ES.getExecutorProcessControl()),
+ AddAbsoluteSymbols(std::move(AddAbsoluteSymbols)) {}
+
+ /// Creates a AutoLoadDynamicLibrarySearchGenerator that searches for symbols
+ /// in the target process.
+ static Expected<std::unique_ptr<AutoLoadDynamicLibrarySearchGenerator>>
+ GetForTargetProcess(ExecutionSession &ES,
+ AddAbsoluteSymbolsFn AddAbsoluteSymbols = nullptr) {
+ return std::make_unique<AutoLoadDynamicLibrarySearchGenerator>(
+ ES, std::move(AddAbsoluteSymbols));
+ }
+
+ Error tryToGenerate(LookupState &LS, LookupKind K, JITDylib &JD,
+ JITDylibLookupFlags JDLookupFlags,
+ const SymbolLookupSet &Symbols) override;
+
+ Error
+ tryToResolve(SymbolNameSet CandidateSyms,
+ ExecutorProcessControl::ResolveSymbolsCompleteFn OnCompleteFn);
+
+private:
+ ExecutorProcessControl &EPC;
+ BloomFilter GlobalFilter;
+ StringSet<> ExcludedSymbols;
+ AddAbsoluteSymbolsFn AddAbsoluteSymbols;
+};
+
} // end namespace orc
} // end namespace llvm
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h
index 887147a77026e0..6bb1b40b1db933 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h
@@ -34,6 +34,7 @@ class EPCGenericDylibManager {
ExecutorAddr Instance;
ExecutorAddr Open;
ExecutorAddr Lookup;
+ ExecutorAddr Resolve;
};
/// Create an EPCGenericMemoryAccess instance from a given set of
@@ -70,6 +71,9 @@ class EPCGenericDylibManager {
using SymbolLookupCompleteFn =
unique_function<void(Expected<std::vector<ExecutorSymbolDef>>)>;
+ using ResolveSymbolsCompleteFn =
+ unique_function<void(Expected<ResolveResult>)>;
+
/// Looks up symbols within the given dylib.
void lookupAsync(tpctypes::DylibHandle H, const SymbolLookupSet &Lookup,
SymbolLookupCompleteFn Complete);
@@ -78,6 +82,14 @@ class EPCGenericDylibManager {
void lookupAsync(tpctypes::DylibHandle H, const RemoteSymbolLookupSet &Lookup,
SymbolLookupCompleteFn Complete);
+ /// Looks up symbols within the given dylib.
+ void resolveAsync(const SymbolLookupSet &Lookup,
+ ResolveSymbolsCompleteFn Complete);
+
+ /// Looks up symbols within the given dylib.
+ void resolveAsync(const RemoteSymbolLookupSet &Lookup,
+ ResolveSymbolsCompleteFn Complete);
+
private:
ExecutorProcessControl &EPC;
SymbolAddrs SAs;
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
index 6468f2dfc11ad0..2c79dc33efb5d4 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
@@ -15,6 +15,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
+#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h"
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
@@ -302,6 +303,9 @@ class ExecutorProcessControl {
using SymbolLookupCompleteFn =
unique_function<void(Expected<std::vector<tpctypes::LookupResult>>)>;
+ using ResolveSymbolsCompleteFn = unique_function<void(
+ Expected<std::vector<ResolveResult>>)>;
+
/// Search for symbols in the target process.
///
/// The result of the lookup is a 2-dimensional array of target addresses
@@ -311,6 +315,9 @@ class ExecutorProcessControl {
virtual void lookupSymbolsAsync(ArrayRef<LookupRequest> Request,
SymbolLookupCompleteFn F) = 0;
+ virtual void resolveSymbolsAsync(ArrayRef<SymbolLookupSet> Request,
+ ResolveSymbolsCompleteFn F) = 0;
+
/// Run function with a main-like signature.
virtual Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) = 0;
@@ -485,6 +492,11 @@ class UnsupportedExecutorProcessControl : public ExecutorProcessControl,
llvm_unreachable("Unsupported");
}
+ void resolveSymbolsAsync(ArrayRef<SymbolLookupSet> Request,
+ ResolveSymbolsCompleteFn F) override {
+ llvm_unreachable("Unsupported");
+ }
+
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) override {
llvm_unreachable("Unsupported");
@@ -531,6 +543,9 @@ class SelfExecutorProcessControl : public ExecutorProcessControl,
void lookupSymbolsAsync(ArrayRef<LookupRequest> Request,
SymbolLookupCompleteFn F) override;
+ void resolveSymbolsAsync(ArrayRef<SymbolLookupSet> Request,
+ ResolveSymbolsCompleteFn F) override;
+
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) override;
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h
new file mode 100644
index 00000000000000..73e8b53edf8598
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h
@@ -0,0 +1,212 @@
+//===------ AutoLoadDylibUtils.h - Auto-Loading Dynamic Library -------*- C++
+//-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_AUTOLOADDYLIBUTILS_H
+#define LLVM_EXECUTIONENGINE_ORC_SHARED_AUTOLOADDYLIBUTILS_H
+
+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
+
+#include <math.h>
+#include <type_traits>
+#include <vector>
+
+namespace llvm {
+namespace orc {
+
+namespace shared {
+using SPSBloomFilter =
+ SPSTuple<bool, uint32_t, uint32_t, uint32_t, SPSSequence<uint64_t>>;
+}
+
+constexpr uint32_t log2u(std::uint32_t n) {
+ return (n > 1) ? 1 + log2u(n >> 1) : 0;
+}
+
+class BloomFilter {
+private:
+ static constexpr int Bits = 8 * sizeof(uint64_t);
+ static constexpr float P = 0.02f;
+
+ bool Initialized = false;
+ uint32_t SymbolsCount = 0;
+ uint32_t BloomSize = 0;
+ uint32_t BloomShift = 0;
+ std::vector<uint64_t> BloomTable;
+
+ // This is a GNU implementation of hash used in bloom filter!
+ static uint32_t GNUHash(StringRef S) {
+ uint32_t H = 5381;
+ for (uint8_t C : S)
+ H = (H << 5) + H + C;
+ return H;
+ }
+ // Helper method for hash testing
+ bool TestHash(uint32_t hash) const {
+ assert(IsInitialized && "Bloom filter is not initialized!");
+ uint32_t hash2 = hash >> BloomShift;
+ uint32_t n = (hash >> log2u(Bits)) % BloomSize;
+ uint64_t mask = ((1ULL << (hash % Bits)) | (1ULL << (hash2 % Bits)));
+ return (mask & BloomTable[n]) == mask;
+ }
+
+ // Helper method to add a hash
+ void AddHash(uint32_t hash) {
+ assert(IsInitialized && "Bloom filter is not initialized!");
+ uint32_t hash2 = hash >> BloomShift;
+ uint32_t n = (hash >> log2u(Bits)) % BloomSize;
+ uint64_t mask = ((1ULL << (hash % Bits)) | (1ULL << (hash2 % Bits)));
+ BloomTable[n] |= mask;
+ }
+
+ // Resizes the Bloom filter table based on symbol count
+ void ResizeTable(uint32_t newSymbolsCount) {
+ assert(SymbolsCount == 0 && "Resize not supported after initialization!");
+ SymbolsCount = newSymbolsCount;
+ BloomSize =
+ static_cast<uint32_t>(ceil((-1.44f * SymbolsCount * log2f(P)) / Bits));
+ BloomShift = std::min(6u, log2u(SymbolsCount));
+ BloomTable.resize(BloomSize, 0);
+ }
+
+ friend class shared::SPSSerializationTraits<shared::SPSBloomFilter,
+ BloomFilter>;
+
+public:
+ BloomFilter() = default;
+ BloomFilter(const BloomFilter &other) noexcept
+ : Initialized(other.Initialized), SymbolsCount(other.SymbolsCount),
+ BloomSize(other.BloomSize), BloomShift(other.BloomShift),
+ BloomTable(other.BloomTable) {
+ }
+ BloomFilter &operator=(const BloomFilter &other) = delete;
+
+ BloomFilter(BloomFilter &&other) noexcept
+ : Initialized(other.Initialized), SymbolsCount(other.SymbolsCount),
+ BloomSize(other.BloomSize), BloomShift(other.BloomShift),
+ BloomTable(std::move(other.BloomTable)) {
+ other.Initialized = false;
+ other.SymbolsCount = 0;
+ other.BloomSize = 0;
+ other.BloomShift = 0;
+ }
+
+ BloomFilter &operator=(BloomFilter &&other) noexcept {
+ if (this != &other) {
+ Initialized = other.Initialized;
+ SymbolsCount = other.SymbolsCount;
+ BloomSize = other.BloomSize;
+ BloomShift = other.BloomShift;
+ BloomTable = std::move(other.BloomTable);
+
+ other.Initialized = false;
+ other.SymbolsCount = 0;
+ other.BloomSize = 0;
+ other.BloomShift = 0;
+ }
+ return *this;
+ }
+
+ void swap(BloomFilter &other) noexcept {
+ std::swap(Initialized, other.Initialized);
+ std::swap(SymbolsCount, other.SymbolsCount);
+ std::swap(BloomSize, other.BloomSize);
+ std::swap(BloomShift, other.BloomShift);
+ std::swap(BloomTable, other.BloomTable);
+ }
+
+ void Initialize(uint32_t newSymbolsCount) {
+ assert(!Initialized && "Cannot reinitialize the Bloom filter!");
+ Initialized = true;
+ ResizeTable(newSymbolsCount);
+ }
+
+ bool IsEmpty() const { return SymbolsCount == 0; }
+
+ uint32_t getSymCount() const { return SymbolsCount; }
+
+ bool IsInitialized() const { return Initialized; }
+
+ bool MayContain(uint32_t hash) const {
+ if (IsEmpty())
+ return false;
+ return TestHash(hash);
+ }
+
+ bool MayContain(StringRef symbol) const {
+ return MayContain(GNUHash(symbol));
+ }
+
+ void AddSymbol(StringRef symbol) { AddHash(GNUHash(symbol)); }
+};
+
+struct ResolveResult {
+ std::optional<BloomFilter> Filter;
+ std::vector<ExecutorSymbolDef> SymbolDef;
+};
+
+namespace shared {
+
+template <> class SPSSerializationTraits<SPSBloomFilter, BloomFilter> {
+public:
+ static size_t size(const BloomFilter &Filter) {
+ return SPSBloomFilter::AsArgList::size(
+ Filter.Initialized, Filter.SymbolsCount, Filter.BloomSize,
+ Filter.BloomShift, Filter.BloomTable);
+ }
+
+ static bool serialize(SPSOutputBuffer &OB, const BloomFilter &Filter) {
+ return SPSBloomFilter::AsArgList::serialize(
+ OB, Filter.Initialized, Filter.SymbolsCount, Filter.BloomSize,
+ Filter.BloomShift, Filter.BloomTable);
+ }
+
+ static bool deserialize(SPSInputBuffer &IB, BloomFilter &Filter) {
+ bool IsInitialized;
+ uint32_t SymbolsCount = 0, BloomSize = 0, BloomShift = 0;
+ std::vector<uint64_t> BloomTable;
+
+ if (!SPSBloomFilter::AsArgList::deserialize(
+ IB, IsInitialized, SymbolsCount, BloomSize, BloomShift, BloomTable))
+ return false;
+
+ Filter.Initialized = IsInitialized;
+ Filter.SymbolsCount = SymbolsCount;
+ Filter.BloomSize = BloomSize;
+ Filter.BloomShift = BloomShift;
+ Filter.BloomTable = std::move(BloomTable);
+
+ return true;
+ }
+};
+
+using SPSResolveResult =
+ SPSTuple<SPSOptional<SPSBloomFilter>, SPSSequence<SPSExecutorSymbolDef>>;
+template <> class SPSSerializationTraits<SPSResolveResult, ResolveResult> {
+public:
+ static size_t size(const ResolveResult &Result) {
+ return SPSResolveResult::AsArgList::size(Result.Filter, Result.SymbolDef);
+ }
+
+ static bool serialize(SPSOutputBuffer &OB, const ResolveResult &Result) {
+ return SPSResolveResult::AsArgList::serialize(OB, Result.Filter,
+ Result.SymbolDef);
+ }
+
+ static bool deserialize(SPSInputBuffer &IB, ResolveResult &Result) {
+ return SPSResolveResult::AsArgList::deserialize(IB, Result.Filter,
+ Result.SymbolDef);
+ }
+};
+
+} // end namespace shared
+} // end namespace orc
+} // end namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_AUTOLOADDYLIBUTILS_H
\ No newline at end of file
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
index 0c549bcbf0130c..b834ec415820c2 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
@@ -13,6 +13,7 @@
#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_ORCRTBRIDGE_H
#define LLVM_EXECUTIONENGINE_ORC_SHARED_ORCRTBRIDGE_H
+#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h"
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h"
#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
@@ -25,6 +26,7 @@ namespace rt {
extern const char *SimpleExecutorDylibManagerInstanceName;
extern const char *SimpleExecutorDylibManagerOpenWrapperName;
extern const char *SimpleExecutorDylibManagerLookupWrapperName;
+extern const char *SimpleExecutorDylibManagerResolveWrapperName;
extern const char *SimpleExecutorMemoryManagerInstanceName;
extern const char *SimpleExecutorMemoryManagerReserveWrapperName;
@@ -59,6 +61,11 @@ using SPSSimpleExecutorDylibManagerLookupSignature =
shared::SPSExecutorAddr, shared::SPSExecutorAddr,
shared::SPSRemoteSymbolLookupSet);
+using SPSSimpleExecutorDylibManagerResolveSignature = shared::SPSExpected<
+ shared::SPSTuple<shared::SPSOptional<shared::SPSBloomFilter>,
+ shared::SPSSequence<shared::SPSExecutorSymbolDef>>>(
+ shared::SPSExecutorAddr, shared::SPSRemoteSymbolLookupSet);
+
using SPSSimpleExecutorMemoryManagerReserveSignature =
shared::SPSExpected<shared::SPSExecutorAddr>(shared::SPSExecutorAddr,
uint64_t);
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h b/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h
index c10b8df01cc0a4..5485961fdcad1d 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h
@@ -74,6 +74,9 @@ class SimpleRemoteEPC : public ExecutorProcessControl,
void lookupSymbolsAsync(ArrayRef<LookupRequest> Request,
SymbolLookupCompleteFn F) override;
+ void resolveSymbolsAsync(ArrayRef<SymbolLookupSet> Request,
+ ResolveSymbolsCompleteFn F) override;
+
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) override;
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.h
new file mode 100644
index 00000000000000..83ad22a183f36f
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.h
@@ -0,0 +1,155 @@
+//===------------ AutoLoadDylibLookup.h ------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_AUTOLOADDYNAMICLIBRARY_H
+#define LLVM_EXECUTIONENGINE_ORC_AUTOLOADDYNAMICLIBRARY_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+namespace llvm {
+namespace orc {
+class DynamicLoader;
+
+#if defined(LLVM_ON_UNIX)
+const char *const kEnvDelim = ":";
+#elif defined(_WIN32)
+const char *const kEnvDelim = ";";
+#else
+#error "Unknown platform (environmental delimiter)"
+#endif
+
+class AutoLoadDynamicLibraryLookup {
+public:
+ /// Describes the library search paths.
+ struct SearchPathInfo {
+ /// The search path.
+ std::string Path;
+
+ /// True if the Path is on the LD_LIBRARY_PATH.
+ bool IsUser;
+
+ bool operator==(const SearchPathInfo &Other) const {
+ return IsUser == Other.IsUser && Path == Other.Path;
+ }
+ };
+ using SearchPathInfos = SmallVector<SearchPathInfo, 32>;
+
+private:
+ StringSet<> LoadedLibraries;
+
+ /// System's include path, get initialized at construction time.
+ SearchPathInfos SearchPaths;
+
+ DynamicLoader *Dyld = nullptr;
+
+ /// Concatenates current and system include paths to look up the filename.
+ /// Considers RPATH and RUNPATH (see: https://en.wikipedia.org/wiki/Rpath).
+ /// Returns the canonical file path or an empty string if not found.
+ std::string lookupLibInPaths(StringRef libStem,
+ SmallVector<StringRef, 2> RPath = {},
+ SmallVector<StringRef, 2> RunPath = {},
+ StringRef libLoader = "") const;
+
+ /// Concatenates current and system include paths, then looks up the filename.
+ /// If not found, adds platform-specific extensions (e.g., .so, .dll, .dylib)
+ /// and retries. Considers RPATH and RUNPATH (see:
+ /// https://en.wikipedia.org/wiki/Rpath). Returns the canonical file path or
+ /// an empty string if not found.
+ std::string lookupLibMaybeAddExt(StringRef filename,
+ SmallVector<StringRef, 2> RPath = {},
+ SmallVector<StringRef, 2> RunPath = {},
+ StringRef libLoader = "") const;
+
+ /// On a success returns to full path to a shared object that holds the
+ /// symbol pointed by func.
+ static std::string getSymbolLocation(void *func);
+
+public:
+ AutoLoadDynamicLibraryLookup();
+ ~AutoLoadDynamicLibraryLookup();
+ AutoLoadDynamicLibraryLookup(const AutoLoadDynamicLibraryLookup &) = delete;
+ AutoLoadDynamicLibraryLookup &
+ operator=(const AutoLoadDynamicLibraryLookup &) = delete;
+
+ const SearchPathInfos &getSearchPaths() const { return SearchPaths; }
+
+ void addSearchPath(StringRef dir, bool isUser = true, bool prepend = false) {
+ if (!dir.empty()) {
+ for (auto &item : SearchPaths)
+ if (dir.equals_insensitive(item.Path))
+ return;
+ auto pos = prepend ? SearchPaths.begin() : SearchPaths.end();
+ SearchPaths.insert(pos, SearchPathInfo{dir.str(), isUser});
+ }
+ }
+
+ /// Searches for a library in current and system include paths.
+ /// Considers RPATH and RUNPATH (see: https://en.wikipedia.org/wiki/Rpath).
+ /// Returns the canonical file path or an empty string if not found.
+ std::string lookupLibrary(StringRef libStem,
+ SmallVector<StringRef, 2> RPath = {},
+ SmallVector<StringRef, 2> RunPath = {},
+ StringRef libLoader = "",
+ bool variateLibStem = true) const;
+
+ /// Add loaded library.
+ void addLoadedLib(StringRef lib);
+
+ /// Returns true if the file was a dynamic library and it was already
+ /// loaded.
+ bool isLibraryLoaded(StringRef fullPath) const;
+
+ /// Initializes the dynamic loader (dyld).
+ /// Accepts a callback to decide if certain libraries, such as those
+ /// overriding malloc, should be ignored.
+ void initializeDynamicLoader(
+ std::function<bool(StringRef)> shouldPermanentlyIgnore);
+
+ /// Finds the first unloaded shared object containing the specified symbol.
+ /// Returns the library name if found, or an empty string otherwise.
+ std::string searchLibrariesForSymbol(StringRef mangledName,
+ bool searchSystem = true) const;
+
+ void dump(raw_ostream *S = nullptr) const;
+
+ /// On a success returns to full path to a shared object that holds the
+ /// symbol pointed by func.
+ template <class T> static std::string getSymbolLocation(T func) {
+ static_assert(std::is_pointer<T>::value, "Must be a function pointer!");
+ return getSymbolLocation(reinterpret_cast<void *>(func));
+ }
+
+ static std::string normalizePath(StringRef path);
+
+ /// Returns true if the file is a shared library.
+ /// Also sets whether the file exists to help identify incompatible formats.
+ static bool isSharedLibrary(StringRef libFullPath, bool *exists = nullptr);
+
+ void BuildGlobalBloomFilter(BloomFilter &Filter) const;
+};
+
+enum class SplitMode {
+ PruneNonExistant, ///< Don't add non-existant paths into output
+ FailNonExistant, ///< Fail on any non-existant paths
+ AllowNonExistant ///< Add all paths whether they exist or not
+};
+
+bool SplitPaths(StringRef PathStr, SmallVectorImpl<StringRef> &Paths,
+ SplitMode Mode, StringRef Delim, bool Verbose = false);
+
+} // end namespace orc
+} // end namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_AUTOLOADDYNAMICLIBRARY_H
\ No newline at end of file
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.h
index 00fd84e3ec1426..132c3c2904732e 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.h
@@ -17,11 +17,13 @@
#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_SIMPLEEXECUTORDYLIBMANAGER_H
#include "llvm/ADT/DenseSet.h"
+#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h"
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h"
#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorBootstrapService.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Error.h"
@@ -41,6 +43,8 @@ class SimpleExecutorDylibManager : public ExecutorBootstrapService {
Expected<std::vector<ExecutorSymbolDef>>
lookup(tpctypes::DylibHandle H, const RemoteSymbolLookupSet &L);
+ Expected<ResolveResult> resolve(const RemoteSymbolLookupSet &L);
+
Error shutdown() override;
void addBootstrapSymbols(StringMap<ExecutorAddr> &M) override;
@@ -53,8 +57,12 @@ class SimpleExecutorDylibManager : public ExecutorBootstrapService {
static llvm::orc::shared::CWrapperFunctionResult
lookupWrapper(const char *ArgData, size_t ArgSize);
+ static llvm::orc::shared::CWrapperFunctionResult
+ resolveWrapper(const char *ArgData, size_t ArgSize);
+
std::mutex M;
DylibSet Dylibs;
+ std::optional<AutoLoadDynamicLibraryLookup> DylibLookup;
};
} // end namespace rt_bootstrap
diff --git a/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp b/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp
index ec2187bad0f2c7..8ff9eb1870474d 100644
--- a/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp
@@ -35,9 +35,9 @@ Error EPCDynamicLibrarySearchGenerator::tryToGenerate(
return Error::success();
LLVM_DEBUG({
- dbgs() << "EPCDynamicLibrarySearchGenerator trying to generate "
- << Symbols << "\n";
- });
+ dbgs() << "EPCDynamicLibrarySearchGenerator trying to generate " << Symbols
+ << "\n";
+ });
SymbolLookupSet LookupSymbols;
@@ -91,5 +91,100 @@ Error EPCDynamicLibrarySearchGenerator::tryToGenerate(
return Error::success();
}
+Error AutoLoadDynamicLibrarySearchGenerator::tryToGenerate(
+ LookupState &LS, LookupKind K, JITDylib &JD,
+ JITDylibLookupFlags JDLookupFlags, const SymbolLookupSet &Symbols) {
+
+ if (Symbols.empty())
+ return Error::success();
+
+ LLVM_DEBUG({
+ dbgs() << "AutoLoadDynamicLibrarySearchGenerator trying to generate "
+ << Symbols << "\n";
+ });
+
+ SymbolNameSet CandidateSyms;
+ for (auto &KV : Symbols) {
+ if (GlobalFilter.IsInitialized() && !GlobalFilter.MayContain(*KV.first) &&
+ !ExcludedSymbols.count(*KV.first))
+ continue;
+
+ CandidateSyms.insert(KV.first);
+ }
+
+ if (CandidateSyms.empty())
+ return Error::success();
+
+ auto Err = tryToResolve(CandidateSyms, [this, &JD, LS = std::move(LS),
+ CandidateSyms](auto Result) mutable {
+ auto &ResolveRes = Result->front();
+ bool IsFilter = GlobalFilter.IsInitialized();
+ if (!IsFilter && ResolveRes.Filter.has_value()) {
+ GlobalFilter = std::move(ResolveRes.Filter.value());
+ }
+
+ if (!Result) {
+ LLVM_DEBUG({
+ dbgs() << "AutoLoadDynamicLibrarySearchGenerator resolve failed due to "
+ "error";
+ });
+ return LS.continueLookup(Result.takeError());
+ }
+
+ auto &Symbols = ResolveRes.SymbolDef;
+ assert(Result->size() == 1 && "Results for more than one library returned");
+ assert(Symbols.size() == CandidateSyms.size() &&
+ "Result has incorrect number of elements");
+
+ SymbolMap NewSymbols;
+ auto ResultI = Symbols.begin();
+ for (auto &S : CandidateSyms) {
+ if (ResultI->getAddress())
+ NewSymbols[S] = *ResultI;
+ else if (IsFilter)
+ ExcludedSymbols.insert(*S);
+ ++ResultI;
+ }
+
+ LLVM_DEBUG({
+ dbgs() << "AutoLoadDynamicLibrarySearchGenerator resolve returned "
+ << NewSymbols << "\n";
+ });
+
+ // If there were no resolved symbols bail out.
+ if (NewSymbols.empty())
+ return LS.continueLookup(Error::success());
+
+ // Define resolved symbols.
+ Error Err = AddAbsoluteSymbols
+ ? AddAbsoluteSymbols(JD, std::move(NewSymbols))
+ : JD.define(absoluteSymbols(std::move(NewSymbols)));
+
+ LS.continueLookup(std::move(Err));
+ });
+
+ return Err;
+}
+
+Error AutoLoadDynamicLibrarySearchGenerator::tryToResolve(
+ SymbolNameSet CandidateSyms,
+ ExecutorProcessControl::ResolveSymbolsCompleteFn OnCompleteFn) {
+
+ LLVM_DEBUG({
+ dbgs() << "AutoLoadDynamicLibrarySearchGenerator trying to resolve "
+ << CandidateSyms << "\n";
+ });
+
+ SymbolLookupSet LookupSymbols;
+
+ for (auto &S : CandidateSyms) {
+ LookupSymbols.add(S, SymbolLookupFlags::WeaklyReferencedSymbol);
+ }
+
+ EPC.resolveSymbolsAsync(LookupSymbols, std::move(OnCompleteFn));
+
+ return Error::success();
+}
+
} // end namespace orc
} // end namespace llvm
diff --git a/llvm/lib/ExecutionEngine/Orc/EPCGenericDylibManager.cpp b/llvm/lib/ExecutionEngine/Orc/EPCGenericDylibManager.cpp
index 298bde46ab7542..4884e033bc2c67 100644
--- a/llvm/lib/ExecutionEngine/Orc/EPCGenericDylibManager.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/EPCGenericDylibManager.cpp
@@ -66,7 +66,8 @@ EPCGenericDylibManager::CreateWithDefaultBootstrapSymbols(
if (auto Err = EPC.getBootstrapSymbols(
{{SAs.Instance, rt::SimpleExecutorDylibManagerInstanceName},
{SAs.Open, rt::SimpleExecutorDylibManagerOpenWrapperName},
- {SAs.Lookup, rt::SimpleExecutorDylibManagerLookupWrapperName}}))
+ {SAs.Lookup, rt::SimpleExecutorDylibManagerLookupWrapperName},
+ {SAs.Resolve, rt::SimpleExecutorDylibManagerResolveWrapperName}}))
return std::move(Err);
return EPCGenericDylibManager(EPC, std::move(SAs));
}
@@ -117,5 +118,37 @@ void EPCGenericDylibManager::lookupAsync(tpctypes::DylibHandle H,
SAs.Instance, H, Lookup);
}
+void EPCGenericDylibManager::resolveAsync(const SymbolLookupSet &Lookup,
+ ResolveSymbolsCompleteFn Complete) {
+ EPC.callSPSWrapperAsync<rt::SPSSimpleExecutorDylibManagerResolveSignature>(
+ SAs.Resolve,
+ [Complete = std::move(Complete)](Error SerializationErr,
+ Expected<ResolveResult> Result) mutable {
+ if (SerializationErr) {
+ cantFail(Result.takeError());
+ Complete(std::move(SerializationErr));
+ return;
+ }
+ Complete(std::move(Result));
+ },
+ SAs.Instance, Lookup);
+}
+
+void EPCGenericDylibManager::resolveAsync(const RemoteSymbolLookupSet &Lookup,
+ ResolveSymbolsCompleteFn Complete) {
+ EPC.callSPSWrapperAsync<rt::SPSSimpleExecutorDylibManagerResolveSignature>(
+ SAs.Resolve,
+ [Complete = std::move(Complete)](Error SerializationErr,
+ Expected<ResolveResult> Result) mutable {
+ if (SerializationErr) {
+ cantFail(Result.takeError());
+ Complete(std::move(SerializationErr));
+ return;
+ }
+ Complete(std::move(Result));
+ },
+ SAs.Instance, Lookup);
+}
+
} // end namespace orc
} // end namespace llvm
diff --git a/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp b/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp
index 0df7c4f25eb82c..05796ca3c0a3a4 100644
--- a/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp
@@ -113,6 +113,11 @@ void SelfExecutorProcessControl::lookupSymbolsAsync(
Complete(std::move(R));
}
+void SelfExecutorProcessControl::resolveSymbolsAsync(
+ ArrayRef<SymbolLookupSet> Request, ResolveSymbolsCompleteFn F) {
+ llvm_unreachable("Unsupported");
+}
+
Expected<int32_t>
SelfExecutorProcessControl::runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) {
diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
index ae39b1d1bfaa9d..bb2e31346d25b6 100644
--- a/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
@@ -18,6 +18,8 @@ const char *SimpleExecutorDylibManagerOpenWrapperName =
"__llvm_orc_SimpleExecutorDylibManager_open_wrapper";
const char *SimpleExecutorDylibManagerLookupWrapperName =
"__llvm_orc_SimpleExecutorDylibManager_lookup_wrapper";
+const char *SimpleExecutorDylibManagerResolveWrapperName =
+ "__llvm_orc_SimpleExecutorDylibManager_resolve_wrapper";
const char *SimpleExecutorMemoryManagerInstanceName =
"__llvm_orc_SimpleExecutorMemoryManager_Instance";
diff --git a/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
index a81019cb1dabb0..95ffa17fcd94fd 100644
--- a/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
@@ -57,11 +57,45 @@ lookupSymbolsAsyncHelper(EPCGenericDylibManager &DylibMgr,
});
}
+static void
+resolveSymbolsAsyncHelper(EPCGenericDylibManager &DylibMgr,
+ ArrayRef<SymbolLookupSet> Request,
+ std::vector<ResolveResult> Result,
+ SimpleRemoteEPC::ResolveSymbolsCompleteFn Complete) {
+ if (Request.empty())
+ return Complete(std::move(Result));
+
+ auto &Symbols = Request.front();
+ DylibMgr.resolveAsync(Symbols, [&DylibMgr, Request,
+ Complete = std::move(Complete),
+ Result = std::move(Result)](auto R) mutable {
+ if (!R)
+ return Complete(R.takeError());
+ Result.push_back({});
+ if (R->Filter.has_value())
+ Result.back().Filter.swap(R->Filter);
+
+ auto &S = R->SymbolDef;
+ auto &SymDef = Result.back().SymbolDef;
+ SymDef.reserve(S.size());
+ for (auto Addr : S)
+ SymDef.push_back(Addr);
+
+ resolveSymbolsAsyncHelper(DylibMgr, Request.drop_front(), std::move(Result),
+ std::move(Complete));
+ });
+}
+
void SimpleRemoteEPC::lookupSymbolsAsync(ArrayRef<LookupRequest> Request,
SymbolLookupCompleteFn Complete) {
lookupSymbolsAsyncHelper(*DylibMgr, Request, {}, std::move(Complete));
}
+void SimpleRemoteEPC::resolveSymbolsAsync(ArrayRef<SymbolLookupSet> Request,
+ ResolveSymbolsCompleteFn Complete) {
+ resolveSymbolsAsyncHelper(*DylibMgr, Request, {}, std::move(Complete));
+}
+
Expected<int32_t> SimpleRemoteEPC::runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) {
int64_t Result = 0;
@@ -299,8 +333,7 @@ Error SimpleRemoteEPC::setup(Setup S) {
// Prepare a handler for the setup packet.
PendingCallWrapperResults[0] =
- RunInPlace()(
- [&](shared::WrapperFunctionResult SetupMsgBytes) {
+ RunInPlace()([&](shared::WrapperFunctionResult SetupMsgBytes) {
if (const char *ErrMsg = SetupMsgBytes.getOutOfBandError()) {
EIP.set_value(
make_error<StringError>(ErrMsg, inconvertibleErrorCode()));
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp
new file mode 100644
index 00000000000000..334507b6d371a9
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp
@@ -0,0 +1,1306 @@
+//===---------------- DynamicLibraryManagerSymbol.cpp ----------------===//
+//
+// 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/ADT/SmallSet.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.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/Debug.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/WithColor.h"
+
+#include <algorithm>
+#include <list>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#ifdef LLVM_ON_UNIX
+#include <dlfcn.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif // LLVM_ON_UNIX
+
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#include <sys/stat.h>
+#undef LC_LOAD_DYLIB
+#undef LC_RPATH
+#endif // __APPLE__
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <libloaderapi.h> // For GetModuleFileNameA
+#include <memoryapi.h> // For VirtualQuery
+#include <windows.h>
+#endif
+
+namespace {
+#define PATH_MAX 1024
+using BasePath = std::string;
+using namespace llvm;
+
+// // This is a GNU implementation of hash used in bloom filter!
+static uint32_t GNUHash(StringRef S) {
+ uint32_t H = 5381;
+ for (uint8_t C : S)
+ H = (H << 5) + H + C;
+ return H;
+}
+
+/// An efficient representation of a full path to a library which does not
+/// duplicate common path patterns reducing the overall memory footprint.
+///
+/// For example, `/home/.../lib/libA.so`, Path will contain a pointer
+/// to `/home/.../lib/`
+/// will be stored and .second `libA.so`.
+/// This approach reduces the duplicate paths as at one location there may be
+/// plenty of libraries.
+struct LibraryPath {
+ const BasePath &Path;
+ std::string LibName;
+ orc::BloomFilter Filter;
+ StringSet<> Symbols;
+ // std::vector<const LibraryPath*> LibDeps;
+
+ LibraryPath(const BasePath &Path, const std::string &LibName)
+ : Path(Path), LibName(LibName) {}
+
+ bool operator==(const LibraryPath &other) const {
+ return (&Path == &other.Path || Path == other.Path) &&
+ LibName == other.LibName;
+ }
+
+ const std::string GetFullName() const {
+ SmallString<512> Vec(Path);
+ sys::path::append(Vec, StringRef(LibName));
+ return Vec.str().str();
+ }
+
+ void AddBloom(StringRef symbol) { Filter.AddSymbol(symbol); }
+
+ StringRef AddSymbol(const std::string &symbol) {
+ auto it = Symbols.insert(symbol);
+ return it.first->getKey();
+ }
+
+ bool hasBloomFilter() const { return Filter.IsInitialized(); }
+
+ bool isBloomFilterEmpty() const {
+ assert(Filter.IsInitialized() && "Bloom filter not initialized!");
+ return Filter.IsEmpty();
+ }
+
+ void InitializeBloomFilter(uint32_t newSymbolsCount) {
+ assert(!Filter.IsInitialized() && "Cannot re-initialize non-empty filter!");
+ Filter.Initialize(newSymbolsCount);
+ }
+
+ bool MayExistSymbol(StringRef symbol) const {
+ // The library had no symbols and the bloom filter is empty.
+ if (isBloomFilterEmpty())
+ return false;
+
+ return Filter.MayContain(symbol);
+ }
+
+ bool ExistSymbol(StringRef symbol) const {
+ return Symbols.find(symbol) != Symbols.end();
+ }
+};
+
+/// A helper class keeping track of loaded libraries. It implements a fast
+/// search O(1) while keeping deterministic iterability in a memory efficient
+/// way. The underlying set uses a custom hasher for better efficiency given the
+/// specific problem where the library names (LibName) are relatively short
+/// strings and the base paths (Path) are repetitive long strings.
+class LibraryPaths {
+ struct LibraryPathHashFn {
+ size_t operator()(const LibraryPath &item) const {
+ return std::hash<size_t>()(item.Path.length()) ^
+ std::hash<std::string>()(item.LibName);
+ }
+ };
+
+ std::vector<const LibraryPath *> Libs;
+ std::unordered_set<LibraryPath, LibraryPathHashFn> LibsH;
+
+public:
+ bool HasRegisteredLib(const LibraryPath &Lib) const {
+ return LibsH.count(Lib);
+ }
+
+ const LibraryPath *GetRegisteredLib(const LibraryPath &Lib) const {
+ auto search = LibsH.find(Lib);
+ if (search != LibsH.end())
+ return &(*search);
+ return nullptr;
+ }
+
+ const LibraryPath *RegisterLib(const LibraryPath &Lib) {
+ auto it = LibsH.insert(Lib);
+ assert(it.second && "Already registered!");
+ Libs.push_back(&*it.first);
+ return &*it.first;
+ }
+
+ void UnregisterLib(const LibraryPath &Lib) {
+ auto found = LibsH.find(Lib);
+ if (found == LibsH.end())
+ return;
+
+ Libs.erase(std::find(Libs.begin(), Libs.end(), &*found));
+ LibsH.erase(found);
+ }
+
+ size_t size() const {
+ assert(Libs.size() == LibsH.size());
+ return Libs.size();
+ }
+
+ const std::vector<const LibraryPath *> &GetLibraries() const { return Libs; }
+};
+
+#ifndef _WIN32
+// Cached version of system function lstat
+static inline mode_t cached_lstat(const char *path) {
+ static StringMap<mode_t> lstat_cache;
+
+ // If already cached - retun cached result
+ auto it = lstat_cache.find(path);
+ if (it != lstat_cache.end())
+ return it->second;
+
+ // If result not in cache - call system function and cache result
+ struct stat buf;
+ mode_t st_mode = (lstat(path, &buf) == -1) ? 0 : buf.st_mode;
+ lstat_cache.insert(std::pair<StringRef, mode_t>(path, st_mode));
+ return st_mode;
+}
+
+// Cached version of system function readlink
+static inline StringRef cached_readlink(const char *pathname) {
+ static StringMap<std::string> readlink_cache;
+
+ // If already cached - retun cached result
+ auto it = readlink_cache.find(pathname);
+ if (it != readlink_cache.end())
+ return StringRef(it->second);
+
+ // If result not in cache - call system function and cache result
+ char buf[PATH_MAX];
+ ssize_t len;
+ if ((len = readlink(pathname, buf, sizeof(buf))) != -1) {
+ buf[len] = '\0';
+ std::string s(buf);
+ readlink_cache.insert(std::pair<StringRef, std::string>(pathname, s));
+ return readlink_cache[pathname];
+ }
+ return "";
+}
+#endif
+
+// Cached version of system function realpath
+std::string cached_realpath(StringRef path, StringRef base_path = "",
+ bool is_base_path_real = false,
+ long symlooplevel = 40) {
+ if (path.empty()) {
+ errno = ENOENT;
+ return "";
+ }
+
+ if (!symlooplevel) {
+ errno = ELOOP;
+ return "";
+ }
+
+ // If already cached - retun cached result
+ static StringMap<std::pair<std::string, int>> cache;
+ bool relative_path = sys::path::is_relative(path);
+ if (!relative_path) {
+ auto it = cache.find(path);
+ if (it != cache.end()) {
+ errno = it->second.second;
+ return it->second.first;
+ }
+ }
+
+ // If result not in cache - call system function and cache result
+
+ StringRef sep(sys::path::get_separator());
+ SmallString<256> result(sep);
+#ifndef _WIN32
+ SmallVector<StringRef, 16> p;
+
+ // Relative or absolute path
+ if (relative_path) {
+ if (is_base_path_real) {
+ result.assign(base_path);
+ } else {
+ if (path[0] == '~' &&
+ (path.size() == 1 || sys::path::is_separator(path[1]))) {
+ static SmallString<128> home;
+ if (home.str().empty())
+ sys::path::home_directory(home);
+ StringRef(home).split(p, sep, /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+ } else if (base_path.empty()) {
+ static SmallString<256> current_path;
+ if (current_path.str().empty())
+ sys::fs::current_path(current_path);
+ StringRef(current_path)
+ .split(p, sep, /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+ } else {
+ base_path.split(p, sep, /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+ }
+ }
+ }
+ path.split(p, sep, /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+
+ // Handle path list items
+ for (auto item : p) {
+ if (item == ".")
+ continue; // skip "." element in "abc/./def"
+ if (item == "..") {
+ // collapse "a/b/../c" to "a/c"
+ size_t s = result.rfind(sep);
+ if (s != StringRef::npos)
+ result.resize(s);
+ if (result.empty())
+ result = sep;
+ continue;
+ }
+
+ size_t old_size = result.size();
+ sys::path::append(result, item);
+ mode_t st_mode = cached_lstat(result.c_str());
+ if (S_ISLNK(st_mode)) {
+ StringRef symlink = cached_readlink(result.c_str());
+ if (sys::path::is_relative(symlink)) {
+ result.resize(old_size);
+ result = cached_realpath(symlink, result, true, symlooplevel - 1);
+ } else {
+ result = cached_realpath(symlink, "", true, symlooplevel - 1);
+ }
+ } else if (st_mode == 0) {
+ cache.insert(std::pair<StringRef, std::pair<std::string, int>>(
+ path, std::pair<std::string, int>("", ENOENT)));
+ errno = ENOENT;
+ return "";
+ }
+ }
+#else
+ sys::fs::real_path(path, result);
+#endif
+ cache.insert(std::pair<StringRef, std::pair<std::string, int>>(
+ path, std::pair<std::string, int>(result.str().str(), errno)));
+ return result.str().str();
+}
+
+using namespace llvm;
+using namespace object;
+
+template <class ELFT>
+static Expected<StringRef> getDynamicStrTab(const 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 createError("dynamic string table not found");
+}
+
+static StringRef GetGnuHashSection(object::ObjectFile *file) {
+ for (auto S : file->sections()) {
+ StringRef name = cantFail(S.getName());
+ if (name == ".gnu.hash") {
+ return cantFail(S.getContents());
+ }
+ }
+ return "";
+}
+
+/// Bloom filter is a stochastic data structure which can tell us if a symbol
+/// name does not exist in a library with 100% certainty. If it tells us it
+/// exists this may not be true:
+/// https://blogs.oracle.com/solaris/gnu-hash-elf-sections-v2
+///
+/// ELF has this optimization in the new linkers by default, It is stored in the
+/// gnu.hash section of the object file.
+///
+///\returns true if the symbol may be in the library.
+static bool MayExistInElfObjectFile(object::ObjectFile *soFile, uint32_t hash) {
+ assert(soFile->isELF() && "Not ELF");
+
+ // Compute the platform bitness -- either 64 or 32.
+ const unsigned bits = 8 * soFile->getBytesInAddress();
+
+ StringRef contents = GetGnuHashSection(soFile);
+ if (contents.size() < 16)
+ // We need to search if the library doesn't have .gnu.hash section!
+ return true;
+ const char *hashContent = contents.data();
+
+ // See https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ for .gnu.hash
+ // table layout.
+ uint32_t maskWords = *reinterpret_cast<const uint32_t *>(hashContent + 8);
+ uint32_t shift2 = *reinterpret_cast<const uint32_t *>(hashContent + 12);
+ uint32_t hash2 = hash >> shift2;
+ uint32_t n = (hash / bits) % maskWords;
+
+ const char *bloomfilter = hashContent + 16;
+ const char *hash_pos = bloomfilter + n * (bits / 8); // * (Bits / 8)
+ uint64_t word = *reinterpret_cast<const uint64_t *>(hash_pos);
+ uint64_t bitmask = ((1ULL << (hash % bits)) | (1ULL << (hash2 % bits)));
+ return (bitmask & word) == bitmask;
+}
+
+} // namespace
+
+// This function isn't referenced outside its translation unit, but it
+// can't use the "static" keyword because its address is used for
+// GetMainExecutable (since some platforms don't support taking the
+// address of main, and some platforms can't implement GetMainExecutable
+// without being given the address of a function in the main executable).
+std::string GetExecutablePath() {
+ // This just needs to be some symbol in the binary; C++ doesn't
+ // allow taking the address of ::main however.
+ return llvm::orc::AutoLoadDynamicLibraryLookup::getSymbolLocation(
+ &GetExecutablePath);
+}
+
+namespace llvm {
+namespace orc {
+class DynamicLoader {
+ struct BasePathHashFunction {
+ size_t operator()(const BasePath &item) const {
+ return std::hash<std::string>()(item);
+ }
+ };
+
+ struct BasePathEqFunction {
+ size_t operator()(const BasePath &l, const BasePath &r) const {
+ return &l == &r || l == r;
+ }
+ };
+ /// A memory efficient VectorSet. The class provides O(1) search
+ /// complexity. It is tuned to compare BasePaths first by checking the
+ /// address and then the representation which models the base path reuse.
+ class BasePaths {
+ public:
+ std::unordered_set<BasePath, BasePathHashFunction, BasePathEqFunction>
+ Paths;
+
+ public:
+ const BasePath &RegisterBasePath(const std::string &Path,
+ bool *WasInserted = nullptr) {
+ auto it = Paths.insert(Path);
+ if (WasInserted)
+ *WasInserted = it.second;
+
+ return *it.first;
+ }
+
+ bool Contains(StringRef Path) { return Paths.count(Path.str()); }
+ };
+
+ bool FirstRun = true;
+ bool FirstRunSysLib = true;
+ bool UseBloomFilter = true;
+ bool UseHashTable = true;
+
+ const AutoLoadDynamicLibraryLookup &AutoLoadDylibMgr;
+
+ /// The basename of `/home/.../lib/libA.so`,
+ /// BasePaths will contain `/home/.../lib/`
+ BasePaths BasePaths;
+
+ LibraryPaths Libraries;
+ LibraryPaths SysLibraries;
+ /// Contains a set of libraries which we gave to the user via ResolveSymbol
+ /// call and next time we should check if the user loaded them to avoid
+ /// useless iterations.
+ LibraryPaths QueriedLibraries;
+
+ using PermanentlyIgnoreCallbackProto = std::function<bool(StringRef)>;
+ const PermanentlyIgnoreCallbackProto ShouldPermanentlyIgnoreCallback;
+ const StringRef ExecutableFormat;
+
+ /// Scan for shared objects which are not yet loaded. They are a our symbol
+ /// resolution candidate sources.
+ /// NOTE: We only scan not loaded shared objects.
+ /// \param[in] searchSystemLibraries - whether to decent to standard system
+ /// locations for shared objects.
+ void ScanForLibraries(bool searchSystemLibraries = false);
+
+ /// Builds a bloom filter lookup optimization.
+ void BuildBloomFilter(LibraryPath *Lib, object::ObjectFile *BinObjFile,
+ unsigned IgnoreSymbolFlags = 0) const;
+
+ /// Looks up symbols from a an object file, representing the library.
+ ///\param[in] Lib - full path to the library.
+ ///\param[in] mangledName - the mangled name to look for.
+ ///\param[in] IgnoreSymbolFlags - The symbols to ignore upon a match.
+ ///\returns true on success.
+ bool ContainsSymbol(const LibraryPath *Lib, StringRef mangledName,
+ unsigned IgnoreSymbolFlags = 0) const;
+
+ bool ShouldPermanentlyIgnore(StringRef FileName) const;
+ void dumpDebugInfo() const;
+
+public:
+ DynamicLoader(const AutoLoadDynamicLibraryLookup &DLM,
+ PermanentlyIgnoreCallbackProto shouldIgnore,
+ StringRef execFormat)
+ : AutoLoadDylibMgr(DLM), ShouldPermanentlyIgnoreCallback(shouldIgnore),
+ ExecutableFormat(execFormat) {}
+
+ ~DynamicLoader(){};
+
+ std::string searchLibrariesForSymbol(StringRef mangledName,
+ bool searchSystem);
+
+ void BuildGlobalBloomFilter(BloomFilter &Filter) const;
+};
+
+std::string RPathToStr(SmallVector<StringRef, 2> V) {
+ std::string result;
+ for (auto item : V)
+ result += item.str() + ",";
+ if (!result.empty())
+ result.pop_back();
+ return result;
+}
+
+template <class ELFT>
+void HandleDynTab(const ELFFile<ELFT> *Elf, StringRef FileName,
+ SmallVector<StringRef, 2> &RPath,
+ SmallVector<StringRef, 2> &RunPath,
+ std::vector<StringRef> &Deps, bool &isPIEExecutable) {
+#define DEBUG_TYPE "Dyld:"
+ const char *Data = "";
+ if (Expected<StringRef> StrTabOrErr = getDynamicStrTab(Elf))
+ Data = StrTabOrErr.get().data();
+
+ isPIEExecutable = false;
+
+ auto DynamicEntriesOrError = Elf->dynamicEntries();
+ if (!DynamicEntriesOrError) {
+ LLVM_DEBUG(dbgs() << "Dyld: failed to read dynamic entries in"
+ << "'" << FileName.str() << "'\n");
+ return;
+ }
+
+ for (const typename ELFT::Dyn &Dyn : *DynamicEntriesOrError) {
+ switch (Dyn.d_tag) {
+ case ELF::DT_NEEDED:
+ Deps.push_back(Data + Dyn.d_un.d_val);
+ break;
+ case ELF::DT_RPATH:
+ SplitPaths(Data + Dyn.d_un.d_val, RPath, SplitMode::AllowNonExistant,
+ kEnvDelim, false);
+ break;
+ case ELF::DT_RUNPATH:
+ SplitPaths(Data + Dyn.d_un.d_val, RunPath, SplitMode::AllowNonExistant,
+ kEnvDelim, false);
+ break;
+ case ELF::DT_FLAGS_1:
+ // Check if this is not a pie executable.
+ if (Dyn.d_un.d_val & ELF::DF_1_PIE)
+ isPIEExecutable = true;
+ break;
+ // (Dyn.d_tag == ELF::DT_NULL) continue;
+ // (Dyn.d_tag == ELF::DT_AUXILIARY || Dyn.d_tag == ELF::DT_FILTER)
+ }
+ }
+#undef DEBUG_TYPE
+}
+
+void DynamicLoader::ScanForLibraries(bool searchSystemLibraries /* = false*/) {
+#define DEBUG_TYPE "Dyld:ScanForLibraries:"
+ const auto &searchPaths = AutoLoadDylibMgr.getSearchPaths();
+
+ LLVM_DEBUG({
+ dbgs() << "Dyld::ScanForLibraries: system="
+ << (searchSystemLibraries ? "true" : "false") << "\n";
+ for (const AutoLoadDynamicLibraryLookup::SearchPathInfo &Info : searchPaths)
+ dbgs() << ">>>" << Info.Path << ", "
+ << (Info.IsUser ? "user\n" : "system\n");
+ });
+
+ SmallSet<const BasePath *, 32> ScannedPaths;
+ // FileName must be always full/absolute/resolved file name.
+ std::function<void(StringRef, unsigned)> ProcessLibraryFile =
+ [&](StringRef FileName, unsigned level) {
+ LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries HandleLib:"
+ << FileName.str() << ", level=" << level << " -> ");
+
+ StringRef FileRealPath = sys::path::parent_path(FileName);
+ StringRef FileRealName = sys::path::filename(FileName);
+ const BasePath &BaseP = BasePaths.RegisterBasePath(FileRealPath.str());
+ LibraryPath LibPath(BaseP, FileRealName.str());
+
+ if (SysLibraries.GetRegisteredLib(LibPath) ||
+ Libraries.GetRegisteredLib(LibPath)) {
+ LLVM_DEBUG(dbgs() << "Already handled!!!\n");
+ return;
+ }
+
+ if (ShouldPermanentlyIgnore(FileName)) {
+ LLVM_DEBUG(dbgs() << "PermanentlyIgnored!!!\n");
+ return;
+ }
+
+ if (searchSystemLibraries)
+ SysLibraries.RegisterLib(LibPath);
+ else
+ Libraries.RegisterLib(LibPath);
+
+ SmallVector<StringRef, 2> RPath, RunPath;
+ std::vector<StringRef> Deps;
+
+ auto ObjFileOrErr = object::ObjectFile::createObjectFile(FileName);
+ if (Error Err = ObjFileOrErr.takeError()) {
+ LLVM_DEBUG({
+ std::string Message;
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
+ Message += EIB.message() + "; ";
+ });
+ dbgs() << "Dyld::ScanForLibraries: Failed to read object file "
+ << FileName.str() << " Errors: " << Message << "\n";
+ });
+ return;
+ }
+
+ object::ObjectFile *BinObjF = ObjFileOrErr.get().getBinary();
+ if (BinObjF->isELF()) {
+ bool isPIEExecutable = false;
+
+ if (const auto *ELF = dyn_cast<ELF32LEObjectFile>(BinObjF))
+ HandleDynTab(&ELF->getELFFile(), FileName, RPath, RunPath, Deps,
+ isPIEExecutable);
+ else if (const auto *ELF = dyn_cast<ELF32BEObjectFile>(BinObjF))
+ HandleDynTab(&ELF->getELFFile(), FileName, RPath, RunPath, Deps,
+ isPIEExecutable);
+ else if (const auto *ELF = dyn_cast<ELF64LEObjectFile>(BinObjF))
+ HandleDynTab(&ELF->getELFFile(), FileName, RPath, RunPath, Deps,
+ isPIEExecutable);
+ else if (const auto *ELF = dyn_cast<ELF64BEObjectFile>(BinObjF))
+ HandleDynTab(&ELF->getELFFile(), FileName, RPath, RunPath, Deps,
+ isPIEExecutable);
+
+ if (level == 0 && isPIEExecutable) {
+ if (searchSystemLibraries)
+ SysLibraries.UnregisterLib(LibPath);
+ else
+ Libraries.UnregisterLib(LibPath);
+ return;
+ }
+ } else if (BinObjF->isMachO()) {
+ MachOObjectFile *Obj = dyn_cast<MachOObjectFile>(BinObjF);
+ for (const auto &Command : Obj->load_commands()) {
+ if (Command.C.cmd == MachO::LC_LOAD_DYLIB) {
+ MachO::dylib_command dylibCmd =
+ Obj->getDylibIDLoadCommand(Command);
+ Deps.push_back(StringRef(Command.Ptr + dylibCmd.dylib.name));
+ } else if (Command.C.cmd == MachO::LC_RPATH) {
+ MachO::rpath_command rpathCmd = Obj->getRpathCommand(Command);
+ SplitPaths(Command.Ptr + rpathCmd.path, RPath,
+ SplitMode::AllowNonExistant, kEnvDelim, false);
+ }
+ }
+ } else if (BinObjF->isCOFF()) {
+ // TODO: Implement COFF support
+ }
+
+ LLVM_DEBUG({
+ dbgs() << "Dyld::ScanForLibraries: Deps Info:\n";
+ dbgs() << " RPATH=" << RPathToStr(RPath) << "\n";
+ dbgs() << " RUNPATH=" << RPathToStr(RunPath) << "\n";
+ for (size_t i = 0; i < Deps.size(); ++i)
+ dbgs() << " Deps[" << i << "]=" << Deps[i].str() << "\n";
+ });
+
+ // Heuristics for workaround performance problems:
+ // (H1) If RPATH and RUNPATH == "" -> skip handling Deps
+ if (RPath.empty() && RunPath.empty()) {
+ LLVM_DEBUG(dbgs()
+ << "Dyld::ScanForLibraries: Skip all deps by Heuristic1: "
+ << FileName.str() << "\n");
+ return;
+ }
+
+ // (H2) If RPATH subset of LD_LIBRARY_PATH &&
+ // RUNPATH subset of LD_LIBRARY_PATH -> skip handling Deps
+ if (std::all_of(
+ RPath.begin(), RPath.end(),
+ [&](StringRef item) {
+ return std::any_of(
+ searchPaths.begin(), searchPaths.end(),
+ [&](const AutoLoadDynamicLibraryLookup::SearchPathInfo
+ &info) { return item == info.Path; });
+ }) &&
+ std::all_of(RunPath.begin(), RunPath.end(), [&](StringRef item) {
+ return std::any_of(
+ searchPaths.begin(), searchPaths.end(),
+ [&](const AutoLoadDynamicLibraryLookup::SearchPathInfo
+ &info) { return item == info.Path; });
+ })) {
+ LLVM_DEBUG(dbgs()
+ << "Dyld::ScanForLibraries: Skip all deps by Heuristic2: "
+ << FileName.str() << "\n");
+ return;
+ }
+
+ // Recursively handle dependencies
+ for (StringRef dep : Deps) {
+ std::string dep_full = AutoLoadDylibMgr.lookupLibrary(
+ dep, RPath, RunPath, FileName, false);
+ ProcessLibraryFile(dep_full, level + 1);
+ }
+ };
+
+ for (const AutoLoadDynamicLibraryLookup::SearchPathInfo &Info : searchPaths) {
+ if (Info.IsUser != searchSystemLibraries) {
+ // Examples which we should handle.
+ // File Real
+ // /lib/1/1.so /lib/1/1.so // file
+ // /lib/1/2.so->/lib/1/1.so /lib/1/1.so // file local link
+ // /lib/1/3.so->/lib/3/1.so /lib/3/1.so // file external link
+ // /lib/2->/lib/1 // path link
+ // /lib/2/1.so /lib/1/1.so // path link, file
+ // /lib/2/2.so->/lib/1/1.so /lib/1/1.so // path link, file local link
+ // /lib/2/3.so->/lib/3/1.so /lib/3/1.so // path link, file external link
+ //
+ // /lib/3/1.so
+ // /lib/3/2.so->/system/lib/s.so
+ // /lib/3/3.so
+ // /system/lib/1.so
+ //
+ // libL.so NEEDED/RPATH libR.so /lib/some-rpath/libR.so //
+ // needed/dependedt library in libL.so RPATH/RUNPATH or other (in)direct
+ // dep
+ //
+ // Paths = /lib/1 : /lib/2 : /lib/3
+
+ // BasePaths = ["/lib/1", "/lib/3", "/system/lib"]
+ // *Libraries = [<0,"1.so">, <1,"1.so">, <2,"s.so">, <1,"3.so">]
+
+ LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries Iter:" << Info.Path
+ << " -> ");
+ std::string RealPath = cached_realpath(Info.Path);
+
+ StringRef DirPath(RealPath);
+ LLVM_DEBUG(dbgs() << RealPath << "\n");
+
+ if (!sys::fs::is_directory(DirPath) || DirPath.empty())
+ continue;
+
+ // Already searched?
+ const BasePath &ScannedBPath = BasePaths.RegisterBasePath(RealPath);
+ if (ScannedPaths.count(&ScannedBPath)) {
+ LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries Already scanned: "
+ << RealPath << "\n");
+ continue;
+ }
+
+ LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries: Iterator: " << DirPath
+ << "\n");
+ std::error_code EC;
+ for (sys::fs::directory_iterator DirIt(DirPath, EC), DirEnd;
+ DirIt != DirEnd && !EC; DirIt.increment(EC)) {
+
+ LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries: Iterator >>> "
+ << DirIt->path()
+ << ", type=" << (short)(DirIt->type()) << "\n");
+
+ const sys::fs::file_type ft = DirIt->type();
+ if (ft == sys::fs::file_type::regular_file) {
+ ProcessLibraryFile(DirIt->path(), 0);
+ } else if (ft == sys::fs::file_type::symlink_file) {
+ std::string DepFileName_str = cached_realpath(DirIt->path());
+ StringRef DepFileName = DepFileName_str;
+ assert(!sys::fs::is_symlink_file(DepFileName));
+ if (!sys::fs::is_directory(DepFileName))
+ ProcessLibraryFile(DepFileName, 0);
+ }
+ }
+ for (sys::fs::directory_iterator DirIt(DirPath, EC), DirEnd;
+ DirIt != DirEnd && !EC; DirIt.increment(EC)) {
+ LLVM_DEBUG(dbgs() << "Dyld::ScanForLibraries: File " << DirIt->path()
+ << ", type=" << (short)DirIt->type() << "\n");
+
+ const sys::fs::file_type ft = DirIt->type();
+ if (ft == sys::fs::file_type::regular_file ||
+ (ft == sys::fs::file_type::symlink_file &&
+ !sys::fs::is_symlink_file(DirIt->path()))) {
+ ProcessLibraryFile(DirIt->path(), 0);
+ }
+ }
+
+ // Register the DirPath as fully scanned.
+ ScannedPaths.insert(&ScannedBPath);
+ }
+ }
+#undef DEBUG_TYPE
+}
+
+void DynamicLoader::BuildBloomFilter(LibraryPath *Lib,
+ object::ObjectFile *BinObjFile,
+ unsigned IgnoreSymbolFlags /*= 0*/) const {
+#define DEBUG_TYPE "Dyld::BuildBloomFilter:"
+ assert(UseBloomFilter && "Bloom filter is disabled");
+ assert(!Lib->hasBloomFilter() && "Bloom filter already built!");
+
+ using namespace llvm;
+ // using namespace object;
+
+ LLVM_DEBUG(dbgs() << "Dyld::BuildBloomFilter: Building Bloom filter for: "
+ << Lib->GetFullName() << "\n");
+
+ std::vector<StringRef> symbols;
+ uint32_t SymbolsCount = 0;
+ // Helper to process each symbol from a range
+ auto ProcessSymbols = [&](auto range) {
+ for (const object::SymbolRef &S : range) {
+ uint32_t Flags = cantFail(S.getFlags());
+
+ // Skip symbols based on the flags (e.g., undefined or ignored)
+ if (Flags & IgnoreSymbolFlags || Flags & object::SymbolRef::SF_Undefined)
+ continue;
+
+ Expected<StringRef> SymName = S.getName();
+ if (!SymName || SymName->empty()) {
+ LLVM_DEBUG(dbgs() << "Dyld::BuildBloomFilter: Skipped empty or failed "
+ "to read symbol\n");
+ continue;
+ }
+
+ symbols.push_back(*SymName);
+ ++SymbolsCount;
+ }
+ };
+
+ ProcessSymbols(BinObjFile->symbols());
+
+ if (BinObjFile->isELF()) {
+ // ELF file format has .dynstr section for the dynamic symbol table.
+ const auto *ElfObj = cast<object::ELFObjectFileBase>(BinObjFile);
+ ProcessSymbols(ElfObj->getDynamicSymbolIterators());
+ } else if (BinObjFile->isCOFF()) { // On Windows, the symbols are present in
+ // COFF format.
+ object::COFFObjectFile *CoffObj = cast<object::COFFObjectFile>(BinObjFile);
+ // In COFF, the symbols are not present in the SymbolTable section
+ // of the Object file. They are present in the ExportDirectory section.
+ for (const object::ExportDirectoryEntryRef &D :
+ CoffObj->export_directories()) {
+ // All the symbols are already flagged as exported.
+ // We cannot really ignore symbols based on flags as we do on unix.
+ StringRef Name;
+ auto Err = D.getSymbolName(Name);
+
+ if (Err) {
+ std::string Message;
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
+ Message += EIB.message() + "; ";
+ });
+ LLVM_DEBUG(dbgs() << "Dyld::BuildBloomFilter: Failed to read symbol "
+ << Message << "\n");
+ continue;
+ }
+ if (Name.empty())
+ continue;
+
+ ++SymbolsCount;
+ symbols.push_back(Name);
+ }
+ }
+
+ // Initialize the Bloom filter with the count of symbols
+ Lib->InitializeBloomFilter(SymbolsCount);
+
+ if (!SymbolsCount) {
+ LLVM_DEBUG(dbgs() << "Dyld::BuildBloomFilter: No symbols found.\n");
+ return;
+ }
+
+ LLVM_DEBUG({
+ dbgs() << "Dyld::BuildBloomFilter: Adding symbols to Bloom filter:\n";
+ for (const auto &Sym : symbols)
+ dbgs() << "- " << Sym << "\n";
+ });
+
+ // Add symbols to the Bloom filter
+ for (const auto &S : symbols) {
+ Lib->AddBloom(UseHashTable ? Lib->AddSymbol(S.str()) : S);
+ }
+#undef DEBUG_TYPE
+}
+
+bool DynamicLoader::ContainsSymbol(const LibraryPath *Lib,
+ StringRef mangledName,
+ unsigned IgnoreSymbolFlags /*= 0*/) const {
+#define DEBUG_TYPE "Dyld::ContainsSymbol:"
+ // Helper lambda to handle symbol search and logging
+ auto logAndReturn = [](bool result, const std::string &msg) {
+ LLVM_DEBUG(dbgs() << msg);
+ return result;
+ };
+
+ const std::string library_filename = Lib->GetFullName();
+
+ LLVM_DEBUG(dbgs() << "Dyld::ContainsSymbol: Find symbol: lib="
+ << library_filename << ", mangled=" << mangledName.str()
+ << "\n");
+
+ auto ObjF = object::ObjectFile::createObjectFile(library_filename);
+ if (Error Err = ObjF.takeError()) {
+ std::string Message;
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {
+ Message += EIB.message() + "; ";
+ });
+ return logAndReturn(false,
+ "Dyld::ContainsSymbol: Failed to read object file " +
+ library_filename + " Errors: " + Message + "\n");
+ }
+
+ object::ObjectFile *BinObjFile = ObjF.get().getBinary();
+ uint32_t hashedMangle = GNUHash(mangledName);
+
+ // Check for the gnu.hash section if ELF and exit early if symbol doesn't
+ // exist
+ if (BinObjFile->isELF() &&
+ !MayExistInElfObjectFile(BinObjFile, hashedMangle)) {
+ return logAndReturn(false,
+ "Dyld::ContainsSymbol: ELF BloomFilter: Skip symbol <" +
+ mangledName.str() + ">.\n");
+ }
+
+ // Use Bloom filter if enabled
+ if (UseBloomFilter) {
+ // Use our bloom filters and create them if necessary.
+ if (!Lib->hasBloomFilter()) {
+ BuildBloomFilter(const_cast<LibraryPath *>(Lib), BinObjFile,
+ IgnoreSymbolFlags);
+ }
+
+ // If the symbol does not exist, exit early. In case it may exist, iterate.
+ if (!Lib->MayExistSymbol(mangledName)) {
+ return logAndReturn(false,
+ "Dyld::ContainsSymbol: BloomFilter: Skip symbol <" +
+ mangledName.str() + ">.\n");
+ }
+ LLVM_DEBUG(dbgs() << "Dyld::ContainsSymbol: BloomFilter: Symbol <"
+ << mangledName.str() << "> May exist. Search for it.\n");
+ }
+
+ // Use hash table if enabled
+ if (UseHashTable) {
+ bool result = Lib->ExistSymbol(mangledName);
+ return logAndReturn(
+ result, result ? "Dyld::ContainsSymbol: HashTable: Symbol Exist\n"
+ : "Dyld::ContainsSymbol: HashTable: Symbol Not exis\n");
+ }
+
+ // Symbol not found in hash table; iterate through all symbols
+ LLVM_DEBUG(dbgs() << "Dyld::ContainsSymbol: Iterate all for <"
+ << mangledName.str() << ">");
+
+ auto ForeachSymbol =
+ [&library_filename](iterator_range<object::symbol_iterator> range,
+ unsigned IgnoreSymbolFlags,
+ StringRef mangledName) -> bool {
+ for (const object::SymbolRef &S : range) {
+
+ uint32_t Flags = cantFail(S.getFlags());
+ if (Flags & IgnoreSymbolFlags)
+ continue;
+
+ // Note, we are at last resort and loading library based on a weak
+ // symbol is allowed. Otherwise, the JIT will issue an unresolved
+ // symbol error.
+ //
+ // There are other weak symbol kinds (marked as 'V') to denote
+ // typeinfo and vtables. It is unclear whether we should load such
+ // libraries or from which library we should resolve the symbol.
+ // We seem to not have a way to differentiate it from the symbol API.
+
+ Expected<StringRef> SymNameErr = S.getName();
+ if (!SymNameErr || SymNameErr.get().empty())
+ continue;
+
+ if (SymNameErr.get() == mangledName) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ // If no hash symbol then iterate to detect symbol
+ // We Iterate only if BloomFilter and/or SymbolHashTable are not supported.
+
+ // Symbol may exist. Iterate.
+ if (ForeachSymbol(BinObjFile->symbols(), IgnoreSymbolFlags, mangledName)) {
+ return logAndReturn(true, "Dyld::ContainsSymbol: Symbol " +
+ mangledName.str() + " found in " +
+ library_filename + "\n");
+ }
+
+ // Check dynamic symbols for ELF files
+ if (BinObjFile->isELF()) {
+ // ELF file format has .dynstr section for the dynamic symbol table.
+ const auto *ElfObj = cast<object::ELFObjectFileBase>(BinObjFile);
+ bool result = ForeachSymbol(ElfObj->getDynamicSymbolIterators(),
+ IgnoreSymbolFlags, mangledName);
+ return logAndReturn(
+ result,
+ result ? "Dyld::ContainsSymbol: Symbol found in ELF dynamic symbols.\n"
+ : "Dyld::ContainsSymbol: Symbol not found in ELF dynamic "
+ "symbols.\n");
+ }
+
+ return logAndReturn(false, "Dyld::ContainsSymbol: Symbol not found.\n");
+#undef DEBUG_TYPE
+}
+
+bool DynamicLoader::ShouldPermanentlyIgnore(StringRef FileName) const {
+#define DEBUG_TYPE "Dyld:"
+ assert(!ExecutableFormat.empty() && "Failed to find the object format!");
+
+ if (!AutoLoadDynamicLibraryLookup::isSharedLibrary(FileName))
+ return true;
+
+ // No need to check linked libraries, as this function is only invoked
+ // for symbols that cannot be found (neither by dlsym nor in the JIT).
+ if (AutoLoadDylibMgr.isLibraryLoaded(FileName))
+ return true;
+
+ auto ObjF = object::ObjectFile::createObjectFile(FileName);
+ if (!ObjF) {
+ LLVM_DEBUG(dbgs() << "[DyLD] Failed to read object file " << FileName
+ << "\n");
+ return true;
+ }
+
+ object::ObjectFile *file = ObjF.get().getBinary();
+
+ LLVM_DEBUG(dbgs() << "Current executable format: " << ExecutableFormat
+ << ". Executable format of " << FileName << " : "
+ << file->getFileFormatName() << "\n");
+
+ // Ignore libraries with different format than the executing one.
+ if (ExecutableFormat != file->getFileFormatName())
+ return true;
+
+ if (isa<object::ELFObjectFileBase>(*file)) {
+ for (auto S : file->sections()) {
+ StringRef name = cantFail(S.getName());
+ if (name == ".text") {
+ // Check if the library has only debug symbols, usually when
+ // stripped with objcopy --only-keep-debug. This check is done by
+ // reading the manual of objcopy and inspection of stripped with
+ // objcopy libraries.
+ auto SecRef = static_cast<object::ELFSectionRef &>(S);
+ if (SecRef.getType() == ELF::SHT_NOBITS)
+ return true;
+
+ return (SecRef.getFlags() & ELF::SHF_ALLOC) == 0;
+ }
+ }
+ return true;
+ }
+
+ // FIXME: Handle osx using isStripped after upgrading to llvm9.
+
+ return ShouldPermanentlyIgnoreCallback(FileName);
+#undef DEBUG_TYPE
+}
+
+void DynamicLoader::dumpDebugInfo() const {
+#define DEBUG_TYPE "Dyld:"
+ LLVM_DEBUG({
+ dbgs() << "---\n";
+ size_t x = 0;
+ for (auto const &item : BasePaths.Paths) {
+ dbgs() << "Dyld: - BasePaths[" << x++ << "]:" << &item << ": " << item
+ << "\n";
+ }
+ dbgs() << "---\n";
+ x = 0;
+ for (auto const &item : Libraries.GetLibraries()) {
+ dbgs() << "Dyld: - Libraries[" << x++ << "]:" << &item << ": "
+ << item->Path << ", " << item->LibName << "\n";
+ }
+ x = 0;
+ for (auto const &item : SysLibraries.GetLibraries()) {
+ dbgs() << "Dyld: - SysLibraries[" << x++ << "]:" << &item << ": "
+ << item->Path << ", " << item->LibName << "\n";
+ }
+ });
+#undef DEBUG_TYPE
+}
+
+std::string
+DynamicLoader::searchLibrariesForSymbol(StringRef mangledName,
+ bool searchSystem /* = true*/) {
+#define DEBUG_TYPE "Dyld:searchLibrariesForSymbol:"
+ assert(!sys::DynamicLibrary::SearchForAddressOfSymbol(mangledName.str()) &&
+ "Library already loaded, please use dlsym!");
+ assert(!mangledName.empty());
+
+ using namespace sys::path;
+ using namespace sys::fs;
+
+ if (FirstRun) {
+ LLVM_DEBUG(dbgs() << "Dyld::searchLibrariesForSymbol:" << mangledName.str()
+ << ", searchSystem=" << (searchSystem ? "true" : "false")
+ << ", FirstRun(user)... scanning\n");
+
+ LLVM_DEBUG(
+ dbgs()
+ << "Dyld::searchLibrariesForSymbol: Before first ScanForLibraries\n");
+ dumpDebugInfo();
+
+ ScanForLibraries(/* SearchSystemLibraries= */ false);
+ FirstRun = false;
+
+ LLVM_DEBUG(
+ dbgs()
+ << "Dyld::searchLibrariesForSymbol: After first ScanForLibraries\n");
+ dumpDebugInfo();
+ }
+
+ if (QueriedLibraries.size() > 0) {
+ // Last call we were asked if a library contains a symbol. Usually, the
+ // caller wants to load this library. Check if was loaded and remove it
+ // from our lists of not-yet-loaded libs.
+
+ LLVM_DEBUG({
+ dbgs() << "Dyld::ResolveSymbol: QueriedLibraries:\n";
+ size_t x = 0;
+ for (auto item : QueriedLibraries.GetLibraries()) {
+ dbgs() << "Dyld::ResolveSymbol - [" << x++ << "]:" << &item << ": "
+ << item->GetFullName() << "\n";
+ }
+ });
+
+ for (const LibraryPath *P : QueriedLibraries.GetLibraries()) {
+ const std::string LibName = P->GetFullName();
+ if (!AutoLoadDylibMgr.isLibraryLoaded(LibName))
+ continue;
+
+ Libraries.UnregisterLib(*P);
+ SysLibraries.UnregisterLib(*P);
+ }
+ // TODO: QueriedLibraries.clear ?
+ }
+
+ // Iterate over files under this path. We want to get each ".so" files
+ for (const LibraryPath *P : Libraries.GetLibraries()) {
+ if (ContainsSymbol(P, mangledName, /*ignore*/
+ object::SymbolRef::SF_Undefined)) {
+ if (!QueriedLibraries.HasRegisteredLib(*P))
+ QueriedLibraries.RegisterLib(*P);
+
+ LLVM_DEBUG(
+ dbgs() << "Dyld::ResolveSymbol: Search found match in [user lib]: "
+ << P->GetFullName() << "!\n");
+
+ return P->GetFullName();
+ }
+ }
+
+ if (!searchSystem)
+ return "";
+
+ LLVM_DEBUG(dbgs() << "Dyld::searchLibrariesForSymbol: SearchSystem!!!\n");
+
+ // Lookup in non-system libraries failed. Expand the search to the system.
+ if (FirstRunSysLib) {
+ LLVM_DEBUG(dbgs() << "Dyld::searchLibrariesForSymbol:" << mangledName.str()
+ << ", searchSystem=" << (searchSystem ? "true" : "false")
+ << ", FirstRun(system)... scanning\n");
+
+ LLVM_DEBUG(dbgs() << "Dyld::searchLibrariesForSymbol: Before first system "
+ "ScanForLibraries\n");
+ dumpDebugInfo();
+
+ ScanForLibraries(/* SearchSystemLibraries= */ true);
+ FirstRunSysLib = false;
+
+ LLVM_DEBUG(dbgs() << "Dyld::searchLibrariesForSymbol: After first system "
+ "ScanForLibraries\n");
+ dumpDebugInfo();
+ }
+
+ for (const LibraryPath *P : SysLibraries.GetLibraries()) {
+ if (ContainsSymbol(P, mangledName, /*ignore*/
+ object::SymbolRef::SF_Undefined |
+ object::SymbolRef::SF_Weak)) {
+ if (!QueriedLibraries.HasRegisteredLib(*P))
+ QueriedLibraries.RegisterLib(*P);
+
+ LLVM_DEBUG(
+ dbgs() << "Dyld::ResolveSymbol: Search found match in [system lib]: "
+ << P->GetFullName() << "!\n");
+
+ return P->GetFullName();
+ }
+ }
+
+ LLVM_DEBUG(dbgs() << "Dyld::ResolveSymbol: Search found no match!\n");
+
+ return ""; // Search found no match.
+#undef DEBUG_TYPE
+}
+
+void DynamicLoader::BuildGlobalBloomFilter(BloomFilter &Filter) const {
+ assert(!Filter.IsInitialized());
+ uint32_t GloabalBloomSize = 0;
+
+ for (auto *L : Libraries.GetLibraries()) {
+ GloabalBloomSize += L->Filter.getSymCount();
+ }
+
+ for (auto *L : SysLibraries.GetLibraries()) {
+ GloabalBloomSize += L->Filter.getSymCount();
+ }
+
+ Filter.Initialize(GloabalBloomSize);
+ for (auto *L : Libraries.GetLibraries()) {
+ for (auto &S : L->Symbols) {
+ Filter.AddSymbol(S.getKey());
+ }
+ }
+
+ for (auto *L : SysLibraries.GetLibraries()) {
+ for (auto &S : L->Symbols) {
+ Filter.AddSymbol(S.getKey());
+ }
+ }
+}
+
+AutoLoadDynamicLibraryLookup::~AutoLoadDynamicLibraryLookup() {
+ static_assert(sizeof(Dyld) > 0, "Incomplete type");
+ delete Dyld;
+}
+
+void AutoLoadDynamicLibraryLookup::initializeDynamicLoader(
+ std::function<bool(StringRef)> shouldPermanentlyIgnore) {
+ assert(!Dyld && "Already initialized!");
+ if (Dyld)
+ return;
+ std::string exeP = GetExecutablePath();
+ auto ObjF = cantFail(object::ObjectFile::createObjectFile(exeP));
+
+ Dyld = new DynamicLoader(*this, shouldPermanentlyIgnore,
+ ObjF.getBinary()->getFileFormatName());
+}
+
+void AutoLoadDynamicLibraryLookup::BuildGlobalBloomFilter(
+ BloomFilter &Filter) const {
+ Dyld->BuildGlobalBloomFilter(Filter);
+}
+
+std::string AutoLoadDynamicLibraryLookup::searchLibrariesForSymbol(
+ StringRef mangledName, bool searchSystem /* = true*/) const {
+ assert(Dyld && "Must call initialize dyld before!");
+ return Dyld->searchLibrariesForSymbol(mangledName, searchSystem);
+}
+
+std::string AutoLoadDynamicLibraryLookup::getSymbolLocation(void *func) {
+#if defined(__CYGWIN__) && defined(__GNUC__)
+ return {};
+#elif defined(_WIN32)
+ MEMORY_BASIC_INFORMATION mbi;
+ if (!VirtualQuery(func, &mbi, sizeof(mbi)))
+ return {};
+
+ HMODULE hMod = (HMODULE)mbi.AllocationBase;
+ char moduleName[MAX_PATH];
+
+ if (!GetModuleFileNameA(hMod, moduleName, sizeof(moduleName)))
+ return {};
+
+ return cached_realpath(moduleName);
+
+#else
+ // assume we have defined HAVE_DLFCN_H and HAVE_DLADDR
+ Dl_info info;
+ if (dladdr((void *)func, &info) == 0) {
+ // Not in a known shared library, let's give up
+ return {};
+ } else {
+ std::string result = cached_realpath(info.dli_fname);
+ if (!result.empty())
+ return result;
+
+ // Else absolute path. For all we know that's a binary.
+ // Some people have dictionaries in binaries, this is how we find their
+ // path: (see also https://stackoverflow.com/a/1024937/6182509)
+#if defined(__APPLE__)
+ char buf[PATH_MAX] = {0};
+ uint32_t bufsize = sizeof(buf);
+ if (_NSGetExecutablePath(buf, &bufsize) >= 0)
+ return cached_realpath(buf);
+ return cached_realpath(info.dli_fname);
+#elif defined(LLVM_ON_UNIX)
+ char buf[PATH_MAX] = {0};
+ // Cross our fingers that /proc/self/exe exists.
+ if (readlink("/proc/self/exe", buf, sizeof(buf)) > 0)
+ return cached_realpath(buf);
+ std::string pipeCmd = std::string("which \"") + info.dli_fname + "\"";
+ FILE *pipe = popen(pipeCmd.c_str(), "r");
+ if (!pipe)
+ return cached_realpath(info.dli_fname);
+ while (fgets(buf, sizeof(buf), pipe))
+ result += buf;
+
+ pclose(pipe);
+ return cached_realpath(result);
+#else
+#error "Unsupported platform."
+#endif
+ return {};
+ }
+#endif
+}
+
+} // namespace orc
+} // namespace llvm
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp
new file mode 100644
index 00000000000000..b474870cd28cca
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp
@@ -0,0 +1,575 @@
+//===---------------- AutoLoadDylibLookup.cpp ----------------===//
+//
+// 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/AutoLoadDylibLookup.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+#if defined(_WIN32)
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/Support/Endian.h"
+#endif
+
+#include <fstream>
+#include <sys/stat.h>
+#include <system_error>
+
+static bool GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string> &Paths) {
+#if defined(__APPLE__) || defined(__CYGWIN__)
+ Paths.push_back("/usr/local/lib/");
+ Paths.push_back("/usr/X11R6/lib/");
+ Paths.push_back("/usr/lib/");
+ Paths.push_back("/lib/");
+
+#ifndef __APPLE__
+ Paths.push_back("/lib/x86_64-linux-gnu/");
+ Paths.push_back("/usr/local/lib64/");
+ Paths.push_back("/usr/lib64/");
+ Paths.push_back("/lib64/");
+#endif
+#elif defined(LLVM_ON_UNIX)
+ llvm::SmallString<1024> Buf;
+ Popen("LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls", Buf, true);
+ const llvm::StringRef Result = Buf.str();
+
+ const std::size_t NPos = std::string::npos;
+ const std::size_t LD = Result.find("(LD_LIBRARY_PATH)");
+ std::size_t From = Result.find("search path=", LD == NPos ? 0 : LD);
+ if (From != NPos) {
+ std::size_t To = Result.find("(system search path)", From);
+ if (To != NPos) {
+ From += 12;
+ while (To > From && isspace(Result[To - 1]))
+ --To;
+ std::string SysPath = Result.substr(From, To - From).str();
+ SysPath.erase(std::remove_if(SysPath.begin(), SysPath.end(), ::isspace),
+ SysPath.end());
+
+ llvm::SmallVector<llvm::StringRef, 10> CurPaths;
+ SplitPaths(SysPath, CurPaths);
+ for (const auto &Path : CurPaths)
+ Paths.push_back(Path.str());
+ }
+ }
+#endif
+ return true;
+}
+
+static std::string NormalizePath(const std::string &Path) {
+
+ llvm::SmallString<256> Buffer;
+ std::error_code EC = llvm::sys::fs::real_path(Path, Buffer, true);
+ if (EC)
+ return std::string();
+ return std::string(Buffer.str());
+}
+
+namespace llvm {
+namespace orc {
+
+void LogNonExistantDirectory(StringRef Path) {
+#define DEBUG_TYPE "LogNonExistantDirectory"
+ LLVM_DEBUG(dbgs() << " ignoring nonexistent directory \"" << Path << "\"\n");
+#undef DEBUG_TYPE
+}
+
+bool SplitPaths(StringRef PathStr, SmallVectorImpl<StringRef> &Paths,
+ SplitMode Mode, StringRef Delim, bool Verbose) {
+#define DEBUG_TYPE "SplitPths"
+
+ assert(Delim.size() && "Splitting without a delimiter");
+
+#if defined(_WIN32)
+ // Support using a ':' delimiter on Windows.
+ const bool WindowsColon = Delim.equals(":");
+#endif
+
+ bool AllExisted = true;
+ for (std::pair<StringRef, StringRef> Split = PathStr.split(Delim);
+ !Split.second.empty(); Split = PathStr.split(Delim)) {
+
+ if (!Split.first.empty()) {
+ bool Exists = sys::fs::is_directory(Split.first);
+
+#if defined(_WIN32)
+ // Because drive letters will have a colon we have to make sure the split
+ // occurs at a colon not followed by a path separator.
+ if (!Exists && WindowsColon && Split.first.size() == 1) {
+ // Both clang and cl.exe support '\' and '/' path separators.
+ if (Split.second.front() == '\\' || Split.second.front() == '/') {
+ const std::pair<StringRef, StringRef> Tmp = Split.second.split(Delim);
+ // Split.first = 'C', but we want 'C:', so Tmp.first.size()+2
+ Split.first = StringRef(Split.first.data(), Tmp.first.size() + 2);
+ Split.second = Tmp.second;
+ Exists = sys::fs::is_directory(Split.first);
+ }
+ }
+#endif
+
+ AllExisted = AllExisted && Exists;
+
+ if (!Exists) {
+ if (Mode == SplitMode::FailNonExistant) {
+ if (Verbose) {
+ // Exiting early, but still log all non-existant paths that we have
+ LogNonExistantDirectory(Split.first);
+ while (!Split.second.empty()) {
+ Split = PathStr.split(Delim);
+ if (sys::fs::is_directory(Split.first)) {
+ LLVM_DEBUG(dbgs() << " ignoring directory that exists \""
+ << Split.first << "\"\n");
+ } else
+ LogNonExistantDirectory(Split.first);
+ Split = Split.second.split(Delim);
+ }
+ if (!sys::fs::is_directory(Split.first))
+ LogNonExistantDirectory(Split.first);
+ }
+ return false;
+ } else if (Mode == SplitMode::AllowNonExistant)
+ Paths.push_back(Split.first);
+ else if (Verbose)
+ LogNonExistantDirectory(Split.first);
+ } else
+ Paths.push_back(Split.first);
+ }
+
+ PathStr = Split.second;
+ }
+
+ // Trim trailing sep in case of A:B:C:D:
+ if (!PathStr.empty() && PathStr.ends_with(Delim))
+ PathStr = PathStr.substr(0, PathStr.size() - Delim.size());
+
+ if (!PathStr.empty()) {
+ if (!sys::fs::is_directory(PathStr)) {
+ AllExisted = false;
+ if (Mode == SplitMode::AllowNonExistant)
+ Paths.push_back(PathStr);
+ else if (Verbose)
+ LogNonExistantDirectory(PathStr);
+ } else
+ Paths.push_back(PathStr);
+ }
+
+ return AllExisted;
+
+#undef DEBUG_TYPE
+}
+
+AutoLoadDynamicLibraryLookup ::AutoLoadDynamicLibraryLookup() {
+ const SmallVector<const char *, 10> kSysLibraryEnv = {
+ "LD_LIBRARY_PATH",
+#if __APPLE__
+ "DYLD_LIBRARY_PATH",
+ "DYLD_FALLBACK_LIBRARY_PATH",
+ /*
+ "DYLD_VERSIONED_LIBRARY_PATH",
+ "DYLD_FRAMEWORK_PATH",
+ "DYLD_FALLBACK_FRAMEWORK_PATH",
+ "DYLD_VERSIONED_FRAMEWORK_PATH",
+ */
+#elif defined(_WIN32)
+ "PATH",
+#endif
+ };
+
+ // Behaviour is to not add paths that don't exist...In an interpreted env
+ // does this make sense? Path could pop into existance at any time.
+ for (const char *Var : kSysLibraryEnv) {
+ if (const char *Env = ::getenv(Var)) {
+ SmallVector<StringRef, 10> CurPaths;
+ SplitPaths(Env, CurPaths, SplitMode::PruneNonExistant, kEnvDelim);
+ for (const auto &Path : CurPaths)
+ addSearchPath(Path);
+ }
+ }
+
+ // $CWD is the last user path searched.
+ addSearchPath(".");
+
+ SmallVector<std::string, 64> SysPaths;
+ GetSystemLibraryPaths(SysPaths);
+
+ for (const std::string &P : SysPaths)
+ addSearchPath(P, /*IsUser*/ false);
+}
+///\returns substitution of pattern in the front of original with replacement
+/// Example: substFront("@rpath/abc", "@rpath/", "/tmp") -> "/tmp/abc"
+static std::string substFront(StringRef original, StringRef pattern,
+ StringRef replacement) {
+ if (!original.starts_with_insensitive(pattern))
+ return original.str();
+ SmallString<512> result(replacement);
+ result.append(original.drop_front(pattern.size()));
+ return result.str().str();
+}
+
+///\returns substitution of all known linker variables in \c original
+static std::string substAll(StringRef original, StringRef libLoader) {
+
+ // Handle substitutions (MacOS):
+ // @rpath - This function does not substitute @rpath, becouse
+ // this variable is already handled by lookupLibrary where
+ // @rpath is replaced with all paths from RPATH one by one.
+ // @executable_path - Main program path.
+ // @loader_path - Loader library (or main program) path.
+ //
+ // Handle substitutions (Linux):
+ // https://man7.org/linux/man-pages/man8/ld.so.8.html
+ // $origin - Loader library (or main program) path.
+ // $lib - lib lib64
+ // $platform - x86_64 AT_PLATFORM
+
+ std::string result;
+#ifdef __APPLE__
+ SmallString<512> mainExecutablePath(
+ llvm::sys::fs::getMainExecutable(nullptr, nullptr));
+ llvm::sys::path::remove_filename(mainExecutablePath);
+ SmallString<512> loaderPath;
+ if (libLoader.empty()) {
+ loaderPath = mainExecutablePath;
+ } else {
+ loaderPath = libLoader.str();
+ llvm::sys::path::remove_filename(loaderPath);
+ }
+
+ result = substFront(original, "@executable_path", mainExecutablePath);
+ result = substFront(result, "@loader_path", loaderPath);
+ return result;
+#else
+ SmallString<512> loaderPath;
+ if (libLoader.empty()) {
+ loaderPath = llvm::sys::fs::getMainExecutable(nullptr, nullptr);
+ } else {
+ loaderPath = libLoader.str();
+ }
+ llvm::sys::path::remove_filename(loaderPath);
+
+ result = substFront(original, "$origin", loaderPath);
+ // result = substFront(result, "$lib", true?"lib":"lib64");
+ // result = substFront(result, "$platform", "x86_64");
+ return result;
+#endif
+}
+
+std::string AutoLoadDynamicLibraryLookup::lookupLibInPaths(
+ StringRef libStem, SmallVector<llvm::StringRef, 2> RPath /*={}*/,
+ SmallVector<llvm::StringRef, 2> RunPath /*={}*/,
+ StringRef libLoader /*=""*/) const {
+#define DEBUG_TYPE "Dyld::lookupLibInPaths"
+
+ LLVM_DEBUG(dbgs() << "Dyld::lookupLibInPaths" << libStem.str()
+ << ", ..., libLoader=" << libLoader << "\n");
+
+ // Lookup priority is: RPATH, LD_LIBRARY_PATH/SearchPaths, RUNPATH
+ // See: https://en.wikipedia.org/wiki/Rpath
+ // See: https://amir.rachum.com/blog/2016/09/17/shared-libraries/
+
+ LLVM_DEBUG({
+ dbgs() << "Dyld::lookupLibInPaths: \n";
+ dbgs() << ":: RPATH\n";
+ for (auto Info : RPath) {
+ dbgs() << ":::: " << Info.str() << "\n";
+ }
+ dbgs() << ":: SearchPaths (LD_LIBRARY_PATH, etc...)\n";
+ for (auto Info : getSearchPaths()) {
+ dbgs() << ":::: " << Info.Path
+ << ", user=" << (Info.IsUser ? "true" : "false") << "\n";
+ }
+ dbgs() << ":: RUNPATH\n";
+ for (auto Info : RunPath) {
+ dbgs() << ":::: " << Info.str() << "\n";
+ }
+ });
+
+ SmallString<512> ThisPath;
+ // RPATH
+ for (auto Info : RPath) {
+ ThisPath = substAll(Info, libLoader);
+ llvm::sys::path::append(ThisPath, libStem);
+ // to absolute path?
+ LLVM_DEBUG(dbgs() << "## Try: " << ThisPath);
+ if (isSharedLibrary(ThisPath.str())) {
+ LLVM_DEBUG(dbgs() << " ... Found (in RPATH)!\n");
+ return ThisPath.str().str();
+ }
+ }
+ // SearchPaths
+ for (const SearchPathInfo &Info : SearchPaths) {
+ ThisPath = Info.Path;
+ llvm::sys::path::append(ThisPath, libStem);
+ // to absolute path?
+ LLVM_DEBUG(dbgs() << "## Try: " << ThisPath);
+ if (isSharedLibrary(ThisPath.str())) {
+ LLVM_DEBUG(dbgs() << " ... Found (in SearchPaths)!\n");
+ return ThisPath.str().str();
+ }
+ }
+ // RUNPATH
+ for (auto Info : RunPath) {
+ ThisPath = substAll(Info, libLoader);
+ llvm::sys::path::append(ThisPath, libStem);
+ // to absolute path?
+ LLVM_DEBUG(dbgs() << "## Try: " << ThisPath);
+ if (isSharedLibrary(ThisPath.str())) {
+ LLVM_DEBUG(dbgs() << " ... Found (in RUNPATH)!\n");
+ return ThisPath.str().str();
+ }
+ }
+
+ LLVM_DEBUG(dbgs() << "## NotFound!!!\n");
+
+ return "";
+
+#undef DEBUG_TYPE
+}
+
+std::string AutoLoadDynamicLibraryLookup::lookupLibMaybeAddExt(
+ StringRef libStem, SmallVector<llvm::StringRef, 2> RPath /*={}*/,
+ SmallVector<llvm::StringRef, 2> RunPath /*={}*/,
+ StringRef libLoader /*=""*/) const {
+#define DEBUG_TYPE "Dyld::lookupLibMaybeAddExt:"
+
+ using namespace llvm::sys;
+
+ LLVM_DEBUG(dbgs() << "Dyld::lookupLibMaybeAddExt: " << libStem.str()
+ << ", ..., libLoader=" << libLoader << "\n");
+
+ std::string foundDyLib = lookupLibInPaths(libStem, RPath, RunPath, libLoader);
+
+ if (foundDyLib.empty()) {
+ // Add DyLib extension:
+ SmallString<512> filenameWithExt(libStem);
+#if defined(LLVM_ON_UNIX)
+#ifdef __APPLE__
+ SmallString<512>::iterator IStemEnd = filenameWithExt.end() - 1;
+#endif
+ static const char *DyLibExt = ".so";
+#elif defined(_WIN32)
+ static const char *DyLibExt = ".dll";
+#else
+#error "Unsupported platform."
+#endif
+ filenameWithExt += DyLibExt;
+ foundDyLib = lookupLibInPaths(filenameWithExt, RPath, RunPath, libLoader);
+#ifdef __APPLE__
+ if (foundDyLib.empty()) {
+ filenameWithExt.erase(IStemEnd + 1, filenameWithExt.end());
+ filenameWithExt += ".dylib";
+ foundDyLib = lookupLibInPaths(filenameWithExt, RPath, RunPath, libLoader);
+ }
+#endif
+ }
+
+ if (foundDyLib.empty())
+ return std::string();
+
+ // get canonical path name and check if already loaded
+ const std::string Path = NormalizePath(foundDyLib);
+ if (Path.empty()) {
+ LLVM_DEBUG(
+ dbgs() << "AutoLoadDynamicLibraryLookup::lookupLibMaybeAddExt(): "
+ << "error getting real (canonical) path of library "
+ << foundDyLib << '\n');
+ return foundDyLib;
+ }
+ return Path;
+
+#undef DEBUG_TYPE
+}
+
+std::string AutoLoadDynamicLibraryLookup::normalizePath(StringRef path) {
+#define DEBUG_TYPE "Dyld::normalizePath:"
+ // Make the path canonical if the file exists.
+ const std::string Path = path.str();
+ struct stat buffer;
+ if (::stat(Path.c_str(), &buffer) != 0)
+ return std::string();
+
+ const std::string NPath = NormalizePath(Path);
+ if (NPath.empty())
+ LLVM_DEBUG(dbgs() << "Could not normalize: '" << Path << "'");
+ return NPath;
+#undef DEBUG_TYPE
+}
+
+std::string RPathToStr2(SmallVector<StringRef, 2> V) {
+ std::string result;
+ for (auto item : V)
+ result += item.str() + ",";
+ if (!result.empty())
+ result.pop_back();
+ return result;
+}
+
+std::string AutoLoadDynamicLibraryLookup::lookupLibrary(
+ StringRef libStem, SmallVector<llvm::StringRef, 2> RPath /*={}*/,
+ SmallVector<llvm::StringRef, 2> RunPath /*={}*/,
+ StringRef libLoader /*=""*/, bool variateLibStem /*=true*/) const {
+#define DEBUG_TYPE "Dyld::lookupLibrary:"
+ LLVM_DEBUG(dbgs() << "Dyld::lookupLibrary: " << libStem.str() << ", "
+ << RPathToStr2(RPath) << ", " << RPathToStr2(RunPath)
+ << ", " << libLoader.str() << "\n");
+ if (libStem.empty())
+ return std::string();
+
+ // If it is an absolute path, don't try iterate over the paths.
+ if (llvm::sys::path::is_absolute(libStem)) {
+ if (isSharedLibrary(libStem))
+ return normalizePath(libStem);
+
+ LLVM_DEBUG(dbgs() << "Dyld::lookupLibrary: '" << libStem.str() << "'"
+ << "is not a shared library\n");
+ return std::string();
+ }
+
+ // Subst all known linker variables ($origin, @rpath, etc.)
+#ifdef __APPLE__
+ // On MacOS @rpath is preplaced by all paths in RPATH one by one.
+ if (libStem.starts_with_insensitive("@rpath")) {
+ for (auto &P : RPath) {
+ std::string result = substFront(libStem, "@rpath", P);
+ if (isSharedLibrary(result))
+ return normalizePath(result);
+ }
+ } else {
+#endif
+ std::string result = substAll(libStem, libLoader);
+ if (isSharedLibrary(result))
+ return normalizePath(result);
+#ifdef __APPLE__
+ }
+#endif
+
+ // Expand libStem with paths, extensions, etc.
+ std::string foundName;
+ if (variateLibStem) {
+ foundName = lookupLibMaybeAddExt(libStem, RPath, RunPath, libLoader);
+ if (foundName.empty()) {
+ StringRef libStemName = llvm::sys::path::filename(libStem);
+ if (!libStemName.starts_with("lib")) {
+ // try with "lib" prefix:
+ foundName = lookupLibMaybeAddExt(
+ libStem.str().insert(libStem.size() - libStemName.size(), "lib"),
+ RPath, RunPath, libLoader);
+ }
+ }
+ } else {
+ foundName = lookupLibInPaths(libStem, RPath, RunPath, libLoader);
+ }
+
+ if (!foundName.empty())
+ return NormalizePath(foundName);
+
+ return std::string();
+#undef DEBUG_TYPE
+}
+
+void AutoLoadDynamicLibraryLookup::addLoadedLib(StringRef lib) {
+ LoadedLibraries.insert(lib);
+}
+
+bool AutoLoadDynamicLibraryLookup::isLibraryLoaded(StringRef fullPath) const {
+ std::string canonPath = normalizePath(fullPath);
+ if (LoadedLibraries.find(canonPath) != LoadedLibraries.end())
+ return true;
+ return false;
+}
+
+void AutoLoadDynamicLibraryLookup::dump(
+ llvm::raw_ostream *S /*= nullptr*/) const {
+ llvm::raw_ostream &OS = S ? *S : llvm::outs();
+
+ // FIXME: print in a stable order the contents of SearchPaths
+ for (const auto &Info : getSearchPaths()) {
+ if (!Info.IsUser)
+ OS << "[system] ";
+ OS << Info.Path.c_str() << "\n";
+ }
+}
+
+#if defined(_WIN32)
+static bool IsDLL(llvm::StringRef headers) {
+ using namespace llvm::support::endian;
+
+ uint32_t headeroffset = read32le(headers.data() + 0x3c);
+ auto peheader = headers.substr(headeroffset, 24);
+ if (peheader.size() != 24) {
+ return false;
+ }
+ // Read Characteristics from the coff header
+ uint32_t characteristics = read16le(peheader.data() + 22);
+ return (characteristics & llvm::COFF::IMAGE_FILE_DLL) != 0;
+}
+#endif
+
+bool AutoLoadDynamicLibraryLookup::isSharedLibrary(StringRef libFullPath,
+ bool *exists /*=0*/) {
+ using namespace llvm;
+
+ auto filetype = sys::fs::get_file_type(libFullPath, /*Follow*/ true);
+ if (filetype != sys::fs::file_type::regular_file) {
+ if (exists) {
+ // get_file_type returns status_error also in case of file_not_found.
+ *exists = filetype != sys::fs::file_type::status_error;
+ }
+ return false;
+ }
+
+ // Do not use the identify_magic overload taking a path: It will open the
+ // file and then mmap its contents, possibly causing bus errors when another
+ // process truncates the file while we are trying to read it. Instead just
+ // read the first 1024 bytes, which should be enough for identify_magic to
+ // do its work.
+ // TODO: Fix the code upstream and consider going back to calling the
+ // convenience function after a future LLVM upgrade.
+ std::string path = libFullPath.str();
+ std::ifstream in(path, std::ios::binary);
+ char header[1024] = {0};
+ in.read(header, sizeof(header));
+ if (in.fail()) {
+ if (exists)
+ *exists = false;
+ return false;
+ }
+
+ StringRef headerStr(header, in.gcount());
+ file_magic Magic = identify_magic(headerStr);
+
+ bool result =
+#ifdef __APPLE__
+ (Magic == file_magic::macho_fixed_virtual_memory_shared_lib ||
+ Magic == file_magic::macho_dynamically_linked_shared_lib ||
+ Magic == file_magic::macho_dynamically_linked_shared_lib_stub ||
+ Magic == file_magic::macho_universal_binary)
+#elif defined(LLVM_ON_UNIX)
+#ifdef __CYGWIN__
+ (Magic == file_magic::pecoff_executable)
+#else
+ (Magic == file_magic::elf_shared_object)
+#endif
+#elif defined(_WIN32)
+ // We should only include dll libraries without including executables,
+ // object code and others...
+ (Magic == file_magic::pecoff_executable && IsDLL(headerStr))
+#else
+#error "Unsupported platform."
+#endif
+ ;
+
+ return result;
+}
+
+} // end namespace orc
+} // end namespace llvm
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
index 3d1dfe758c79dd..00f142c23356d6 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
@@ -10,6 +10,8 @@ if( LLVM_USE_INTEL_JITEVENTS )
endif()
add_llvm_component_library(LLVMOrcTargetProcess
+ AutoLoadDylibLookup.cpp
+ AutoLoadDyLoader.cpp
ExecutorSharedMemoryMapperService.cpp
JITLoaderGDB.cpp
JITLoaderPerf.cpp
@@ -30,6 +32,7 @@ add_llvm_component_library(LLVMOrcTargetProcess
LINK_COMPONENTS
${intel_jit_profiling}
+ Object
OrcShared
Support
TargetParser
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp
index b7e256a826ca43..20f1f45df50d28 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp
@@ -11,6 +11,10 @@
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
#include "llvm/Support/FormatVariadic.h"
+#include <dlfcn.h>
+#include <string>
+#include <unordered_map>
+
#define DEBUG_TYPE "orc"
namespace llvm {
@@ -65,6 +69,7 @@ SimpleExecutorDylibManager::lookup(tpctypes::DylibHandle H,
#endif
void *Addr = DL.getAddressOfSymbol(DemangledSymName);
+
if (!Addr && E.Required)
return make_error<StringError>(Twine("Missing definition for ") +
DemangledSymName,
@@ -78,6 +83,74 @@ SimpleExecutorDylibManager::lookup(tpctypes::DylibHandle H,
return Result;
}
+Expected<ResolveResult>
+SimpleExecutorDylibManager::resolve(const RemoteSymbolLookupSet &L) {
+ if (!DylibLookup.has_value()) {
+ DylibLookup.emplace();
+ DylibLookup->initializeDynamicLoader([](llvm::StringRef) { /*ignore*/
+ return false;
+ });
+ }
+
+ BloomFilter Filter;
+ std::vector<ExecutorSymbolDef> Result;
+ for (auto &E : L) {
+
+ if (E.Name.empty()) {
+ if (E.Required)
+ return make_error<StringError>("Required address for empty symbol \"\"",
+ inconvertibleErrorCode());
+ else
+ Result.push_back(ExecutorSymbolDef());
+ } else {
+ const char *DemangledSymName = E.Name.c_str();
+#ifdef __APPLE__
+ if (E.Name.front() != '_')
+ return make_error<StringError>(Twine("MachO symbol \"") + E.Name +
+ "\" missing leading '_'",
+ inconvertibleErrorCode());
+ ++DemangledSymName;
+#endif
+
+ void *Addr =
+ sys::DynamicLibrary::SearchForAddressOfSymbol(DemangledSymName);
+
+ if (Addr) {
+ Result.push_back(
+ {ExecutorAddr::fromPtr(Addr), JITSymbolFlags::Exported});
+ continue;
+ }
+
+ auto lib = DylibLookup->searchLibrariesForSymbol(E.Name);
+ auto canonicalLoadedLib = DylibLookup->lookupLibrary(lib);
+ if (canonicalLoadedLib.empty()) {
+ if (!Filter.IsInitialized())
+ DylibLookup->BuildGlobalBloomFilter(Filter);
+ Result.push_back(ExecutorSymbolDef());
+ } else {
+ auto H = open(canonicalLoadedLib, 0);
+ if (!H)
+ return H.takeError();
+
+ DylibLookup->addLoadedLib(canonicalLoadedLib);
+ void *Addr = dlsym(H.get().toPtr<void *>(), DemangledSymName);
+ if (!Addr)
+ return make_error<StringError>(Twine("Missing definition for ") +
+ DemangledSymName,
+ inconvertibleErrorCode());
+ Result.push_back(
+ {ExecutorAddr::fromPtr(Addr), JITSymbolFlags::Exported});
+ }
+ }
+ }
+
+ ResolveResult Res;
+ if (Filter.IsInitialized())
+ Res.Filter.emplace(std::move(Filter));
+ Res.SymbolDef = std::move(Result);
+ return Res;
+}
+
Error SimpleExecutorDylibManager::shutdown() {
DylibSet DS;
@@ -97,6 +170,8 @@ void SimpleExecutorDylibManager::addBootstrapSymbols(
ExecutorAddr::fromPtr(&openWrapper);
M[rt::SimpleExecutorDylibManagerLookupWrapperName] =
ExecutorAddr::fromPtr(&lookupWrapper);
+ M[rt::SimpleExecutorDylibManagerResolveWrapperName] =
+ ExecutorAddr::fromPtr(&resolveWrapper);
}
llvm::orc::shared::CWrapperFunctionResult
@@ -119,6 +194,17 @@ SimpleExecutorDylibManager::lookupWrapper(const char *ArgData, size_t ArgSize) {
.release();
}
+llvm::orc::shared::CWrapperFunctionResult
+SimpleExecutorDylibManager::resolveWrapper(const char *ArgData,
+ size_t ArgSize) {
+ return shared::WrapperFunction<
+ rt::SPSSimpleExecutorDylibManagerResolveSignature>::
+ handle(ArgData, ArgSize,
+ shared::makeMethodWrapperHandler(
+ &SimpleExecutorDylibManager::resolve))
+ .release();
+}
+
} // namespace rt_bootstrap
} // end namespace orc
} // end namespace llvm
>From 68d85e5c56a861c68ff3338e2b90adf9b26b34c5 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Fri, 20 Sep 2024 14:20:01 +0530
Subject: [PATCH 2/2] Minor Fix
---
.../llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h | 8 ++++----
.../Orc/TargetProcess/SimpleExecutorDylibManager.cpp | 1 -
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h
index 6bb1b40b1db933..0147aa538fbf7a 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h
@@ -82,13 +82,13 @@ class EPCGenericDylibManager {
void lookupAsync(tpctypes::DylibHandle H, const RemoteSymbolLookupSet &Lookup,
SymbolLookupCompleteFn Complete);
- /// Looks up symbols within the given dylib.
+ /// Look up and resolve symbols across all available dynamic libraries.
void resolveAsync(const SymbolLookupSet &Lookup,
- ResolveSymbolsCompleteFn Complete);
+ ResolveSymbolsCompleteFn Complete);
- /// Looks up symbols within the given dylib.
+ /// Look up and resolve symbols across all available dynamic libraries.
void resolveAsync(const RemoteSymbolLookupSet &Lookup,
- ResolveSymbolsCompleteFn Complete);
+ ResolveSymbolsCompleteFn Complete);
private:
ExecutorProcessControl &EPC;
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp
index 20f1f45df50d28..92a5a2abd59899 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp
@@ -69,7 +69,6 @@ SimpleExecutorDylibManager::lookup(tpctypes::DylibHandle H,
#endif
void *Addr = DL.getAddressOfSymbol(DemangledSymName);
-
if (!Addr && E.Required)
return make_error<StringError>(Twine("Missing definition for ") +
DemangledSymName,
More information about the llvm-commits
mailing list