From a738a0f99dbf51df88a0f565a2c49a51d3a395bc Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Thu, 19 Sep 2024 17:16:45 +0530 Subject: [PATCH 1/9] [ORC] Add Auto-Loading DyLib Feature with Symbol Resolution --- .../Orc/EPCDynamicLibrarySearchGenerator.h | 42 + .../Orc/EPCGenericDylibManager.h | 12 + .../Orc/ExecutorProcessControl.h | 6 +- .../Orc/Shared/AutoLoadDylibUtils.h | 212 +++ .../ExecutionEngine/Orc/Shared/OrcRTBridge.h | 7 + .../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 ++ 16 files changed, 2585 insertions(+), 7 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 e56afe4fe656b..901e5d05bbeb1 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; + + /// 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> + GetForTargetProcess(ExecutionSession &ES, + AddAbsoluteSymbolsFn AddAbsoluteSymbols = nullptr) { + return std::make_unique( + 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 887147a77026e..6bb1b40b1db93 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>)>; + using ResolveSymbolsCompleteFn = + unique_function)>; + /// 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 dcf5592f1717c..826bf2ae4e7db 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h @@ -16,6 +16,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" #include "llvm/ExecutionEngine/Orc/DylibManager.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" @@ -505,7 +506,10 @@ class SelfExecutorProcessControl : public ExecutorProcessControl, void lookupSymbolsAsync(ArrayRef Request, SymbolLookupCompleteFn F) override; - + + void resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn F) override; + std::unique_ptr OwnedMemMgr; char GlobalManglingPrefix = 0; }; 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 0000000000000..73e8b53edf859 --- /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 +#include +#include + +namespace llvm { +namespace orc { + +namespace shared { +using SPSBloomFilter = + SPSTuple>; +} + +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 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(ceil((-1.44f * SymbolsCount * log2f(P)) / Bits)); + BloomShift = std::min(6u, log2u(SymbolsCount)); + BloomTable.resize(BloomSize, 0); + } + + friend class shared::SPSSerializationTraits; + +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 Filter; + std::vector SymbolDef; +}; + +namespace shared { + +template <> class SPSSerializationTraits { +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 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, SPSSequence>; +template <> class SPSSerializationTraits { +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 aed43f6308cba..bbfa6387e37fc 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; @@ -60,6 +62,11 @@ using SPSSimpleExecutorDylibManagerLookupSignature = shared::SPSExecutorAddr, shared::SPSExecutorAddr, shared::SPSRemoteSymbolLookupSet); +using SPSSimpleExecutorDylibManagerResolveSignature = shared::SPSExpected< + shared::SPSTuple, + shared::SPSSequence>>( + shared::SPSExecutorAddr, shared::SPSRemoteSymbolLookupSet); + using SPSSimpleExecutorMemoryManagerReserveSignature = shared::SPSExpected(shared::SPSExecutorAddr, uint64_t); 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 0000000000000..83ad22a183f36 --- /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; + +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 RPath = {}, + SmallVector 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 RPath = {}, + SmallVector 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 RPath = {}, + SmallVector 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 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 static std::string getSymbolLocation(T func) { + static_assert(std::is_pointer::value, "Must be a function pointer!"); + return getSymbolLocation(reinterpret_cast(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 &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 00fd84e3ec142..132c3c2904732 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> lookup(tpctypes::DylibHandle H, const RemoteSymbolLookupSet &L); + Expected resolve(const RemoteSymbolLookupSet &L); + Error shutdown() override; void addBootstrapSymbols(StringMap &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 DylibLookup; }; } // end namespace rt_bootstrap diff --git a/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp b/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp index 2a93fcbf6c8c8..e58f1d1ad51c9 100644 --- a/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp +++ b/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp @@ -38,9 +38,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; @@ -95,5 +95,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 f98b18ccd0dc7..5382c0d7e5cdd 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( + SAs.Resolve, + [Complete = std::move(Complete)](Error SerializationErr, + Expected 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( + SAs.Resolve, + [Complete = std::move(Complete)](Error SerializationErr, + Expected 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 aa799687e6d5d..bf9aa2a47e34e 100644 --- a/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp +++ b/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp @@ -115,6 +115,11 @@ void SelfExecutorProcessControl::lookupSymbolsAsync( Complete(std::move(R)); } +void SelfExecutorProcessControl::resolveSymbolsAsync( + ArrayRef Request, ResolveSymbolsCompleteFn F) { + llvm_unreachable("Unsupported"); +} + Expected SelfExecutorProcessControl::runAsMain(ExecutorAddr MainFnAddr, ArrayRef Args) { diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp index 54a25c007c589..71d6fe9d1b214 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 53d5e049798aa..9cfd2d47e5f78 100644 --- a/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp +++ b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp @@ -56,11 +56,45 @@ lookupSymbolsAsyncHelper(EPCGenericDylibManager &DylibMgr, }); } +static void +resolveSymbolsAsyncHelper(EPCGenericDylibManager &DylibMgr, + ArrayRef Request, + std::vector 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 Request, SymbolLookupCompleteFn Complete) { lookupSymbolsAsyncHelper(*EPCDylibMgr, Request, {}, std::move(Complete)); } +void SimpleRemoteEPC::resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn Complete) { + resolveSymbolsAsyncHelper(*DylibMgr, Request, {}, std::move(Complete)); +} + Expected SimpleRemoteEPC::runAsMain(ExecutorAddr MainFnAddr, ArrayRef Args) { int64_t Result = 0; @@ -308,8 +342,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(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 0000000000000..334507b6d371a --- /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 +#include +#include +#include +#include + +#ifdef LLVM_ON_UNIX +#include +#include +#include +#endif // LLVM_ON_UNIX + +#ifdef __APPLE__ +#include +#include +#undef LC_LOAD_DYLIB +#undef LC_RPATH +#endif // __APPLE__ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include // For GetModuleFileNameA +#include // For VirtualQuery +#include +#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 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()(item.Path.length()) ^ + std::hash()(item.LibName); + } + }; + + std::vector Libs; + std::unordered_set 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 &GetLibraries() const { return Libs; } +}; + +#ifndef _WIN32 +// Cached version of system function lstat +static inline mode_t cached_lstat(const char *path) { + static StringMap 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(path, st_mode)); + return st_mode; +} + +// Cached version of system function readlink +static inline StringRef cached_readlink(const char *pathname) { + static StringMap 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(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> 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 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>( + path, std::pair("", ENOENT))); + errno = ENOENT; + return ""; + } + } +#else + sys::fs::real_path(path, result); +#endif + cache.insert(std::pair>( + path, std::pair(result.str().str(), errno))); + return result.str().str(); +} + +using namespace llvm; +using namespace object; + +template +static Expected getDynamicStrTab(const ELFFile *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(*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(hashContent + 8); + uint32_t shift2 = *reinterpret_cast(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(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()(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 + 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; + 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 V) { + std::string result; + for (auto item : V) + result += item.str() + ","; + if (!result.empty()) + result.pop_back(); + return result; +} + +template +void HandleDynTab(const ELFFile *Elf, StringRef FileName, + SmallVector &RPath, + SmallVector &RunPath, + std::vector &Deps, bool &isPIEExecutable) { +#define DEBUG_TYPE "Dyld:" + const char *Data = ""; + if (Expected 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 ScannedPaths; + // FileName must be always full/absolute/resolved file name. + std::function 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 RPath, RunPath; + std::vector 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(BinObjF)) + HandleDynTab(&ELF->getELFFile(), FileName, RPath, RunPath, Deps, + isPIEExecutable); + else if (const auto *ELF = dyn_cast(BinObjF)) + HandleDynTab(&ELF->getELFFile(), FileName, RPath, RunPath, Deps, + isPIEExecutable); + else if (const auto *ELF = dyn_cast(BinObjF)) + HandleDynTab(&ELF->getELFFile(), FileName, RPath, RunPath, Deps, + isPIEExecutable); + else if (const auto *ELF = dyn_cast(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(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 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 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(BinObjFile); + ProcessSymbols(ElfObj->getDynamicSymbolIterators()); + } else if (BinObjFile->isCOFF()) { // On Windows, the symbols are present in + // COFF format. + object::COFFObjectFile *CoffObj = cast(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(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 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 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(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(*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(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 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 0000000000000..b474870cd28cc --- /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 +#include +#include + +static bool GetSystemLibraryPaths(llvm::SmallVectorImpl &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 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 &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 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 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 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 CurPaths; + SplitPaths(Env, CurPaths, SplitMode::PruneNonExistant, kEnvDelim); + for (const auto &Path : CurPaths) + addSearchPath(Path); + } + } + + // $CWD is the last user path searched. + addSearchPath("."); + + SmallVector 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 RPath /*={}*/, + SmallVector 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 RPath /*={}*/, + SmallVector 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 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 RPath /*={}*/, + SmallVector 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 03677d610cbb7..ab109b49bdd21 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt @@ -13,6 +13,8 @@ if( LLVM_USE_INTEL_JITEVENTS ) endif() add_llvm_component_library(LLVMOrcTargetProcess + AutoLoadDylibLookup.cpp + AutoLoadDyLoader.cpp ExecutorSharedMemoryMapperService.cpp JITLoaderGDB.cpp JITLoaderPerf.cpp @@ -33,6 +35,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 db6f20130db9e..46e94b6246a6e 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp @@ -10,6 +10,10 @@ #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" +#include +#include +#include + #define DEBUG_TYPE "orc" namespace llvm { @@ -64,6 +68,7 @@ SimpleExecutorDylibManager::lookup(tpctypes::DylibHandle H, #endif void *Addr = DL.getAddressOfSymbol(DemangledSymName); + if (!Addr && E.Required) return make_error(Twine("Missing definition for ") + DemangledSymName, @@ -77,6 +82,74 @@ SimpleExecutorDylibManager::lookup(tpctypes::DylibHandle H, return Result; } +Expected +SimpleExecutorDylibManager::resolve(const RemoteSymbolLookupSet &L) { + if (!DylibLookup.has_value()) { + DylibLookup.emplace(); + DylibLookup->initializeDynamicLoader([](llvm::StringRef) { /*ignore*/ + return false; + }); + } + + BloomFilter Filter; + std::vector Result; + for (auto &E : L) { + + if (E.Name.empty()) { + if (E.Required) + return make_error("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(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(), DemangledSymName); + if (!Addr) + return make_error(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; @@ -96,6 +169,8 @@ void SimpleExecutorDylibManager::addBootstrapSymbols( ExecutorAddr::fromPtr(&openWrapper); M[rt::SimpleExecutorDylibManagerLookupWrapperName] = ExecutorAddr::fromPtr(&lookupWrapper); + M[rt::SimpleExecutorDylibManagerResolveWrapperName] = + ExecutorAddr::fromPtr(&resolveWrapper); } llvm::orc::shared::CWrapperFunctionResult @@ -118,6 +193,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 f00b9d0e6103a5f81840e34478cdb940297a6735 Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Fri, 20 Sep 2024 14:20:01 +0530 Subject: [PATCH 2/9] 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 6bb1b40b1db93..0147aa538fbf7 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 46e94b6246a6e..401f026aa78d8 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp @@ -68,7 +68,6 @@ SimpleExecutorDylibManager::lookup(tpctypes::DylibHandle H, #endif void *Addr = DL.getAddressOfSymbol(DemangledSymName); - if (!Addr && E.Required) return make_error(Twine("Missing definition for ") + DemangledSymName, From cdac8e972c5e9f9a6f9a2de21070216931b734b1 Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Mon, 30 Sep 2024 15:49:49 +0530 Subject: [PATCH 3/9] Refactor code --- .../llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h | 3 +-- .../Orc/TargetProcess/AutoLoadDyLoader.cpp | 2 +- .../Orc/TargetProcess/AutoLoadDylibLookUp.cpp | 8 ++++---- .../Orc/TargetProcess/SimpleExecutorDylibManager.cpp | 2 -- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h index 73e8b53edf859..d1ea9e45d0921 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h @@ -83,8 +83,7 @@ class BloomFilter { BloomFilter(const BloomFilter &other) noexcept : Initialized(other.Initialized), SymbolsCount(other.SymbolsCount), BloomSize(other.BloomSize), BloomShift(other.BloomShift), - BloomTable(other.BloomTable) { - } + BloomTable(other.BloomTable) {} BloomFilter &operator=(const BloomFilter &other) = delete; BloomFilter(BloomFilter &&other) noexcept diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp index 334507b6d371a..d79a41022b5b5 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp @@ -491,7 +491,7 @@ class DynamicLoader { : AutoLoadDylibMgr(DLM), ShouldPermanentlyIgnoreCallback(shouldIgnore), ExecutableFormat(execFormat) {} - ~DynamicLoader(){}; + ~DynamicLoader() {}; std::string searchLibrariesForSymbol(StringRef mangledName, bool searchSystem); diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp index b474870cd28cc..da14700296224 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp @@ -169,10 +169,10 @@ bool SplitPaths(StringRef PathStr, SmallVectorImpl &Paths, AutoLoadDynamicLibraryLookup ::AutoLoadDynamicLibraryLookup() { const SmallVector kSysLibraryEnv = { - "LD_LIBRARY_PATH", + "LD_LIBRARY_PATH", #if __APPLE__ - "DYLD_LIBRARY_PATH", - "DYLD_FALLBACK_LIBRARY_PATH", + "DYLD_LIBRARY_PATH", + "DYLD_FALLBACK_LIBRARY_PATH", /* "DYLD_VERSIONED_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH", @@ -180,7 +180,7 @@ AutoLoadDynamicLibraryLookup ::AutoLoadDynamicLibraryLookup() { "DYLD_VERSIONED_FRAMEWORK_PATH", */ #elif defined(_WIN32) - "PATH", + "PATH", #endif }; diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp index 401f026aa78d8..8f8f2a4f4f1fb 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp @@ -11,8 +11,6 @@ #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" #include -#include -#include #define DEBUG_TYPE "orc" From 4a58760f69264e2af7ed45102b124cc04b6ce02f Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Thu, 14 Nov 2024 14:47:37 +0530 Subject: [PATCH 4/9] Merge with main branch --- llvm/include/llvm/ExecutionEngine/Orc/DylibManager.h | 7 +++++++ .../ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h | 2 +- .../llvm/ExecutionEngine/Orc/ExecutorProcessControl.h | 1 - llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h | 3 +++ .../Orc/EPCDynamicLibrarySearchGenerator.cpp | 4 ++-- llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp | 4 ++-- 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/Orc/DylibManager.h b/llvm/include/llvm/ExecutionEngine/Orc/DylibManager.h index bbcc2e639a186..38699dfcac202 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/DylibManager.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/DylibManager.h @@ -13,6 +13,7 @@ #ifndef LLVM_EXECUTIONENGINE_ORC_DYLIBMANAGER_H #define LLVM_EXECUTIONENGINE_ORC_DYLIBMANAGER_H +#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h" #include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" #include "llvm/Support/Error.h" #include "llvm/Support/MSVCErrorWorkarounds.h" @@ -60,6 +61,9 @@ class DylibManager { using SymbolLookupCompleteFn = unique_function>)>; + using ResolveSymbolsCompleteFn = + unique_function>)>; + /// Search for symbols in the target process. /// /// The result of the lookup is a 2-dimensional array of target addresses @@ -68,6 +72,9 @@ class DylibManager { /// symbol is not found then it be assigned a '0' value. virtual void lookupSymbolsAsync(ArrayRef Request, SymbolLookupCompleteFn F) = 0; + + virtual void resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn F) = 0; }; } // end namespace llvm::orc diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h index 901e5d05bbeb1..cfb9353e7b12d 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h @@ -104,7 +104,7 @@ class AutoLoadDynamicLibrarySearchGenerator : public DefinitionGenerator { Error tryToResolve(SymbolNameSet CandidateSyms, - ExecutorProcessControl::ResolveSymbolsCompleteFn OnCompleteFn); + DylibManager::ResolveSymbolsCompleteFn OnCompleteFn); private: ExecutorProcessControl &EPC; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h index 826bf2ae4e7db..b6f0ca7ea73e7 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h @@ -16,7 +16,6 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" #include "llvm/ExecutionEngine/Orc/DylibManager.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" diff --git a/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h b/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h index 195cf80a1f0fd..54d84201ff408 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h @@ -122,6 +122,9 @@ class SimpleRemoteEPC : public ExecutorProcessControl, void lookupSymbolsAsync(ArrayRef Request, SymbolLookupCompleteFn F) override; + void resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn F) override; + using PendingCallWrapperResultsMap = DenseMap; diff --git a/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp b/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp index e58f1d1ad51c9..8e0c7f30c1a41 100644 --- a/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp +++ b/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp @@ -172,7 +172,7 @@ Error AutoLoadDynamicLibrarySearchGenerator::tryToGenerate( Error AutoLoadDynamicLibrarySearchGenerator::tryToResolve( SymbolNameSet CandidateSyms, - ExecutorProcessControl::ResolveSymbolsCompleteFn OnCompleteFn) { + DylibManager::ResolveSymbolsCompleteFn OnCompleteFn) { LLVM_DEBUG({ dbgs() << "AutoLoadDynamicLibrarySearchGenerator trying to resolve " @@ -185,7 +185,7 @@ Error AutoLoadDynamicLibrarySearchGenerator::tryToResolve( LookupSymbols.add(S, SymbolLookupFlags::WeaklyReferencedSymbol); } - EPC.resolveSymbolsAsync(LookupSymbols, std::move(OnCompleteFn)); + EPC.getDylibMgr().resolveSymbolsAsync(LookupSymbols, std::move(OnCompleteFn)); return Error::success(); } diff --git a/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp index 9cfd2d47e5f78..b954cf17cdce1 100644 --- a/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp +++ b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp @@ -60,7 +60,7 @@ static void resolveSymbolsAsyncHelper(EPCGenericDylibManager &DylibMgr, ArrayRef Request, std::vector Result, - SimpleRemoteEPC::ResolveSymbolsCompleteFn Complete) { + DylibManager::ResolveSymbolsCompleteFn Complete) { if (Request.empty()) return Complete(std::move(Result)); @@ -92,7 +92,7 @@ void SimpleRemoteEPC::lookupSymbolsAsync(ArrayRef Request, void SimpleRemoteEPC::resolveSymbolsAsync(ArrayRef Request, ResolveSymbolsCompleteFn Complete) { - resolveSymbolsAsyncHelper(*DylibMgr, Request, {}, std::move(Complete)); + resolveSymbolsAsyncHelper(*EPCDylibMgr, Request, {}, std::move(Complete)); } Expected SimpleRemoteEPC::runAsMain(ExecutorAddr MainFnAddr, From 8140d0356a96879bd92d6101a0daf74291cd34e5 Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Fri, 15 Nov 2024 10:21:49 +0530 Subject: [PATCH 5/9] Fix code format --- .../Orc/EPCDynamicLibrarySearchGenerator.h | 5 ++--- .../llvm/ExecutionEngine/Orc/ExecutorProcessControl.h | 4 ++-- .../Orc/TargetProcess/AutoLoadDyLoader.cpp | 2 +- .../Orc/TargetProcess/AutoLoadDylibLookUp.cpp | 8 ++++---- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h index cfb9353e7b12d..fc4e772d8953c 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h @@ -102,9 +102,8 @@ class AutoLoadDynamicLibrarySearchGenerator : public DefinitionGenerator { JITDylibLookupFlags JDLookupFlags, const SymbolLookupSet &Symbols) override; - Error - tryToResolve(SymbolNameSet CandidateSyms, - DylibManager::ResolveSymbolsCompleteFn OnCompleteFn); + Error tryToResolve(SymbolNameSet CandidateSyms, + DylibManager::ResolveSymbolsCompleteFn OnCompleteFn); private: ExecutorProcessControl &EPC; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h index b6f0ca7ea73e7..e111033eac266 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h @@ -505,10 +505,10 @@ class SelfExecutorProcessControl : public ExecutorProcessControl, void lookupSymbolsAsync(ArrayRef Request, SymbolLookupCompleteFn F) override; - + void resolveSymbolsAsync(ArrayRef Request, ResolveSymbolsCompleteFn F) override; - + std::unique_ptr OwnedMemMgr; char GlobalManglingPrefix = 0; }; diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp index d79a41022b5b5..334507b6d371a 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp @@ -491,7 +491,7 @@ class DynamicLoader { : AutoLoadDylibMgr(DLM), ShouldPermanentlyIgnoreCallback(shouldIgnore), ExecutableFormat(execFormat) {} - ~DynamicLoader() {}; + ~DynamicLoader(){}; std::string searchLibrariesForSymbol(StringRef mangledName, bool searchSystem); diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp index da14700296224..b474870cd28cc 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp @@ -169,10 +169,10 @@ bool SplitPaths(StringRef PathStr, SmallVectorImpl &Paths, AutoLoadDynamicLibraryLookup ::AutoLoadDynamicLibraryLookup() { const SmallVector kSysLibraryEnv = { - "LD_LIBRARY_PATH", + "LD_LIBRARY_PATH", #if __APPLE__ - "DYLD_LIBRARY_PATH", - "DYLD_FALLBACK_LIBRARY_PATH", + "DYLD_LIBRARY_PATH", + "DYLD_FALLBACK_LIBRARY_PATH", /* "DYLD_VERSIONED_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH", @@ -180,7 +180,7 @@ AutoLoadDynamicLibraryLookup ::AutoLoadDynamicLibraryLookup() { "DYLD_VERSIONED_FRAMEWORK_PATH", */ #elif defined(_WIN32) - "PATH", + "PATH", #endif }; From d8a3e1dd00493f50576947ae7249a69d3daff20d Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Fri, 15 Nov 2024 10:36:14 +0530 Subject: [PATCH 6/9] Fix minor compiler warning --- .../lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp index 334507b6d371a..39fc562a1c66e 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp @@ -942,8 +942,7 @@ bool DynamicLoader::ContainsSymbol(const LibraryPath *Lib, LLVM_DEBUG(dbgs() << "Dyld::ContainsSymbol: Iterate all for <" << mangledName.str() << ">"); - auto ForeachSymbol = - [&library_filename](iterator_range range, + auto ForeachSymbol = [](iterator_range range, unsigned IgnoreSymbolFlags, StringRef mangledName) -> bool { for (const object::SymbolRef &S : range) { From 2dc19f97dd7df8976ebbd21c8c7fd9d2b94bc93b Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Sat, 23 Nov 2024 20:43:50 +0530 Subject: [PATCH 7/9] Fix minor errors --- .../Orc/Shared/AutoLoadDylibUtils.h | 4 ++-- .../Orc/TargetProcess/AutoLoadDyLoader.cpp | 2 +- .../Orc/TargetProcess/AutoLoadDylibLookUp.cpp | 20 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h index d1ea9e45d0921..3adb17bb0dda7 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h @@ -49,7 +49,7 @@ class BloomFilter { } // Helper method for hash testing bool TestHash(uint32_t hash) const { - assert(IsInitialized && "Bloom filter is not initialized!"); + assert(Initialized && "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))); @@ -58,7 +58,7 @@ class BloomFilter { // Helper method to add a hash void AddHash(uint32_t hash) { - assert(IsInitialized && "Bloom filter is not initialized!"); + assert(Initialized && "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))); diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp index 39fc562a1c66e..ee281b858246b 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp @@ -491,7 +491,7 @@ class DynamicLoader { : AutoLoadDylibMgr(DLM), ShouldPermanentlyIgnoreCallback(shouldIgnore), ExecutableFormat(execFormat) {} - ~DynamicLoader(){}; + ~DynamicLoader() {}; std::string searchLibrariesForSymbol(StringRef mangledName, bool searchSystem); diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp index b474870cd28cc..adda9469a5832 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp @@ -169,18 +169,18 @@ bool SplitPaths(StringRef PathStr, SmallVectorImpl &Paths, AutoLoadDynamicLibraryLookup ::AutoLoadDynamicLibraryLookup() { const SmallVector kSysLibraryEnv = { - "LD_LIBRARY_PATH", + "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", - */ + "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", + "PATH", #endif }; From d5ab06cd59de2a142294a683bda9caa824adde07 Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Sun, 24 Nov 2024 09:39:28 +0530 Subject: [PATCH 8/9] Fix typo in Cmake config --- ...toLoadDylibLookUp.cpp => AutoLoadDylibLookup.cpp} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename llvm/lib/ExecutionEngine/Orc/TargetProcess/{AutoLoadDylibLookUp.cpp => AutoLoadDylibLookup.cpp} (99%) diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.cpp similarity index 99% rename from llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp rename to llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.cpp index adda9469a5832..da14700296224 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookUp.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.cpp @@ -173,12 +173,12 @@ AutoLoadDynamicLibraryLookup ::AutoLoadDynamicLibraryLookup() { #if __APPLE__ "DYLD_LIBRARY_PATH", "DYLD_FALLBACK_LIBRARY_PATH", - /* - "DYLD_VERSIONED_LIBRARY_PATH", - "DYLD_FRAMEWORK_PATH", - "DYLD_FALLBACK_FRAMEWORK_PATH", - "DYLD_VERSIONED_FRAMEWORK_PATH", - */ + /* + "DYLD_VERSIONED_LIBRARY_PATH", + "DYLD_FRAMEWORK_PATH", + "DYLD_FALLBACK_FRAMEWORK_PATH", + "DYLD_VERSIONED_FRAMEWORK_PATH", + */ #elif defined(_WIN32) "PATH", #endif From 05fef6bef4f3c9aeb31035298837238c6c3db043 Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Tue, 18 Feb 2025 14:28:04 +0530 Subject: [PATCH 9/9] Fix minor issues --- .../Orc/Shared/AutoLoadDylibUtils.h | 21 ++ .../Orc/TargetProcess/AutoLoadDylibLookup.h | 17 -- .../Orc/EPCDynamicLibrarySearchGenerator.cpp | 16 +- .../Orc/Shared/AutoLoadDylibUtils.cpp | 190 ++++++++++++ .../ExecutionEngine/Orc/Shared/CMakeLists.txt | 1 + .../Orc/TargetProcess/AutoLoadDyLoader.cpp | 121 +++++--- .../Orc/TargetProcess/AutoLoadDylibLookup.cpp | 276 +++++++++--------- .../SimpleExecutorDylibManager.cpp | 6 +- .../Orc/ObjectLinkingLayerTest.cpp | 3 + 9 files changed, 448 insertions(+), 203 deletions(-) create mode 100644 llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.cpp diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h index 3adb17bb0dda7..6660adc998297 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h @@ -29,6 +29,27 @@ constexpr uint32_t log2u(std::uint32_t n) { return (n > 1) ? 1 + log2u(n >> 1) : 0; } +extern const char *const kEnvDelim; + +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 +}; + +/// Collect the constituant paths from a PATH string. +/// /bin:/usr/bin:/usr/local/bin -> {/bin, /usr/bin, /usr/local/bin} +bool SplitPaths(llvm::StringRef PathStr, + llvm::SmallVectorImpl &Paths, + SplitMode Mode = SplitMode::PruneNonExistant, + llvm::StringRef Delim = kEnvDelim, bool Verbose = false); + +/// +bool GetSystemLibraryPaths(llvm::SmallVectorImpl &Paths); + +/// Returns a normalized version of the given Path +std::string NormalizePath(const std::string &Path); + class BloomFilter { private: static constexpr int Bits = 8 * sizeof(uint64_t); diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.h index 83ad22a183f36..fb686d2377256 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.h @@ -22,14 +22,6 @@ 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. @@ -140,15 +132,6 @@ class AutoLoadDynamicLibraryLookup { 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 &Paths, - SplitMode Mode, StringRef Delim, bool Verbose = false); - } // end namespace orc } // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp b/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp index 8e0c7f30c1a41..b3a3507ab9442 100644 --- a/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp +++ b/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp @@ -109,8 +109,8 @@ Error AutoLoadDynamicLibrarySearchGenerator::tryToGenerate( SymbolNameSet CandidateSyms; for (auto &KV : Symbols) { - if (GlobalFilter.IsInitialized() && !GlobalFilter.MayContain(*KV.first) && - !ExcludedSymbols.count(*KV.first)) + if (GlobalFilter.IsInitialized() && (!GlobalFilter.MayContain(*KV.first) || + ExcludedSymbols.count(*KV.first))) continue; CandidateSyms.insert(KV.first); @@ -121,12 +121,6 @@ Error AutoLoadDynamicLibrarySearchGenerator::tryToGenerate( 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 " @@ -135,6 +129,12 @@ Error AutoLoadDynamicLibrarySearchGenerator::tryToGenerate( return LS.continueLookup(Result.takeError()); } + auto &ResolveRes = Result->front(); + bool IsFilter = GlobalFilter.IsInitialized(); + if (!IsFilter && ResolveRes.Filter.has_value()) { + GlobalFilter = std::move(ResolveRes.Filter.value()); + } + auto &Symbols = ResolveRes.SymbolDef; assert(Result->size() == 1 && "Results for more than one library returned"); assert(Symbols.size() == CandidateSyms.size() && diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.cpp new file mode 100644 index 0000000000000..9b047bc348ef7 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.cpp @@ -0,0 +1,190 @@ +//===---------------- AutoLoadDylibUtils.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/Shared/AutoLoadDylibUtils.h" + +#include "llvm/Support/Debug.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +namespace llvm { +namespace orc { + +#if defined(LLVM_ON_UNIX) +const char *const kEnvDelim = ":"; +#elif defined(_WIN32) +const char *const kEnvDelim = ";"; +#else +#error "Unknown platform (environmental delimiter)" +#endif + +#if defined(LLVM_ON_UNIX) +bool Popen(const std::string &Cmd, llvm::SmallVectorImpl &Buf, bool RdE) { + if (FILE *PF = ::popen(RdE ? (Cmd + " 2>&1").c_str() : Cmd.c_str(), "r")) { + Buf.resize(0); + const size_t Chunk = Buf.capacity_in_bytes(); + while (true) { + const size_t Len = Buf.size(); + Buf.resize(Len + Chunk); + const size_t R = ::fread(&Buf[Len], sizeof(char), Chunk, PF); + if (R < Chunk) { + Buf.resize(Len + R); + break; + } + } + ::pclose(PF); + return !Buf.empty(); + } + return false; +} +#endif + +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()); +} + +static void LogNonExistantDirectory(StringRef Path) { +#define DEBUG_TYPE "LogNonExistantDirectory" + LLVM_DEBUG(dbgs() << " ignoring nonexistent directory \"" << Path << "\"\n"); +#undef DEBUG_TYPE +} + +bool SplitPaths(StringRef PathStr, SmallVectorImpl &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 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 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 +} + +bool GetSystemLibraryPaths(llvm::SmallVectorImpl &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 CurPaths; + SplitPaths(SysPath, CurPaths); + for (const auto &Path : CurPaths) + Paths.push_back(Path.str()); + } + } +#endif + return true; +} + +} // namespace orc +} // namespace llvm \ No newline at end of file diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt index 792b0cc8251cc..19b452b385272 100644 --- a/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt @@ -1,5 +1,6 @@ add_llvm_component_library(LLVMOrcShared AllocationActions.cpp + AutoLoadDylibUtils.cpp MachOObjectFormat.cpp ObjectFormats.cpp OrcError.cpp diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp index ee281b858246b..9408b67dc0ea4 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDyLoader.cpp @@ -636,6 +636,11 @@ void DynamicLoader::ScanForLibraries(bool searchSystemLibraries /* = false*/) { MachOObjectFile *Obj = dyn_cast(BinObjF); for (const auto &Command : Obj->load_commands()) { if (Command.C.cmd == MachO::LC_LOAD_DYLIB) { + // Command.C.cmd == MachO::LC_ID_DYLIB || + // Command.C.cmd == MachO::LC_LOAD_WEAK_DYLIB || + // Command.C.cmd == MachO::LC_REEXPORT_DYLIB || + // Command.C.cmd == MachO::LC_LAZY_LOAD_DYLIB || + // Command.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB || MachO::dylib_command dylibCmd = Obj->getDylibIDLoadCommand(Command); Deps.push_back(StringRef(Command.Ptr + dylibCmd.dylib.name)); @@ -761,18 +766,6 @@ void DynamicLoader::ScanForLibraries(bool searchSystemLibraries /* = false*/) { 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); @@ -794,35 +787,72 @@ void DynamicLoader::BuildBloomFilter(LibraryPath *Lib, LLVM_DEBUG(dbgs() << "Dyld::BuildBloomFilter: Building Bloom filter for: " << Lib->GetFullName() << "\n"); - std::vector symbols; + // If BloomFilter is empty then build it. + // Count Symbols and generate BloomFilter 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()); + std::list symbols; + for (const llvm::object::SymbolRef &S : BinObjFile->symbols()) { + uint32_t Flags = llvm::cantFail(S.getFlags()); + // Do not insert in the table symbols flagged to ignore. + if (Flags & IgnoreSymbolFlags) + continue; - // Skip symbols based on the flags (e.g., undefined or ignored) - if (Flags & IgnoreSymbolFlags || Flags & object::SymbolRef::SF_Undefined) - 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. + + llvm::Expected SymNameErr = S.getName(); + if (!SymNameErr) { + LLVM_DEBUG(dbgs() << "Dyld::BuildBloomFilter: Failed to read symbol " + << SymNameErr.get() << "\n"); + continue; + } - Expected SymName = S.getName(); - if (!SymName || SymName->empty()) { - LLVM_DEBUG(dbgs() << "Dyld::BuildBloomFilter: Skipped empty or failed " - "to read symbol\n"); + if (SymNameErr.get().empty()) + continue; + + ++SymbolsCount; + symbols.push_back(SymNameErr.get()); + } + + if (BinObjFile->isELF()) { + // ELF file format has .dynstr section for the dynamic symbol table. + const auto *ElfObj = cast(BinObjFile); + + for (const object::SymbolRef &S : ElfObj->getDynamicSymbolIterators()) { + uint32_t Flags = llvm::cantFail(S.getFlags()); + // DO NOT insert to table if symbol was undefined + if (Flags & llvm::object::SymbolRef::SF_Undefined) + 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. + + llvm::Expected SymNameErr = S.getName(); + if (!SymNameErr) { + LLVM_DEBUG(dbgs() << "Dyld::BuildBloomFilter: Failed to read symbol " + << SymNameErr.get() << "\n"); continue; } - symbols.push_back(*SymName); + if (SymNameErr.get().empty()) + continue; + ++SymbolsCount; + symbols.push_back(SymNameErr.get()); } - }; - ProcessSymbols(BinObjFile->symbols()); - - if (BinObjFile->isELF()) { - // ELF file format has .dynstr section for the dynamic symbol table. - const auto *ElfObj = cast(BinObjFile); - ProcessSymbols(ElfObj->getDynamicSymbolIterators()); } else if (BinObjFile->isCOFF()) { // On Windows, the symbols are present in // COFF format. object::COFFObjectFile *CoffObj = cast(BinObjFile); @@ -942,12 +972,13 @@ bool DynamicLoader::ContainsSymbol(const LibraryPath *Lib, LLVM_DEBUG(dbgs() << "Dyld::ContainsSymbol: Iterate all for <" << mangledName.str() << ">"); - auto ForeachSymbol = [](iterator_range range, + auto ForeachSymbol = + [&library_filename](iterator_range range, unsigned IgnoreSymbolFlags, StringRef mangledName) -> bool { for (const object::SymbolRef &S : range) { - - uint32_t Flags = cantFail(S.getFlags()); + uint32_t Flags = llvm::cantFail(S.getFlags()); + // Do not insert in the table symbols flagged to ignore. if (Flags & IgnoreSymbolFlags) continue; @@ -961,10 +992,19 @@ bool DynamicLoader::ContainsSymbol(const LibraryPath *Lib, // We seem to not have a way to differentiate it from the symbol API. Expected SymNameErr = S.getName(); - if (!SymNameErr || SymNameErr.get().empty()) + if (!SymNameErr) { + LLVM_DEBUG(dbgs() << "Dyld::ContainsSymbol: Failed to read symbol " + << mangledName.str() << "\n"); + continue; + } + + if (SymNameErr.get().empty()) continue; if (SymNameErr.get() == mangledName) { + LLVM_DEBUG(dbgs() << "Dyld::ContainsSymbol: Symbol " + << mangledName.str() << " found in " + << library_filename << "\n"); return true; } } @@ -1012,8 +1052,15 @@ bool DynamicLoader::ShouldPermanentlyIgnore(StringRef FileName) const { auto ObjF = object::ObjectFile::createObjectFile(FileName); if (!ObjF) { + // Note: It is important to always call handleAllErrors, otherwise the + // destructor of llvm::Error will abort the program "due to an unhandled + // Error" + std::string Message; + handleAllErrors(ObjF.takeError(), [&](llvm::ErrorInfoBase &EIB) { + Message += EIB.message() + "; "; + }); LLVM_DEBUG(dbgs() << "[DyLD] Failed to read object file " << FileName - << "\n"); + << " message : " << Message << "\n";); return true; } diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.cpp index da14700296224..4567e36b113ba 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/AutoLoadDylibLookup.cpp @@ -24,148 +24,148 @@ #include #include -static bool GetSystemLibraryPaths(llvm::SmallVectorImpl &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 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()); -} +// static bool GetSystemLibraryPaths(llvm::SmallVectorImpl &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 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 &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 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 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 -} +// void LogNonExistantDirectory(StringRef Path) { +// #define DEBUG_TYPE "LogNonExistantDirectory" +// LLVM_DEBUG(dbgs() << " ignoring nonexistent directory \"" << Path << "\"\n"); +// #undef DEBUG_TYPE +// } + +// bool SplitPaths(StringRef PathStr, SmallVectorImpl &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 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 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 kSysLibraryEnv = { diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp index 8f8f2a4f4f1fb..0e541c337fca9 100644 --- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp @@ -10,8 +10,6 @@ #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" -#include - #define DEBUG_TYPE "orc" namespace llvm { @@ -129,7 +127,9 @@ SimpleExecutorDylibManager::resolve(const RemoteSymbolLookupSet &L) { return H.takeError(); DylibLookup->addLoadedLib(canonicalLoadedLib); - void *Addr = dlsym(H.get().toPtr(), DemangledSymName); + + sys::DynamicLibrary Dl(H.get().toPtr()); + void *Addr = Dl.getAddressOfSymbol(DemangledSymName); if (!Addr) return make_error(Twine("Missing definition for ") + DemangledSymName, diff --git a/llvm/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp b/llvm/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp index 415ad0ee80577..db76293ba3865 100644 --- a/llvm/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp +++ b/llvm/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp @@ -315,6 +315,9 @@ TEST(ObjectLinkingLayerSearchGeneratorTest, AbsoluteSymbolsObjectLayer) { } Complete(std::vector{1, Result}); } + + void resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn F) override {} }; ExecutionSession ES{std::make_unique()};