diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index d5cb7b1745f2d..cdbc8a737b28d 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -104,7 +104,6 @@ clang/LLVM technology. #include "clang/Serialization/GlobalModuleIndex.h" #include "cling/Interpreter/ClangInternalState.h" -#include "cling/Interpreter/DynamicLibraryManager.h" #include "cling/Interpreter/Interpreter.h" #include "cling/Interpreter/LookupHelper.h" #include "cling/Interpreter/Value.h" @@ -115,6 +114,9 @@ clang/LLVM technology. #include "cling/Utils/SourceNormalization.h" #include "cling/Interpreter/Exception.h" +#include "llvm/ExecutionEngine/Orc/AutoLoadEPC.h" +#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibLookup.h" + #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Module.h" @@ -1600,17 +1602,17 @@ TCling::TCling(const char *name, const char *title, const char* const argv[], vo fInterpreter->setCallbacks(std::move(clingCallbacks)); if (!fromRootCling) { - cling::DynamicLibraryManager& DLM = *fInterpreter->getDynamicLibraryManager(); + llvm::orc::AutoLoadEPC& EPC = *fInterpreter->getClingEPC(); // Make sure cling looks into ROOT's libdir, even if not part of LD_LIBRARY_PATH // e.g. because of an RPATH build. - DLM.addSearchPath(TROOT::GetSharedLibDir().Data(), /*isUser=*/true, - /*prepend=*/true); + EPC.getDylibLookup().addSearchPath(TROOT::GetSharedLibDir().Data(), /*isUser=*/true, + /*prepend=*/true); auto ShouldPermanentlyIgnore = [](llvm::StringRef FileName) -> bool{ llvm::StringRef stem = llvm::sys::path::stem(FileName); return stem.startswith("libNew") || stem.startswith("libcppyy_backend"); }; // Initialize the dyld for AutoloadLibraryGenerator. - DLM.initializeDyld(ShouldPermanentlyIgnore); + EPC.getDylibLookup().initializeDynamicLoader(ShouldPermanentlyIgnore); } } @@ -2087,7 +2089,7 @@ void TCling::RegisterModule(const char* modulename, if (payloadCode) code += payloadCode; - std::string dyLibName = cling::DynamicLibraryManager::getSymbolLocation(triggerFunc); + std::string dyLibName = llvm::orc::AutoLoadDynamicLibraryLookup::getSymbolLocation(triggerFunc); assert(!llvm::sys::fs::is_symlink_file(dyLibName)); if (dyLibName.empty()) { @@ -2096,7 +2098,7 @@ void TCling::RegisterModule(const char* modulename, } // The triggerFunc may not be in a shared object but in an executable. - bool isSharedLib = cling::DynamicLibraryManager::isSharedLibrary(dyLibName); + bool isSharedLib = llvm::orc::AutoLoadDynamicLibraryLookup::isSharedLibrary(dyLibName); bool wasDlopened = false; @@ -2551,7 +2553,7 @@ Longptr_t TCling::ProcessLine(const char* line, EErrorCode* error/*=0*/) indent = HandleInterpreterException(GetMetaProcessorImpl(), mod_line, compRes, &result); } } - } else if (cling::DynamicLibraryManager::isSharedLibrary(fname.Data()) && + } else if (llvm::orc::AutoLoadDynamicLibraryLookup::isSharedLibrary(fname.Data()) && strncmp(sLine.Data(), ".L", 2) != 0) { // .x *.so or *.dll if (gSystem->Load(fname) < 0) { // Loading failed. @@ -3144,7 +3146,7 @@ static Bool_t s_IsLibraryLoaded(const char* libname, cling::Interpreter* fInterp // Check shared library. TString tLibName(libname); if (gSystem->FindDynamicLibrary(tLibName, kTRUE)) - return fInterpreter->getDynamicLibraryManager()->isLibraryLoaded(tLibName.Data()); + return fInterpreter->getClingEPC()->IsDylibLoaded(tLibName.Data()); return false; } @@ -3187,7 +3189,7 @@ Bool_t TCling::IsLoaded(const char* filename) const R__LOCKGUARD(gInterpreterMutex); //FIXME: if we use llvm::sys::fs::make_absolute all this can go away. See - // cling::DynamicLibraryManager. + // llvm::orc::AutoLoadDynamicLibraryLookup. std::string file_name = filename; size_t at = std::string::npos; @@ -3338,7 +3340,7 @@ static int callback_for_dl_iterate_phdr(struct dl_phdr_info *info, size_t size, //has no path && strncmp(info->dlpi_name, "[vdso]", 6) //the linker does not like to be mmapped - //causes a crash in cling::DynamicLibraryManager::loadLibrary()) + //causes a crash in llvm::orc::AutoLoadDynamicLibraryLookup::loadLibrary()) //with error message "mmap of entire address space failed: Cannot allocate memory" && strncmp(info->dlpi_name, "/libexec/ld-elf.so.1", 20) #endif @@ -3432,9 +3434,9 @@ void TCling::RegisterLoadedSharedLibrary(const char* filename) // Tell the interpreter that this library is available; all libraries can be // used to resolve symbols. - cling::DynamicLibraryManager* DLM = fInterpreter->getDynamicLibraryManager(); - if (!DLM->isLibraryLoaded(filename)) { - DLM->loadLibrary(filename, true /*permanent*/, true /*resolved*/); + llvm::orc::AutoLoadEPC* EPC = fInterpreter->getClingEPC(); + if (!EPC->IsDylibLoaded(filename)) { + EPC->loadDylib(filename, true /*permanent*/, true /*resolved*/); } #if defined(R__MACOSX) @@ -3531,29 +3533,28 @@ Int_t TCling::Load(const char* filename, Bool_t system) // Used to return 0 on success, 1 on duplicate, -1 on failure, -2 on "fatal". R__LOCKGUARD_CLING(gInterpreterMutex); - cling::DynamicLibraryManager* DLM = fInterpreter->getDynamicLibraryManager(); - std::string canonLib = DLM->lookupLibrary(filename); - cling::DynamicLibraryManager::LoadLibResult res - = cling::DynamicLibraryManager::kLoadLibNotFound; + llvm::orc::AutoLoadEPC *EPC = fInterpreter->getClingEPC(); + std::string canonLib = EPC->lookupDylib(filename); + llvm::orc::AutoLoadEPC::LoadLibResult res = llvm::orc::AutoLoadEPC::kLoadLibNotFound; if (!canonLib.empty()) { if (system) - res = DLM->loadLibrary(filename, system, true); + res = EPC->loadDylib(filename, system, true); else { // For the non system libs, we'd like to be able to unload them. // FIXME: Here we lose the information about kLoadLibAlreadyLoaded case. cling::Interpreter::CompilationResult compRes; HandleInterpreterException(GetMetaProcessorImpl(), Form(".L %s", canonLib.c_str()), compRes, /*cling::Value*/nullptr); if (compRes == cling::Interpreter::kSuccess) - res = cling::DynamicLibraryManager::kLoadLibSuccess; + res = llvm::orc::AutoLoadEPC::kLoadLibSuccess; } } - if (res == cling::DynamicLibraryManager::kLoadLibSuccess) { + if (res == llvm::orc::AutoLoadEPC::kLoadLibSuccess) { UpdateListOfLoadedSharedLibraries(); } switch (res) { - case cling::DynamicLibraryManager::kLoadLibSuccess: return 0; - case cling::DynamicLibraryManager::kLoadLibAlreadyLoaded: return 1; + case llvm::orc::AutoLoadEPC::kLoadLibSuccess: return 0; + case llvm::orc::AutoLoadEPC::kLoadLibAlreadyLoaded: return 1; default: break; }; return -1; @@ -6585,10 +6586,10 @@ bool TCling::LibraryLoadingFailed(const std::string& errmessage, const std::stri // This branch is taken when the callback was from DynamicLibraryManager::loadLibrary std::string mangled_name = std::string(errMsg.split("undefined symbol: ").second); void* res = ((TCling*)gCling)->LazyFunctionCreatorAutoload(mangled_name); - cling::DynamicLibraryManager* DLM = fInterpreter->getDynamicLibraryManager(); - if (res && DLM && (DLM->loadLibrary(libStem, permanent, resolved) == cling::DynamicLibraryManager::kLoadLibSuccess)) - // Return success when LazyFunctionCreatorAutoload could find mangled_name - return true; + llvm::orc::AutoLoadEPC *EPC = fInterpreter->getClingEPC(); + if (res && EPC && (EPC->loadDylib(libStem, permanent, resolved) == llvm::orc::AutoLoadEPC::kLoadLibSuccess)) + // Return success when LazyFunctionCreatorAutoload could find mangled_name + return true; } else { // The callback is from IncrementalExecutor::diagnoseUnresolvedSymbols if ( ((TCling*)gCling)->LazyFunctionCreatorAutoload(errmessage)) @@ -6608,7 +6609,7 @@ void* TCling::LazyFunctionCreatorAutoload(const std::string& mangled_name) { if (void* Addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(dlsym_mangled_name)) return Addr; - const cling::DynamicLibraryManager &DLM = *GetInterpreterImpl()->getDynamicLibraryManager(); + const llvm::orc::AutoLoadEPC &EPC = *GetInterpreterImpl()->getClingEPC(); R__LOCKGUARD(gInterpreterMutex); auto LibLoader = [](const std::string& LibName) -> bool { @@ -6620,8 +6621,8 @@ void* TCling::LazyFunctionCreatorAutoload(const std::string& mangled_name) { return true; //success. }; - std::string libName = DLM.searchLibrariesForSymbol(mangled_name, - /*searchSystem=*/ true); + std::string libName = EPC.getDylibLookup().searchLibrariesForSymbol(mangled_name, + /*searchSystem=*/true); assert(!llvm::StringRef(libName).startswith("libNew") && "We must not resolve symbols from libNew!"); @@ -7244,7 +7245,8 @@ static std::string GetSharedLibImmediateDepsSlow(std::string lib, } R__LOCKGUARD(gInterpreterMutex); - std::string found = interp->getDynamicLibraryManager()->searchLibrariesForSymbol(SymName, /*searchSystem*/false); + std::string found = + interp->getClingEPC()->getDylibLookup().searchLibrariesForSymbol(SymName, /*searchSystem*/ false); // The expected output is just filename without the full path, which // is not very accurate, because our Dyld implementation might find // a match in location a/b/c.so and if we return just c.so ROOT might @@ -7662,8 +7664,8 @@ int TCling::UnloadFile(const char* path) const { // Modifying the interpreter state needs locking. R__LOCKGUARD(gInterpreterMutex); - cling::DynamicLibraryManager* DLM = fInterpreter->getDynamicLibraryManager(); - std::string canonical = DLM->lookupLibrary(path); + llvm::orc::AutoLoadEPC *EPC = fInterpreter->getClingEPC(); + std::string canonical = EPC->lookupDylib(path); if (canonical.empty()) { canonical = path; } diff --git a/core/metacling/src/TClingCallbacks.cxx b/core/metacling/src/TClingCallbacks.cxx index 1b988aa6f5abf..1b9443439fda0 100644 --- a/core/metacling/src/TClingCallbacks.cxx +++ b/core/metacling/src/TClingCallbacks.cxx @@ -13,7 +13,6 @@ #include -#include "cling/Interpreter/DynamicLibraryManager.h" #include "cling/Interpreter/Interpreter.h" #include "cling/Interpreter/InterpreterCallbacks.h" #include "cling/Interpreter/Transaction.h" @@ -35,7 +34,9 @@ #include "clang/Serialization/GlobalModuleIndex.h" #include "clang/Basic/DiagnosticSema.h" +#include "llvm/ExecutionEngine/Orc/AutoLoadEPC.h" #include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" @@ -84,77 +85,6 @@ extern "C" { void TCling__UnlockCompilationDuringUserCodeExecution(void *state); } -class AutoloadLibraryGenerator : public llvm::orc::DefinitionGenerator { - const TClingCallbacks &fCallbacks; - cling::Interpreter *fInterpreter; -public: - AutoloadLibraryGenerator(cling::Interpreter *interp, const TClingCallbacks& cb) - : fCallbacks(cb), fInterpreter(interp) {} - - llvm::Error tryToGenerate(llvm::orc::LookupState &LS, llvm::orc::LookupKind K, llvm::orc::JITDylib &JD, - llvm::orc::JITDylibLookupFlags JDLookupFlags, - const llvm::orc::SymbolLookupSet &Symbols) override - { - if (!fCallbacks.IsAutoLoadingEnabled()) - return llvm::Error::success(); - - // If we get here, the symbols have not been found in the current process, - // so no need to check that again. Instead search for the library that - // provides the symbol and create one MaterializationUnit per library to - // actually load it if needed. - std::unordered_map found; - - // TODO: Do we need to take gInterpreterMutex? - // R__LOCKGUARD(gInterpreterMutex); - - for (auto &&KV : Symbols) { - llvm::orc::SymbolStringPtr name = KV.first; - - const cling::DynamicLibraryManager &DLM = *fInterpreter->getDynamicLibraryManager(); - - std::string libName = DLM.searchLibrariesForSymbol((*name).str(), - /*searchSystem=*/true); - - // libNew overrides memory management functions; must never autoload that. - assert(libName.find("/libNew.") == std::string::npos && "We must not autoload libNew!"); - - // libCling symbols are intentionally hidden from the process, and libCling must not be - // dlopened. Instead, symbols must be resolved by specifically querying the dynlib handle of - // libCling, which by definition is loaded - else we could not call this code. The handle - // is made available as argument to `CreateInterpreter`. - assert(libName.find("/libCling.") == std::string::npos && "Must not autoload libCling!"); - - if (!libName.empty()) - found[libName].push_back(name); - } - - llvm::orc::SymbolMap loadedSymbols; - for (const auto &KV : found) { - // Try to load the library which should provide the symbol definition. - // TODO: Should this interface with the DynamicLibraryManager directly? - if (TCling__LoadLibrary(KV.first.c_str()) < 0) { - ROOT::TMetaUtils::Error("AutoloadLibraryMU", "Failed to load library %s", KV.first.c_str()); - } - - for (const auto &symbol : KV.second) { - std::string symbolStr = (*symbol).str(); - std::string nameForDlsym = ROOT::TMetaUtils::DemangleNameForDlsym(symbolStr); - - void *addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(nameForDlsym); - if (addr) { - loadedSymbols[symbol] = {llvm::orc::ExecutorAddr::fromPtr(addr), llvm::JITSymbolFlags::Exported}; - } - } - } - - if (!loadedSymbols.empty()) { - return JD.define(absoluteSymbols(std::move(loadedSymbols))); - } - - return llvm::Error::success(); - } -}; - TClingCallbacks::TClingCallbacks(cling::Interpreter *interp, bool hasCodeGen) : InterpreterCallbacks(interp) { if (hasCodeGen) { @@ -162,7 +92,12 @@ TClingCallbacks::TClingCallbacks(cling::Interpreter *interp, bool hasCodeGen) : m_Interpreter->declare("namespace __ROOT_SpecialObjects{}", &T); fROOTSpecialNamespace = dyn_cast(T->getFirstDecl().getSingleDecl()); - interp->addGenerator(std::make_unique(interp, *this)); + auto F = [&] () -> bool { return this->IsAutoLoadingEnabled(); }; + if (auto DSG = + llvm::orc::AutoLoadDynamicLibrarySearchGenerator::GetForTargetProcess( + interp->getClingEPC()->getExecutionSession(), std::move(F))) + interp->addGenerator(std::move(*DSG)); + // interp->addGenerator(std::make_unique(interp, *this)); } } diff --git a/interpreter/cling/include/cling/Interpreter/DynamicLibraryManager.h b/interpreter/cling/include/cling/Interpreter/DynamicLibraryManager.h deleted file mode 100644 index d4868971f21af..0000000000000 --- a/interpreter/cling/include/cling/Interpreter/DynamicLibraryManager.h +++ /dev/null @@ -1,214 +0,0 @@ -//--------------------------------------------------------------------*- C++ -*- -// CLING - the C++ LLVM-based InterpreterG :) -// author: Vassil Vassilev -// -// This file is dual-licensed: you can choose to license it under the University -// of Illinois Open Source License or the GNU Lesser General Public License. See -// LICENSE.TXT for details. -//------------------------------------------------------------------------------ - -#ifndef CLING_DYNAMIC_LIBRARY_MANAGER_H -#define CLING_DYNAMIC_LIBRARY_MANAGER_H - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringSet.h" - -#include "llvm/Support/Path.h" - -namespace cling { - class Dyld; - class InterpreterCallbacks; - - ///\brief A helper class managing dynamic shared objects. - /// - class DynamicLibraryManager { - public: - ///\brief Describes the result of loading a library. - /// - enum LoadLibResult { - kLoadLibSuccess, ///< library loaded successfully - kLoadLibAlreadyLoaded, ///< library was already loaded - kLoadLibNotFound, ///< library was not found - kLoadLibLoadError, ///< loading the library failed - kLoadLibNumResults - }; - - /// 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 = llvm::SmallVector; - private: - typedef const void* DyLibHandle; - typedef llvm::DenseMap DyLibs; - ///\brief DynamicLibraries loaded by this Interpreter. - /// - DyLibs m_DyLibs; - llvm::StringSet<> m_LoadedLibraries; - - ///\brief System's include path, get initialized at construction time. - /// - SearchPathInfos m_SearchPaths; - - InterpreterCallbacks* m_Callbacks = nullptr; - - Dyld* m_Dyld = nullptr; - - ///\brief Concatenates current include paths and the system include paths - /// and performs a lookup for the filename. - /// See more information for RPATH and RUNPATH: https://en.wikipedia.org/wiki/Rpath - ///\param[in] libStem - The filename being looked up - ///\param[in] RPath - RPATH as provided by loader library, searching for libStem - ///\param[in] RunPath - RUNPATH as provided by loader library, searching for libStem - ///\param[in] libLoader - The library that loads libStem. Use "" for main program. - /// - ///\returns the canonical path to the file or empty string if not found - /// - std::string lookupLibInPaths(llvm::StringRef libStem, - llvm::SmallVector RPath = {}, - llvm::SmallVector RunPath = {}, - llvm::StringRef libLoader = "") const; - - ///\brief Concatenates current include paths and the system include paths - /// and performs a lookup for the filename. If still not found it tries to - /// add the platform-specific extensions (such as so, dll, dylib) and - /// retries the lookup (from lookupLibInPaths) - /// See more information for RPATH and RUNPATH: https://en.wikipedia.org/wiki/Rpath - ///\param[in] filename - The filename being looked up - ///\param[in] RPath - RPATH as provided by loader library, searching for libStem - ///\param[in] RunPath - RUNPATH as provided by loader library, searching for libStem - ///\param[in] libLoader - The library that loads libStem. Use "" for main program. - /// - ///\returns the canonical path to the file or empty string if not found - /// - std::string lookupLibMaybeAddExt(llvm::StringRef filename, - llvm::SmallVector RPath = {}, - llvm::SmallVector RunPath = {}, - llvm::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: - DynamicLibraryManager(); - ~DynamicLibraryManager(); - DynamicLibraryManager(const DynamicLibraryManager&) = delete; - DynamicLibraryManager& operator=(const DynamicLibraryManager&) = delete; - - InterpreterCallbacks* getCallbacks() { return m_Callbacks; } - const InterpreterCallbacks* getCallbacks() const { return m_Callbacks; } - void setCallbacks(InterpreterCallbacks* C) { m_Callbacks = C; } - - ///\brief Returns the system include paths. - /// - ///\returns System include paths. - /// - const SearchPathInfos& getSearchPaths() const { - return m_SearchPaths; - } - - void addSearchPath(llvm::StringRef dir, bool isUser = true, - bool prepend = false) { - if (!dir.empty()) { - for (auto & item : m_SearchPaths) - if (dir.equals(item.Path)) return; - auto pos = prepend ? m_SearchPaths.begin() : m_SearchPaths.end(); - m_SearchPaths.insert(pos, SearchPathInfo{dir.str(), isUser}); - } - } - - ///\brief Looks up a library taking into account the current include paths - /// and the system include paths. - /// See more information for RPATH and RUNPATH: https://en.wikipedia.org/wiki/Rpath - ///\param[in] libStem - The filename being looked up - ///\param[in] RPath - RPATH as provided by loader library, searching for libStem - ///\param[in] RunPath - RUNPATH as provided by loader library, searching for libStem - ///\param[in] libLoader - The library that loads libStem. Use "" for main program. - ///\param[in] variateLibStem - If this param is true, and libStem is "L", then - /// we search for "L", "libL", "L.so", "libL.so"", etc. - /// - ///\returns the canonical path to the file or empty string if not found - /// - std::string lookupLibrary(llvm::StringRef libStem, - llvm::SmallVector RPath = {}, - llvm::SmallVector RunPath = {}, - llvm::StringRef libLoader = "", - bool variateLibStem = true) const; - - ///\brief Loads a shared library. - /// - ///\param [in] libStem - The file to load. - ///\param [in] permanent - If false, the file can be unloaded later. - ///\param [in] resolved - Whether libStem is an absolute path or resolved - /// from a previous call to DynamicLibraryManager::lookupLibrary - /// - ///\returns kLoadLibSuccess on success, kLoadLibAlreadyLoaded if the library - /// was already loaded, kLoadLibError if the library cannot be found or any - /// other error was encountered. - /// - LoadLibResult loadLibrary(llvm::StringRef, bool permanent, - bool resolved = false); - - void unloadLibrary(llvm::StringRef libStem); - - ///\brief Returns true if the file was a dynamic library and it was already - /// loaded. - /// - bool isLibraryLoaded(llvm::StringRef fullPath) const; - - /// Initialize the dyld. - /// - ///\param [in] shouldPermanentlyIgnore - a callback deciding if a library - /// should be ignored from the result set. Useful for ignoring - /// dangerous libraries such as the ones overriding malloc. - /// - void - initializeDyld(std::function shouldPermanentlyIgnore); - - /// Find the first not-yet-loaded shared object that contains the symbol - /// - ///\param[in] mangledName - the mangled name to look for. - ///\param[in] searchSystem - whether to decend into system libraries. - /// - ///\returns the library name if found, and empty string otherwise. - /// - std::string searchLibrariesForSymbol(llvm::StringRef mangledName, - bool searchSystem = true) const; - - void dump(llvm::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(llvm::StringRef path); - - /// Returns true if file is a shared library. - /// - ///\param[in] libFullPath - the full path to file. - /// - ///\param[out] exists - sets if the file exists. Useful to distinguish if it - /// is a library but of incompatible file format. - /// - static bool isSharedLibrary(llvm::StringRef libFullPath, bool *exists = nullptr); - }; -} // end namespace cling -#endif // CLING_DYNAMIC_LIBRARY_MANAGER_H diff --git a/interpreter/cling/include/cling/Interpreter/Interpreter.h b/interpreter/cling/include/cling/Interpreter/Interpreter.h index 1fa0c0d23d83f..8401027128ab6 100644 --- a/interpreter/cling/include/cling/Interpreter/Interpreter.h +++ b/interpreter/cling/include/cling/Interpreter/Interpreter.h @@ -18,6 +18,7 @@ #include "cling/Interpreter/RuntimeOptions.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/Orc/AutoLoadEPC.h" #include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h" #include @@ -777,8 +778,8 @@ namespace cling { /// managed by m_Executor llvm::orc::LLJIT* getExecutionEngine(); - const DynamicLibraryManager* getDynamicLibraryManager() const; - DynamicLibraryManager* getDynamicLibraryManager(); + const llvm::orc::AutoLoadEPC* getClingEPC() const; + llvm::orc::AutoLoadEPC* getClingEPC(); const Transaction* getFirstTransaction() const; const Transaction* getLastTransaction() const; diff --git a/interpreter/cling/include/cling/Interpreter/InterpreterCallbacks.h b/interpreter/cling/include/cling/Interpreter/InterpreterCallbacks.h index 45d3464c8578f..b5787d8b92407 100644 --- a/interpreter/cling/include/cling/Interpreter/InterpreterCallbacks.h +++ b/interpreter/cling/include/cling/Interpreter/InterpreterCallbacks.h @@ -15,7 +15,7 @@ #include "clang/Basic/SourceManager.h" #include "llvm/ADT/ArrayRef.h" - +#include "llvm/ExecutionEngine/Orc/AutoLoadEPC.h" #include namespace clang { @@ -43,7 +43,7 @@ namespace cling { /// \brief This interface provides a way to observe the actions of the /// interpreter as it does its thing. Clients can define their hooks here to /// implement interpreter level tools. - class InterpreterCallbacks { + class InterpreterCallbacks : public llvm::orc::ORCCallbacks { protected: ///\brief Our interpreter instance. @@ -118,7 +118,7 @@ namespace cling { /// /// \param[in] - Error message and parameters passed to loadLibrary /// \returns true if the error was handled. - virtual bool LibraryLoadingFailed(const std::string&, const std::string&, bool, bool) { return 0; } + // virtual bool LibraryLoadingFailed(const std::string&, const std::string&, bool, bool) { return 0; } ///\brief This callback is invoked whenever interpreter has committed new /// portion of declarations. @@ -159,8 +159,8 @@ namespace cling { ///\param[in] - The declaration that has been shadowed. virtual void DefinitionShadowed(const clang::NamedDecl*) {} - virtual void LibraryLoaded(const void*, llvm::StringRef) {} - virtual void LibraryUnloaded(const void*, llvm::StringRef) {} + // virtual void LibraryLoaded(const void*, llvm::StringRef) {} + // virtual void LibraryUnloaded(const void*, llvm::StringRef) {} ///\brief Cling calls this is printing a stack trace can be beneficial, /// for instance when throwing interpreter exceptions. diff --git a/interpreter/cling/lib/Interpreter/CMakeLists.txt b/interpreter/cling/lib/Interpreter/CMakeLists.txt index e397da97f6bda..f2321a61f07fc 100644 --- a/interpreter/cling/lib/Interpreter/CMakeLists.txt +++ b/interpreter/cling/lib/Interpreter/CMakeLists.txt @@ -72,8 +72,6 @@ add_cling_library(clingInterpreter OBJECT DefinitionShadower.cpp DeclUnloader.cpp DeviceKernelInliner.cpp - DynamicLibraryManager.cpp - DynamicLibraryManagerSymbol.cpp DynamicLookup.cpp DynamicExprInfo.cpp Exception.cpp diff --git a/interpreter/cling/lib/Interpreter/ClingPragmas.cpp b/interpreter/cling/lib/Interpreter/ClingPragmas.cpp index 6b3ab3fd20d1b..05640796e1ccd 100644 --- a/interpreter/cling/lib/Interpreter/ClingPragmas.cpp +++ b/interpreter/cling/lib/Interpreter/ClingPragmas.cpp @@ -9,7 +9,6 @@ #include "ClingPragmas.h" -#include "cling/Interpreter/DynamicLibraryManager.h" #include "cling/Interpreter/Interpreter.h" #include "cling/Interpreter/Transaction.h" #include "cling/Utils/Output.h" @@ -254,7 +253,7 @@ namespace { default: do { if (Command == kAddLibrary) - m_Interp.getDynamicLibraryManager()->addSearchPath(std::move(Literal)); + m_Interp.getClingEPC()->getDylibLookup().addSearchPath(std::move(Literal)); else if (Command == kAddInclude) m_Interp.AddIncludePath(Literal); } while (GetNextLiteral(PP, Tok, Literal, Command)); diff --git a/interpreter/cling/lib/Interpreter/DynamicLibraryManager.cpp b/interpreter/cling/lib/Interpreter/DynamicLibraryManager.cpp deleted file mode 100644 index 50d7f3b672f94..0000000000000 --- a/interpreter/cling/lib/Interpreter/DynamicLibraryManager.cpp +++ /dev/null @@ -1,504 +0,0 @@ -//------------------------------------------------------------------------------ -// CLING - the C++ LLVM-based InterpreterG :) -// author: Vassil Vassilev -// -// This file is dual-licensed: you can choose to license it under the University -// of Illinois Open Source License or the GNU Lesser General Public License. See -// LICENSE.TXT for details. -//------------------------------------------------------------------------------ - -#include "cling/Interpreter/DynamicLibraryManager.h" -#include "cling/Interpreter/InterpreterCallbacks.h" -#include "cling/Utils/Paths.h" -#include "cling/Utils/Platform.h" -#include "cling/Utils/Output.h" - -#include "llvm/ADT/StringSet.h" -#include "llvm/BinaryFormat/Magic.h" -#include "llvm/Support/DynamicLibrary.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" - -#include -#include -#include - -// FIXME: Implement debugging output stream in cling. -constexpr unsigned DEBUG = 0; - -namespace cling { - DynamicLibraryManager::DynamicLibraryManager() { - const llvm::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)) { - llvm::SmallVector CurPaths; - SplitPaths(Env, CurPaths, utils::kPruneNonExistant, platform::kEnvDelim); - for (const auto& Path : CurPaths) - addSearchPath(Path); - } - } - - // $CWD is the last user path searched. - addSearchPath("."); - - llvm::SmallVector SysPaths; - platform::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(llvm::StringRef original, llvm::StringRef pattern, - llvm::StringRef replacement) { - if (!original.starts_with_insensitive(pattern)) - return original.str(); - llvm::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(llvm::StringRef original, - llvm::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__ - llvm::SmallString<512> mainExecutablePath(llvm::sys::fs::getMainExecutable(nullptr, nullptr)); - llvm::sys::path::remove_filename(mainExecutablePath); - llvm::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 - llvm::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 - DynamicLibraryManager::lookupLibInPaths(llvm::StringRef libStem, - llvm::SmallVector RPath /*={}*/, - llvm::SmallVector RunPath /*={}*/, - llvm::StringRef libLoader /*=""*/) const { - - if (DEBUG > 7) { - cling::errs() << "Dyld::lookupLibInPaths:" << libStem.str() << - ", ..., libLodaer=" << libLoader << "\n"; - } - - // Lookup priority is: RPATH, LD_LIBRARY_PATH/m_SearchPaths, RUNPATH - // See: https://en.wikipedia.org/wiki/Rpath - // See: https://amir.rachum.com/blog/2016/09/17/shared-libraries/ - - if (DEBUG > 7) { - cling::errs() << "Dyld::lookupLibInPaths: \n"; - cling::errs() << ":: RPATH\n"; - for (auto Info : RPath) { - cling::errs() << ":::: " << Info.str() << "\n"; - } - cling::errs() << ":: SearchPaths (LD_LIBRARY_PATH, etc...)\n"; - for (auto Info : getSearchPaths()) { - cling::errs() << ":::: " << Info.Path << ", user=" << (Info.IsUser?"true":"false") << "\n"; - } - cling::errs() << ":: RUNPATH\n"; - for (auto Info : RunPath) { - cling::errs() << ":::: " << Info.str() << "\n"; - } - } - - llvm::SmallString<512> ThisPath; - // RPATH - for (auto Info : RPath) { - ThisPath = substAll(Info, libLoader); - llvm::sys::path::append(ThisPath, libStem); - // to absolute path? - if (DEBUG > 7) { - cling::errs() << "## Try: " << ThisPath; - } - if (isSharedLibrary(ThisPath.str())) { - if (DEBUG > 7) { - cling::errs() << " ... Found (in RPATH)!\n"; - } - return ThisPath.str().str(); - } - } - // m_SearchPaths - for (const SearchPathInfo& Info : m_SearchPaths) { - ThisPath = Info.Path; - llvm::sys::path::append(ThisPath, libStem); - // to absolute path? - if (DEBUG > 7) { - cling::errs() << "## Try: " << ThisPath; - } - if (isSharedLibrary(ThisPath.str())) { - if (DEBUG > 7) { - cling::errs() << " ... 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? - if (DEBUG > 7) { - cling::errs() << "## Try: " << ThisPath; - } - if (isSharedLibrary(ThisPath.str())) { - if (DEBUG > 7) { - cling::errs() << " ... Found (in RUNPATH)!\n"; - } - return ThisPath.str().str(); - } - } - - if (DEBUG > 7) { - cling::errs() << "## NotFound!!!\n"; - } - - return ""; - } - - std::string - DynamicLibraryManager::lookupLibMaybeAddExt(llvm::StringRef libStem, - llvm::SmallVector RPath /*={}*/, - llvm::SmallVector RunPath /*={}*/, - llvm::StringRef libLoader /*=""*/) const { - - using namespace llvm::sys; - - if (DEBUG > 7) { - cling::errs() << "Dyld::lookupLibMaybeAddExt: " << libStem.str() << - ", ..., libLoader=" << libLoader << "\n"; - } - - std::string foundDyLib = lookupLibInPaths(libStem, RPath, RunPath, libLoader); - - if (foundDyLib.empty()) { - // Add DyLib extension: - llvm::SmallString<512> filenameWithExt(libStem); -#if defined(LLVM_ON_UNIX) -#ifdef __APPLE__ - llvm::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 = platform::NormalizePath(foundDyLib); - if (Path.empty()) { - cling::errs() << "cling::DynamicLibraryManager::lookupLibMaybeAddExt(): " - "error getting real (canonical) path of library " << foundDyLib << '\n'; - return foundDyLib; - } - return Path; - } - - std::string DynamicLibraryManager::normalizePath(llvm::StringRef path) { - // 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 = platform::NormalizePath(Path); - if (NPath.empty()) - cling::log() << "Could not normalize: '" << Path << "'"; - return NPath; - } - - std::string RPathToStr2(llvm::SmallVector V) { - std::string result; - for (auto item : V) - result += item.str() + ","; - if (!result.empty()) - result.pop_back(); - return result; - } - - std::string - DynamicLibraryManager::lookupLibrary(llvm::StringRef libStem, - llvm::SmallVector RPath /*={}*/, - llvm::SmallVector RunPath /*={}*/, -// llvm::StringRef RPath /*=""*/, -// llvm::StringRef RunPath /*=""*/, - llvm::StringRef libLoader /*=""*/, - bool variateLibStem /*=true*/) const { - if (DEBUG > 7) { - cling::errs() << "Dyld::lookupLibrary: " << libStem.str() << ", " << - RPathToStr2(RPath) << ", " << RPathToStr2(RunPath) << ", " << libLoader.str() << "\n"; - } - - // 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); - else - 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()) { - llvm::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 platform::NormalizePath(foundName); - - return std::string(); - } - - DynamicLibraryManager::LoadLibResult - DynamicLibraryManager::loadLibrary(llvm::StringRef libStem, - bool permanent, bool resolved) { - if (DEBUG > 7) { - cling::errs() << "Dyld::loadLibrary: " << libStem.str() << ", " << - (permanent ? "permanent" : "not-permanent") << ", " << - (resolved ? "resolved" : "not-resolved") << "\n"; - } - - std::string canonicalLoadedLib; - if (resolved) { - canonicalLoadedLib = libStem.str(); - } else { - canonicalLoadedLib = lookupLibrary(libStem); - if (canonicalLoadedLib.empty()) - return kLoadLibNotFound; - } - - if (m_LoadedLibraries.find(canonicalLoadedLib) != m_LoadedLibraries.end()) - return kLoadLibAlreadyLoaded; - - // TODO: !permanent case - - std::string errMsg; - DyLibHandle dyLibHandle = platform::DLOpen(canonicalLoadedLib, &errMsg); - if (!dyLibHandle) { - // We emit callback to LibraryLoadingFailed when we get error with error message. - if (InterpreterCallbacks* C = getCallbacks()) { - if (C->LibraryLoadingFailed(errMsg, libStem.str(), permanent, resolved)) - return kLoadLibSuccess; - } - - cling::errs() << "cling::DynamicLibraryManager::loadLibrary(): " << errMsg - << '\n'; - return kLoadLibLoadError; - } - else if (InterpreterCallbacks* C = getCallbacks()) - C->LibraryLoaded(dyLibHandle, canonicalLoadedLib); - - std::pair insRes - = m_DyLibs.insert(std::pair(dyLibHandle, - canonicalLoadedLib)); - if (!insRes.second) - return kLoadLibAlreadyLoaded; - m_LoadedLibraries.insert(canonicalLoadedLib); - return kLoadLibSuccess; - } - - void DynamicLibraryManager::unloadLibrary(llvm::StringRef libStem) { - std::string canonicalLoadedLib = lookupLibrary(libStem); - if (!isLibraryLoaded(canonicalLoadedLib)) - return; - - DyLibHandle dyLibHandle = nullptr; - for (DyLibs::const_iterator I = m_DyLibs.begin(), E = m_DyLibs.end(); - I != E; ++I) { - if (I->second == canonicalLoadedLib) { - dyLibHandle = I->first; - break; - } - } - - // TODO: !permanent case - - std::string errMsg; - platform::DLClose(dyLibHandle, &errMsg); - if (!errMsg.empty()) { - cling::errs() << "cling::DynamicLibraryManager::unloadLibrary(): " - << errMsg << '\n'; - } - - if (InterpreterCallbacks* C = getCallbacks()) - C->LibraryUnloaded(dyLibHandle, canonicalLoadedLib); - - m_DyLibs.erase(dyLibHandle); - m_LoadedLibraries.erase(canonicalLoadedLib); - } - - bool DynamicLibraryManager::isLibraryLoaded(llvm::StringRef fullPath) const { - std::string canonPath = normalizePath(fullPath); - if (m_LoadedLibraries.find(canonPath) != m_LoadedLibraries.end()) - return true; - return false; - } - - void DynamicLibraryManager::dump(llvm::raw_ostream* S /*= nullptr*/) const { - llvm::raw_ostream &OS = S ? *S : cling::outs(); - - // FIXME: print in a stable order the contents of m_SearchPaths - for (const auto& Info : getSearchPaths()) { - if (!Info.IsUser) - OS << "[system] "; - OS << Info.Path.c_str() << "\n"; - } - } - - bool DynamicLibraryManager::isSharedLibrary(llvm::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::ifstream in(libFullPath.str(), 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 && - platform::IsDLL(libFullPath.str())) -#else -# error "Unsupported platform." -#endif - ; - - return result; - } - -} // end namespace cling diff --git a/interpreter/cling/lib/Interpreter/DynamicLibraryManagerSymbol.cpp b/interpreter/cling/lib/Interpreter/DynamicLibraryManagerSymbol.cpp deleted file mode 100644 index 5d523776aa003..0000000000000 --- a/interpreter/cling/lib/Interpreter/DynamicLibraryManagerSymbol.cpp +++ /dev/null @@ -1,1432 +0,0 @@ -//------------------------------------------------------------------------------ -// CLING - the C++ LLVM-based InterpreterG :) -// author: Vassil Vassilev -// author: Alexander Penev -// -// This file is dual-licensed: you can choose to license it under the University -// of Illinois Open Source License or the GNU Lesser General Public License. See -// LICENSE.TXT for details. -//------------------------------------------------------------------------------ - -#include "cling/Interpreter/DynamicLibraryManager.h" -#include "cling/Utils/Paths.h" -#include "cling/Utils/Platform.h" -#include "cling/Utils/Output.h" - -#include "llvm/ADT/SmallSet.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/BinaryFormat/MachO.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/DynamicLibrary.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Program.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/WithColor.h" - -#include -#include -#include -#include -#include - -#if defined (__FreeBSD__) -#include -#include -#include -#include - -// libprocstat pulls in sys/elf.h which seems to clash with llvm/BinaryFormat/ELF.h -// similar collision happens with ZFS. Defining ZFS disables this include. -# ifndef ZFS -# define ZFS -# define defined_ZFS_for_libprocstat -# endif -#include -# ifdef defined_ZFS_for_libprocstat -# undef ZFS -# undef defined_ZFS_for_libprocstat -# endif - -#include -#endif - -#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 -#include -#include // For GetModuleFileNameA -#include // For VirtualQuery -#endif - -// FIXME: Implement debugging output stream in cling. -constexpr unsigned DEBUG = 0; - -namespace { - -using BasePath = std::string; - -// This is a GNU implementation of hash used in bloom filter! -static uint32_t GNUHash(llvm::StringRef S) { - uint32_t H = 5381; - for (uint8_t C : S) - H = (H << 5) + H + C; - return H; -} - -constexpr uint32_t log2u(std::uint32_t n) { - return (n > 1) ? 1 + log2u(n >> 1) : 0; -} - -struct BloomFilter { - - // https://hur.st/bloomfilter - // - // n = ceil(m / (-k / log(1 - exp(log(p) / k)))) - // p = pow(1 - exp(-k / (m / n)), k) - // m = ceil((n * log(p)) / log(1 / pow(2, log(2)))); - // k = round((m / n) * log(2)); - // - // n = symbolsCount - // p = 0.02 - // k = 2 (k1=GNUHash and k2=GNUHash >> bloomShift) - // m = ceil((symbolsCount * log(p)) / log(1 / pow(2, log(2)))); - // bloomShift = min(5 for bits=32 or 6 for bits=64, log2(symbolsCount)) - // bloomSize = ceil((-1.44 * n * log2f(p)) / bits) - - const int m_Bits = 8 * sizeof(uint64_t); - const float m_P = 0.02; - - bool m_IsInitialized = false; - uint32_t m_SymbolsCount = 0; - uint32_t m_BloomSize = 0; - uint32_t m_BloomShift = 0; - std::vector m_BloomTable; - - bool TestHash(uint32_t hash) const { - // This function is superhot. No branches here, breaks inlining and makes - // overall performance around 4x slower. - assert(m_IsInitialized && "Not yet initialized!"); - uint32_t hash2 = hash >> m_BloomShift; - uint32_t n = (hash >> log2u(m_Bits)) % m_BloomSize; - uint64_t mask = ((1ULL << (hash % m_Bits)) | (1ULL << (hash2 % m_Bits))); - return (mask & m_BloomTable[n]) == mask; - } - - void AddHash(uint32_t hash) { - assert(m_IsInitialized && "Not yet initialized!"); - uint32_t hash2 = hash >> m_BloomShift; - uint32_t n = (hash >> log2u(m_Bits)) % m_BloomSize; - uint64_t mask = ((1ULL << (hash % m_Bits)) | (1ULL << (hash2 % m_Bits))); - m_BloomTable[n] |= mask; - } - - void ResizeTable(uint32_t newSymbolsCount) { - assert(m_SymbolsCount == 0 && "Not supported yet!"); - m_SymbolsCount = newSymbolsCount; - m_BloomSize = ceil((-1.44f * m_SymbolsCount * log2f(m_P)) / m_Bits); - m_BloomShift = std::min(6u, log2u(m_SymbolsCount)); - m_BloomTable.resize(m_BloomSize); - } - -}; - -/// 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`, m_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& m_Path; - std::string m_LibName; - BloomFilter m_Filter; - llvm::StringSet<> m_Symbols; - //std::vector m_LibDeps; - - LibraryPath(const BasePath& Path, const std::string& LibName) - : m_Path(Path), m_LibName(LibName) { - } - - bool operator==(const LibraryPath &other) const { - return (&m_Path == &other.m_Path || m_Path == other.m_Path) && - m_LibName == other.m_LibName; - } - - const std::string GetFullName() const { - llvm::SmallString<512> Vec(m_Path); - llvm::sys::path::append(Vec, llvm::StringRef(m_LibName)); - return Vec.str().str(); - } - - void AddBloom(llvm::StringRef symbol) { - m_Filter.AddHash(GNUHash(symbol)); - } - - llvm::StringRef AddSymbol(const std::string& symbol) { - auto it = m_Symbols.insert(symbol); - return it.first->getKey(); - } - - bool hasBloomFilter() const { - return m_Filter.m_IsInitialized; - } - - bool isBloomFilterEmpty() const { - assert(m_Filter.m_IsInitialized && "Bloom filter not initialized!"); - return m_Filter.m_SymbolsCount == 0; - } - - void InitializeBloomFilter(uint32_t newSymbolsCount) { - assert(!m_Filter.m_IsInitialized && - "Cannot re-initialize non-empty filter!"); - m_Filter.m_IsInitialized = true; - m_Filter.ResizeTable(newSymbolsCount); - } - - bool MayExistSymbol(uint32_t hash) const { - // The library had no symbols and the bloom filter is empty. - if (isBloomFilterEmpty()) - return false; - - return m_Filter.TestHash(hash); - } - - bool ExistSymbol(llvm::StringRef symbol) const { - return m_Symbols.find(symbol) != m_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 (m_LibName) are relatively short -/// strings and the base paths (m_Path) are repetitive long strings. -class LibraryPaths { - struct LibraryPathHashFn { - size_t operator()(const LibraryPath& item) const { - return std::hash()(item.m_Path.length()) ^ - std::hash()(item.m_LibName); - } - }; - - std::vector m_Libs; - std::unordered_set m_LibsH; -public: - bool HasRegisteredLib(const LibraryPath& Lib) const { - return m_LibsH.count(Lib); - } - - const LibraryPath* GetRegisteredLib(const LibraryPath& Lib) const { - auto search = m_LibsH.find(Lib); - if (search != m_LibsH.end()) - return &(*search); - return nullptr; - } - - const LibraryPath* RegisterLib(const LibraryPath& Lib) { - auto it = m_LibsH.insert(Lib); - assert(it.second && "Already registered!"); - m_Libs.push_back(&*it.first); - return &*it.first; - } - - void UnregisterLib(const LibraryPath& Lib) { - auto found = m_LibsH.find(Lib); - if (found == m_LibsH.end()) - return; - - m_Libs.erase(std::find(m_Libs.begin(), m_Libs.end(), &*found)); - m_LibsH.erase(found); - } - - size_t size() const { - assert(m_Libs.size() == m_LibsH.size()); - return m_Libs.size(); - } - - const std::vector& GetLibraries() const { - return m_Libs; - } -}; - -#ifndef _WIN32 -// Cached version of system function lstat -static inline mode_t cached_lstat(const char *path) { - static llvm::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 llvm::StringRef cached_readlink(const char* pathname) { - static llvm::StringMap readlink_cache; - - // If already cached - retun cached result - auto it = readlink_cache.find(pathname); - if (it != readlink_cache.end()) - return llvm::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(llvm::StringRef path, llvm::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 llvm::StringMap> cache; - bool relative_path = llvm::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 - - llvm::StringRef sep(llvm::sys::path::get_separator()); - llvm::SmallString<256> result(sep); -#ifndef _WIN32 - llvm::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 || llvm::sys::path::is_separator(path[1]))) { - static llvm::SmallString<128> home; - if (home.str().empty()) - llvm::sys::path::home_directory(home); - llvm::StringRef(home).split(p, sep, /*MaxSplit*/ -1, /*KeepEmpty*/ false); - } else if (base_path.empty()) { - static llvm::SmallString<256> current_path; - if (current_path.str().empty()) - llvm::sys::fs::current_path(current_path); - llvm::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 != llvm::StringRef::npos) - result.resize(s); - if (result.empty()) - result = sep; - continue; - } - - size_t old_size = result.size(); - llvm::sys::path::append(result, item); - mode_t st_mode = cached_lstat(result.c_str()); - if (S_ISLNK(st_mode)) { - llvm::StringRef symlink = cached_readlink(result.c_str()); - if (llvm::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 - llvm::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 llvm::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 llvm::StringRef GetGnuHashSection(llvm::object::ObjectFile *file) { - for (auto S : file->sections()) { - llvm::StringRef name = llvm::cantFail(S.getName()); - if (name == ".gnu.hash") { - return llvm::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(llvm::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(); - - llvm::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; -} - -} // anon 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 cling::DynamicLibraryManager::getSymbolLocation(&GetExecutablePath); -} - -namespace cling { - class Dyld { - 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 llvm::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 m_Paths; - public: - const BasePath& RegisterBasePath(const std::string& Path, - bool* WasInserted = nullptr) { - auto it = m_Paths.insert(Path); - if (WasInserted) - *WasInserted = it.second; - - return *it.first; - } - - bool Contains(StringRef Path) { - return m_Paths.count(Path.str()); - } - }; - - bool m_FirstRun = true; - bool m_FirstRunSysLib = true; - bool m_UseBloomFilter = true; - bool m_UseHashTable = true; - - const cling::DynamicLibraryManager& m_DynamicLibraryManager; - - /// The basename of `/home/.../lib/libA.so`, - /// m_BasePaths will contain `/home/.../lib/` - BasePaths m_BasePaths; - - LibraryPaths m_Libraries; - LibraryPaths m_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 m_QueriedLibraries; - - using PermanentlyIgnoreCallbackProto = std::function; - const PermanentlyIgnoreCallbackProto m_ShouldPermanentlyIgnoreCallback; - const llvm::StringRef m_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, llvm::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: - Dyld(const cling::DynamicLibraryManager &DLM, - PermanentlyIgnoreCallbackProto shouldIgnore, - llvm::StringRef execFormat) - : m_DynamicLibraryManager(DLM), - m_ShouldPermanentlyIgnoreCallback(shouldIgnore), - m_ExecutableFormat(execFormat) { } - - ~Dyld(){}; - - std::string searchLibrariesForSymbol(StringRef mangledName, - bool searchSystem); - }; - - std::string RPathToStr(llvm::SmallVector V) { - std::string result; - for (auto item : V) - result += item.str() + ","; - if (!result.empty()) - result.pop_back(); - return result; - } - - void CombinePaths(std::string& P1, const char* P2) { - if (!P2 || !P2[0]) return; - if (!P1.empty()) - P1 += llvm::sys::EnvPathSeparator; - P1 += P2; - } - - template - void HandleDynTab(const ELFFile* Elf, llvm::StringRef FileName, - llvm::SmallVector& RPath, - llvm::SmallVector& RunPath, - std::vector& Deps, - bool& isPIEExecutable) { - const char *Data = ""; - if (Expected StrTabOrErr = getDynamicStrTab(Elf)) - Data = StrTabOrErr.get().data(); - - isPIEExecutable = false; - - auto DynamicEntriesOrError = Elf->dynamicEntries(); - if (!DynamicEntriesOrError) { - cling::errs() << "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, utils::kAllowNonExistant, platform::kEnvDelim, false); - break; - case ELF::DT_RUNPATH: - SplitPaths(Data + Dyn.d_un.d_val, RunPath, utils::kAllowNonExistant, platform::kEnvDelim, false); - break; - case ELF::DT_FLAGS_1: - // Check if this is not a pie executable. - if (Dyn.d_un.d_val & llvm::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) - } - } - } - - void Dyld::ScanForLibraries(bool searchSystemLibraries/* = false*/) { - - const auto &searchPaths = m_DynamicLibraryManager.getSearchPaths(); - - if (DEBUG > 7) { - cling::errs() << "Dyld::ScanForLibraries: system=" << (searchSystemLibraries?"true":"false") << "\n"; - for (const DynamicLibraryManager::SearchPathInfo &Info : searchPaths) - cling::errs() << ">>>" << Info.Path << ", " << (Info.IsUser?"user\n":"system\n"); - } - - llvm::SmallSet ScannedPaths; - - for (const DynamicLibraryManager::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 - - // m_BasePaths = ["/lib/1", "/lib/3", "/system/lib"] - // m_*Libraries = [<0,"1.so">, <1,"1.so">, <2,"s.so">, <1,"3.so">] - - if (DEBUG > 7) { - cling::errs() << "Dyld::ScanForLibraries Iter:" << Info.Path << " -> "; - } - std::string RealPath = cached_realpath(Info.Path); - - llvm::StringRef DirPath(RealPath); - if (DEBUG > 7) { - cling::errs() << RealPath << "\n"; - } - - if (!llvm::sys::fs::is_directory(DirPath) || DirPath.empty()) - continue; - - // Already searched? - const BasePath &ScannedBPath = m_BasePaths.RegisterBasePath(RealPath); - if (ScannedPaths.count(&ScannedBPath)) { - if (DEBUG > 7) { - cling::errs() << "Dyld::ScanForLibraries Already scanned: " << RealPath << "\n"; - } - continue; - } - - // FileName must be always full/absolute/resolved file name. - std::function HandleLib = - [&](llvm::StringRef FileName, unsigned level) { - - if (DEBUG > 7) { - cling::errs() << "Dyld::ScanForLibraries HandleLib:" << FileName.str() - << ", level=" << level << " -> "; - } - - llvm::StringRef FileRealPath = llvm::sys::path::parent_path(FileName); - llvm::StringRef FileRealName = llvm::sys::path::filename(FileName); - const BasePath& BaseP = - m_BasePaths.RegisterBasePath(FileRealPath.str()); - LibraryPath LibPath(BaseP, FileRealName.str()); //bp, str - - if (m_SysLibraries.GetRegisteredLib(LibPath) || - m_Libraries.GetRegisteredLib(LibPath)) { - if (DEBUG > 7) { - cling::errs() << "Already handled!!!\n"; - } - return; - } - - if (ShouldPermanentlyIgnore(FileName)) { - if (DEBUG > 7) { - cling::errs() << "PermanentlyIgnored!!!\n"; - } - return; - } - - if (searchSystemLibraries) - m_SysLibraries.RegisterLib(LibPath); - else - m_Libraries.RegisterLib(LibPath); - - // Handle lib dependencies - llvm::SmallVector RPath; - llvm::SmallVector RunPath; - std::vector Deps; - auto ObjFileOrErr = - llvm::object::ObjectFile::createObjectFile(FileName); - if (llvm::Error Err = ObjFileOrErr.takeError()) { - // 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(std::move(Err), [&](llvm::ErrorInfoBase& EIB) { - Message += EIB.message() + "; "; - }); - if (DEBUG > 1) { - cling::errs() - << "Dyld::ScanForLibraries: Failed to read object file " - << FileName.str() << " Errors: " << Message << "\n"; - } - return; - } - llvm::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) - m_SysLibraries.UnregisterLib(LibPath); - else - m_Libraries.UnregisterLib(LibPath); - return; - } - } else if (BinObjF->isMachO()) { - MachOObjectFile *Obj = (MachOObjectFile*)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)); - } - else if (Command.C.cmd == MachO::LC_RPATH) { - MachO::rpath_command rpathCmd = Obj->getRpathCommand(Command); - SplitPaths(Command.Ptr + rpathCmd.path, RPath, utils::kAllowNonExistant, platform::kEnvDelim, false); - } - } - } else if (BinObjF->isCOFF()) { - // TODO: COFF support - } - - if (DEBUG > 7) { - cling::errs() << "Dyld::ScanForLibraries: Deps Info:\n"; - cling::errs() << "Dyld::ScanForLibraries: RPATH=" << RPathToStr(RPath) << "\n"; - cling::errs() << "Dyld::ScanForLibraries: RUNPATH=" << RPathToStr(RunPath) << "\n"; - int x = 0; - for (StringRef dep : Deps) - cling::errs() << "Dyld::ScanForLibraries: Deps[" << x++ << "]=" << dep.str() << "\n"; - } - - // Heuristics for workaround performance problems: - // (H1) If RPATH and RUNPATH == "" -> skip handling Deps - if (RPath.empty() && RunPath.empty()) { - if (DEBUG > 7) { - cling::errs() << "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(), [&](DynamicLibraryManager::SearchPathInfo item1){ return item==item1.Path; }); }) && - std::all_of(RunPath.begin(), RunPath.end(), [&](StringRef item){ return std::any_of(searchPaths.begin(), searchPaths.end(), [&](DynamicLibraryManager::SearchPathInfo item1){ return item==item1.Path; }); }) ) { - if (DEBUG > 7) { - cling::errs() << "Dyld::ScanForLibraries: Skip all deps by Heuristic2: " << FileName.str() << "\n"; - } - return; - } - - // Handle dependencies - for (StringRef dep : Deps) { - std::string dep_full = - m_DynamicLibraryManager.lookupLibrary(dep, RPath, RunPath, FileName, false); - HandleLib(dep_full, level + 1); - } - - }; - - if (DEBUG > 7) { - cling::errs() << "Dyld::ScanForLibraries: Iterator: " << DirPath << "\n"; - } - std::error_code EC; - for (llvm::sys::fs::directory_iterator DirIt(DirPath, EC), DirEnd; - DirIt != DirEnd && !EC; DirIt.increment(EC)) { - - if (DEBUG > 7) { - cling::errs() << "Dyld::ScanForLibraries: Iterator >>> " << - DirIt->path() << ", type=" << (short)(DirIt->type()) << "\n"; - } - - const llvm::sys::fs::file_type ft = DirIt->type(); - if (ft == llvm::sys::fs::file_type::regular_file) { - HandleLib(DirIt->path(), 0); - } else if (ft == llvm::sys::fs::file_type::symlink_file) { - std::string DepFileName_str = cached_realpath(DirIt->path()); - llvm::StringRef DepFileName = DepFileName_str; - assert(!llvm::sys::fs::is_symlink_file(DepFileName)); - if (!llvm::sys::fs::is_directory(DepFileName)) - HandleLib(DepFileName, 0); - } - } - - // Register the DirPath as fully scanned. - ScannedPaths.insert(&ScannedBPath); - } - } - } - - void Dyld::BuildBloomFilter(LibraryPath* Lib, - llvm::object::ObjectFile *BinObjFile, - unsigned IgnoreSymbolFlags /*= 0*/) const { - assert(m_UseBloomFilter && "Bloom filter is disabled"); - assert(!Lib->hasBloomFilter() && "Already built!"); - - using namespace llvm; - using namespace llvm::object; - - if (DEBUG > 7) { - cling::errs()<< "Dyld::BuildBloomFilter: Start building Bloom filter for: " - << Lib->GetFullName() << "\n"; - } - - // If BloomFilter is empty then build it. - // Count Symbols and generate BloomFilter - uint32_t SymbolsCount = 0; - 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; - - // 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) { - cling::errs()<< "Dyld::BuildBloomFilter: Failed to read symbol " - << SymNameErr.get() << "\n"; - continue; - } - - 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) { - cling::errs() << "Dyld::BuildBloomFilter: Failed to read symbol " - <isCOFF()) { // On Windows, the symbols are present in COFF format. - llvm::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 (auto I=CoffObj->export_directory_begin(), - E=CoffObj->export_directory_end(); I != E; I = ++I) { - // All the symbols are already flagged as exported. - // We cannot really ignore symbols based on flags as we do on unix. - StringRef Name; - if (I->getSymbolName(Name)) - continue; - if (Name.empty()) - continue; - - ++SymbolsCount; - symbols.push_back(Name); - } - } - - Lib->InitializeBloomFilter(SymbolsCount); - - if (!SymbolsCount) { - if (DEBUG > 7) - cling::errs() << "Dyld::BuildBloomFilter: No symbols!\n"; - return; - } - - if (DEBUG > 7) { - cling::errs() << "Dyld::BuildBloomFilter: Symbols:\n"; - for (auto it : symbols) - cling::errs() << "Dyld::BuildBloomFilter" << "- " << it << "\n"; - } - - // Generate BloomFilter - for (const auto &S : symbols) { - if (m_UseHashTable) - Lib->AddBloom(Lib->AddSymbol(S.str())); - else - Lib->AddBloom(S); - } - } - - bool Dyld::ContainsSymbol(const LibraryPath* Lib, - StringRef mangledName, - unsigned IgnoreSymbolFlags /*= 0*/) const { - const std::string library_filename = Lib->GetFullName(); - - if (DEBUG > 7) { - cling::errs() << "Dyld::ContainsSymbol: Find symbol: lib=" - << library_filename << ", mangled=" - << mangledName.str() << "\n"; - } - - auto ObjF = llvm::object::ObjectFile::createObjectFile(library_filename); - if (llvm::Error Err = ObjF.takeError()) { - // 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(std::move(Err), [&](llvm::ErrorInfoBase& EIB) { - Message += EIB.message() + "; "; - }); - if (DEBUG > 1) { - cling::errs() << "Dyld::ContainsSymbol: Failed to read object file " - << library_filename << " Errors: " << Message << "\n"; - } - return false; - } - - llvm::object::ObjectFile *BinObjFile = ObjF.get().getBinary(); - - uint32_t hashedMangle = GNUHash(mangledName); - // Check for the gnu.hash section if ELF. - // If the symbol doesn't exist, exit early. - if (BinObjFile->isELF() && - !MayExistInElfObjectFile(BinObjFile, hashedMangle)) { - if (DEBUG > 7) - cling::errs() << "Dyld::ContainsSymbol: ELF BloomFilter: Skip symbol <" << mangledName.str() << ">.\n"; - return false; - } - - if (m_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(hashedMangle)) { - if (DEBUG > 7) - cling::errs() << "Dyld::ContainsSymbol: BloomFilter: Skip symbol <" << mangledName.str() << ">.\n"; - return false; - } - if (DEBUG > 7) - cling::errs() << "Dyld::ContainsSymbol: BloomFilter: Symbol <" << mangledName.str() << "> May exist." - << " Search for it. "; - } - - if (m_UseHashTable) { - bool result = Lib->ExistSymbol(mangledName); - if (DEBUG > 7) - cling::errs() << "Dyld::ContainsSymbol: HashTable: Symbol " - << (result ? "Exist" : "Not exist") << "\n"; - return result; - } - - auto ForeachSymbol = - [&library_filename](llvm::iterator_range range, - unsigned IgnoreSymbolFlags, llvm::StringRef mangledName) -> bool { - for (const llvm::object::SymbolRef &S : range) { - uint32_t Flags = llvm::cantFail(S.getFlags()); - // Do not insert in the table symbols flagged to ignore. - 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. - - llvm::Expected SymNameErr = S.getName(); - if (!SymNameErr) { - cling::errs() << "Dyld::ContainsSymbol: Failed to read symbol " - << mangledName.str() << "\n"; - continue; - } - - if (SymNameErr.get().empty()) - continue; - - if (SymNameErr.get() == mangledName) { - if (DEBUG > 1) { - cling::errs() << "Dyld::ContainsSymbol: Symbol " - << mangledName.str() << " found in " - << library_filename << "\n"; - return true; - } - } - } - return false; - }; - - // If no hash symbol then iterate to detect symbol - // We Iterate only if BloomFilter and/or SymbolHashTable are not supported. - - if (DEBUG > 7) - cling::errs() << "Dyld::ContainsSymbol: Iterate all for <" - << mangledName.str() << ">"; - - // Symbol may exist. Iterate. - if (ForeachSymbol(BinObjFile->symbols(), IgnoreSymbolFlags, mangledName)) { - if (DEBUG > 7) - cling::errs() << " -> found.\n"; - return true; - } - - - if (!BinObjFile->isELF()) { - if (DEBUG > 7) - cling::errs() << " -> not found.\n"; - return false; - } - - // ELF file format has .dynstr section for the dynamic symbol table. - const auto *ElfObj = - llvm::cast(BinObjFile); - - bool result = ForeachSymbol(ElfObj->getDynamicSymbolIterators(), - IgnoreSymbolFlags, mangledName); - if (DEBUG > 7) - cling::errs() << (result ? " -> found.\n" : " -> not found.\n"); - return result; - } - - bool Dyld::ShouldPermanentlyIgnore(StringRef FileName) const { - assert(!m_ExecutableFormat.empty() && "Failed to find the object format!"); - - if (!cling::DynamicLibraryManager::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 (m_DynamicLibraryManager.isLibraryLoaded(FileName)) - return true; - - - auto ObjF = llvm::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() + "; "; - }); - if (DEBUG > 1) - cling::errs() << "[DyLD] Failed to read object file " - << FileName << ". Message: '" << Message << "\n"; - return true; - } - - llvm::object::ObjectFile *file = ObjF.get().getBinary(); - - if (DEBUG > 1) - cling::errs() << "Current executable format: " << m_ExecutableFormat - << ". Executable format of " << FileName << " : " - << file->getFileFormatName() << "\n"; - - // Ignore libraries with different format than the executing one. - if (m_ExecutableFormat != file->getFileFormatName()) - return true; - - if (llvm::isa(*file)) { - for (auto S : file->sections()) { - llvm::StringRef name = llvm::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() == llvm::ELF::SHT_NOBITS) - return true; - - return (SecRef.getFlags() & llvm::ELF::SHF_ALLOC) == 0; - } - } - return true; - } - - //FIXME: Handle osx using isStripped after upgrading to llvm9. - - return m_ShouldPermanentlyIgnoreCallback(FileName); - } - - void Dyld::dumpDebugInfo() const { - cling::errs() << "Dyld: m_BasePaths:\n"; - cling::errs() << "---\n"; - size_t x = 0; - for (auto const &item : m_BasePaths.m_Paths) { - cling::errs() << "Dyld: - m_BasePaths[" << x++ << "]:" - << &item << ": " << item << "\n"; - } - cling::errs() << "---\n"; - x = 0; - for (auto const &item : m_Libraries.GetLibraries()) { - cling::errs() << "Dyld: - m_Libraries[" << x++ << "]:" - << &item << ": " << item->m_Path << ", " - << item->m_LibName << "\n"; - } - x = 0; - for (auto const &item : m_SysLibraries.GetLibraries()) { - cling::errs() << "Dyld: - m_SysLibraries[" << x++ << "]:" - << &item << ": " << item->m_Path << ", " - << item->m_LibName << "\n"; - } - } - - std::string Dyld::searchLibrariesForSymbol(StringRef mangledName, - bool searchSystem/* = true*/) { - assert(!llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(mangledName.str()) && - "Library already loaded, please use dlsym!"); - assert(!mangledName.empty()); - - using namespace llvm::sys::path; - using namespace llvm::sys::fs; - - if (m_FirstRun) { - if (DEBUG > 7) { - cling::errs() << "Dyld::searchLibrariesForSymbol:" << mangledName.str() << - ", searchSystem=" << (searchSystem ? "true" : "false") << ", FirstRun(user)... scanning\n"; - } - - if (DEBUG > 7) { - cling::errs() << "Dyld::searchLibrariesForSymbol: Before first ScanForLibraries\n"; - dumpDebugInfo(); - } - - ScanForLibraries(/* SearchSystemLibraries= */ false); - m_FirstRun = false; - - if (DEBUG > 7) { - cling::errs() << "Dyld::searchLibrariesForSymbol: After first ScanForLibraries\n"; - dumpDebugInfo(); - } - } - - if (m_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. - - if (DEBUG > 7) { - cling::errs() << "Dyld::ResolveSymbol: m_QueriedLibraries:\n"; - size_t x = 0; - for (auto item : m_QueriedLibraries.GetLibraries()) { - cling::errs() << "Dyld::ResolveSymbol - [" << x++ << "]:" - << &item << ": " << item->GetFullName() << "\n"; - } - } - - for (const LibraryPath* P : m_QueriedLibraries.GetLibraries()) { - const std::string LibName = P->GetFullName(); - if (!m_DynamicLibraryManager.isLibraryLoaded(LibName)) - continue; - - m_Libraries.UnregisterLib(*P); - m_SysLibraries.UnregisterLib(*P); - } - // TODO: m_QueriedLibraries.clear ? - } - - // Iterate over files under this path. We want to get each ".so" files - for (const LibraryPath* P : m_Libraries.GetLibraries()) { - if (ContainsSymbol(P, mangledName, /*ignore*/ - llvm::object::SymbolRef::SF_Undefined)) { - if (!m_QueriedLibraries.HasRegisteredLib(*P)) - m_QueriedLibraries.RegisterLib(*P); - - if (DEBUG > 7) - cling::errs() << "Dyld::ResolveSymbol: Search found match in [user lib]: " - << P->GetFullName() << "!\n"; - - return P->GetFullName(); - } - } - - if (!searchSystem) - return ""; - - if (DEBUG > 7) - cling::errs() << "Dyld::searchLibrariesForSymbol: SearchSystem!!!\n"; - - // Lookup in non-system libraries failed. Expand the search to the system. - if (m_FirstRunSysLib) { - if (DEBUG > 7) { - cling::errs() << "Dyld::searchLibrariesForSymbol:" << mangledName.str() << - ", searchSystem=" << (searchSystem ? "true" : "false") << ", FirstRun(system)... scanning\n"; - } - - if (DEBUG > 7) { - cling::errs() << "Dyld::searchLibrariesForSymbol: Before first system ScanForLibraries\n"; - dumpDebugInfo(); - } - - ScanForLibraries(/* SearchSystemLibraries= */ true); - m_FirstRunSysLib = false; - - if (DEBUG > 7) { - cling::errs() << "Dyld::searchLibrariesForSymbol: After first system ScanForLibraries\n"; - dumpDebugInfo(); - } - } - - for (const LibraryPath* P : m_SysLibraries.GetLibraries()) { - if (ContainsSymbol(P, mangledName, /*ignore*/ - llvm::object::SymbolRef::SF_Undefined | - llvm::object::SymbolRef::SF_Weak)) { - if (!m_QueriedLibraries.HasRegisteredLib(*P)) - m_QueriedLibraries.RegisterLib(*P); - - if (DEBUG > 7) - cling::errs() << "Dyld::ResolveSymbol: Search found match in [system lib]: " - << P->GetFullName() << "!\n"; - - return P->GetFullName(); - } - } - - if (DEBUG > 7) - cling::errs() << "Dyld::ResolveSymbol: Search found no match!\n"; - - return ""; // Search found no match. - } - - DynamicLibraryManager::~DynamicLibraryManager() { - static_assert(sizeof(Dyld) > 0, "Incomplete type"); - delete m_Dyld; - } - - void DynamicLibraryManager::initializeDyld( - std::function shouldPermanentlyIgnore) { - //assert(!m_Dyld && "Already initialized!"); - if (m_Dyld) - delete m_Dyld; - - std::string exeP = GetExecutablePath(); - auto ObjF = - cantFail(llvm::object::ObjectFile::createObjectFile(exeP)); - - m_Dyld = new Dyld(*this, shouldPermanentlyIgnore, - ObjF.getBinary()->getFileFormatName()); - } - - std::string - DynamicLibraryManager::searchLibrariesForSymbol(StringRef mangledName, - bool searchSystem/* = true*/) const { - assert(m_Dyld && "Must call initialize dyld before!"); - return m_Dyld->searchLibrariesForSymbol(mangledName, searchSystem); - } - - std::string DynamicLibraryManager::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 (__FreeBSD__) - procstat* ps = procstat_open_sysctl(); - kinfo_proc* kp = kinfo_getproc(getpid()); - - char buf[PATH_MAX] = ""; - if (kp!=NULL) { - procstat_getpathname(ps, kp, buf, sizeof(buf)); - }; - free(kp); - procstat_close(ps); - return cached_realpath(buf); -# 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 cling diff --git a/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp b/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp index 1752ef2844984..1c8a5c1101d24 100644 --- a/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp +++ b/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp @@ -1,6 +1,5 @@ #include "ForwardDeclPrinter.h" -#include "cling/Interpreter/DynamicLibraryManager.h" #include "cling/Interpreter/Transaction.h" #include "cling/Utils/AST.h" #include "cling/Utils/Output.h" diff --git a/interpreter/cling/lib/Interpreter/IncrementalExecutor.cpp b/interpreter/cling/lib/Interpreter/IncrementalExecutor.cpp index 17db71c5db6f4..46e9e20076721 100644 --- a/interpreter/cling/lib/Interpreter/IncrementalExecutor.cpp +++ b/interpreter/cling/lib/Interpreter/IncrementalExecutor.cpp @@ -12,7 +12,6 @@ #include "IncrementalJIT.h" #include "Threading.h" -#include "cling/Interpreter/DynamicLibraryManager.h" #include "cling/Interpreter/Transaction.h" #include "cling/Interpreter/Value.h" #include "cling/Utils/AST.h" @@ -22,7 +21,9 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Frontend/CompilerInstance.h" +#include "llvm/ExecutionEngine/Orc/AutoLoadEPC.h" #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" +#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/ADT/SmallPtrSet.h" @@ -41,13 +42,11 @@ IncrementalExecutor::IncrementalExecutor(clang::DiagnosticsEngine& /*diags*/, : m_Diags(diags) #endif { - m_DyLibManager.initializeDyld([](llvm::StringRef){/*ignore*/ return false;}); - // MSVC doesn't support m_AtExitFuncsSpinLock=ATOMIC_FLAG_INIT; in the class definition std::atomic_flag_clear( &m_AtExitFuncsSpinLock ); llvm::Error Err = llvm::Error::success(); - auto EPC = llvm::cantFail(llvm::orc::SelfExecutorProcessControl::Create()); + auto EPC = llvm::cantFail(llvm::orc::AutoLoadEPC::Create()); m_JIT.reset(new IncrementalJIT(*this, CI, std::move(EPC), Err, ExtraLibHandle, Verbose)); if (Err) { @@ -56,6 +55,9 @@ IncrementalExecutor::IncrementalExecutor(clang::DiagnosticsEngine& /*diags*/, } m_BackendPasses.reset(new BackendPasses(CI.getCodeGenOpts(), *m_JIT, m_JIT->getTargetMachine())); + + ClingEPC = static_cast( + &m_JIT->getLLJIT()->getExecutionSession().getExecutorProcessControl()); } IncrementalExecutor::~IncrementalExecutor() {} @@ -222,7 +224,7 @@ IncrementalExecutor::executeWrapper(llvm::StringRef function, void IncrementalExecutor::setCallbacks(InterpreterCallbacks* callbacks) { m_Callbacks = callbacks; - m_DyLibManager.setCallbacks(callbacks); + ClingEPC->setCallbacks(callbacks); } void IncrementalExecutor::addGenerator( @@ -334,8 +336,9 @@ bool IncrementalExecutor::diagnoseUnresolvedSymbols(llvm::StringRef trigger, << "Maybe you need to load the corresponding shared library?\n"; } - std::string libName = m_DyLibManager.searchLibrariesForSymbol(sym, - /*searchSystem=*/ true); + std::string libName = ClingEPC->getDylibLookup().searchLibrariesForSymbol( + sym, + /*searchSystem=*/true); if (!libName.empty()) cling::errs() << "Symbol found in '" << libName << "';" << " did you mean to load it with '.L " diff --git a/interpreter/cling/lib/Interpreter/IncrementalExecutor.h b/interpreter/cling/lib/Interpreter/IncrementalExecutor.h index eeb89f2eabdd1..fff9de9cbf4fe 100644 --- a/interpreter/cling/lib/Interpreter/IncrementalExecutor.h +++ b/interpreter/cling/lib/Interpreter/IncrementalExecutor.h @@ -15,7 +15,6 @@ #include "BackendPasses.h" #include "EnterUserCodeRAII.h" -#include "cling/Interpreter/DynamicLibraryManager.h" #include "cling/Interpreter/InterpreterCallbacks.h" #include "cling/Interpreter/Transaction.h" #include "cling/Interpreter/Value.h" @@ -130,7 +129,8 @@ namespace cling { /// Dynamic library manager object. /// - DynamicLibraryManager m_DyLibManager; + // DynamicLibraryManager m_DyLibManager; + llvm::orc::AutoLoadEPC *ClingEPC; public: enum ExecutionResult { @@ -157,11 +157,12 @@ namespace cling { ///\brief Return the LLJIT held by the IncrementalJIT llvm::orc::LLJIT* getLLJIT() { return m_JIT ? m_JIT->getLLJIT() : nullptr; } - const DynamicLibraryManager& getDynamicLibraryManager() const { - return const_cast(this)->m_DyLibManager; + const llvm::orc::AutoLoadEPC *getClingEPC() const { + return const_cast(this)->ClingEPC; } - DynamicLibraryManager& getDynamicLibraryManager() { - return m_DyLibManager; + + llvm::orc::AutoLoadEPC *getClingEPC() { + return ClingEPC; } /// Register a DefinitionGenerator to dynamically provide symbols for diff --git a/interpreter/cling/lib/Interpreter/Interpreter.cpp b/interpreter/cling/lib/Interpreter/Interpreter.cpp index 6af9010850654..a389ca083f7bb 100644 --- a/interpreter/cling/lib/Interpreter/Interpreter.cpp +++ b/interpreter/cling/lib/Interpreter/Interpreter.cpp @@ -29,7 +29,6 @@ #include "cling/Interpreter/ClingCodeCompleteConsumer.h" #include "cling/Interpreter/CompilationOptions.h" #include "cling/Interpreter/DynamicExprInfo.h" -#include "cling/Interpreter/DynamicLibraryManager.h" #include "cling/Interpreter/Exception.h" #include "cling/Interpreter/IncrementalCUDADeviceCompiler.h" #include "cling/Interpreter/LookupHelper.h" @@ -281,7 +280,7 @@ namespace cling { return; for (const std::string &P : m_Opts.LibSearchPath) - getDynamicLibraryManager()->addSearchPath(P); + getClingEPC()->getDylibLookup().addSearchPath(P); } bool usingCxxModules = getSema().getLangOpts().Modules; @@ -299,8 +298,8 @@ namespace cling { } if (m_Opts.CompilerOpts.CUDAHost && !isInSyntaxOnlyMode()) { - if (getDynamicLibraryManager()->loadLibrary("libcudart.so", true) == - cling::DynamicLibraryManager::LoadLibResult::kLoadLibNotFound){ + if (getClingEPC()->loadDylib("libcudart.so", true, false) == + llvm::orc::AutoLoadEPC::LoadLibResult::kLoadLibNotFound){ llvm::errs() << "Error: libcudart.so not found!\n" << "Please add the cuda lib path to LD_LIBRARY_PATH or set it via -L argument.\n"; } @@ -663,8 +662,8 @@ namespace cling { } void Interpreter::DumpDynamicLibraryInfo(llvm::raw_ostream* S) const { - if (auto DLM = getDynamicLibraryManager()) - DLM->dump(S); + if (auto *EPC = getClingEPC()) + EPC->getDylibLookup().dump(); } // FIXME: Add stream argument and move DumpIncludePath path here. @@ -1443,11 +1442,12 @@ namespace cling { } std::string Interpreter::lookupFileOrLibrary(llvm::StringRef file) { - std::string canonicalFile = DynamicLibraryManager::normalizePath(file); + std::string canonicalFile = + llvm::orc::AutoLoadDynamicLibraryLookup::normalizePath(file); if (canonicalFile.empty()) canonicalFile = file.str(); - //Copied from clang's PPDirectives.cpp + // Copied from clang's PPDirectives.cpp bool isAngled = false; // Clang doc says: // "LookupFrom is set when this is a \#include_next directive, it @@ -1458,36 +1458,37 @@ namespace cling { Preprocessor& PP = getCI()->getPreprocessor(); // PP::LookupFile uses it to issue 'nice' diagnostic SourceLocation fileNameLoc; - auto FE = PP.LookupFile(fileNameLoc, canonicalFile, isAngled, FromDir, - FromFile, CurDir, /*SearchPath*/nullptr, - /*RelativePath*/ nullptr, /*suggestedModule*/nullptr, - /*IsMapped*/ nullptr, /*IsFrameworkFound*/ nullptr, - /*SkipCache*/ false, /*OpenFile*/ false, - /*CacheFail*/ false); + auto FE = + PP.LookupFile(fileNameLoc, canonicalFile, isAngled, FromDir, FromFile, + CurDir, /*SearchPath*/ nullptr, + /*RelativePath*/ nullptr, /*suggestedModule*/ nullptr, + /*IsMapped*/ nullptr, /*IsFrameworkFound*/ nullptr, + /*SkipCache*/ false, /*OpenFile*/ false, + /*CacheFail*/ false); if (FE) return FE->getName().str(); - return getDynamicLibraryManager()->lookupLibrary(canonicalFile); + return getClingEPC()->lookupDylib(canonicalFile); } Interpreter::CompilationResult Interpreter::loadLibrary(const std::string& filename, bool lookup) { - DynamicLibraryManager* DLM = getDynamicLibraryManager(); + llvm::orc::AutoLoadEPC* EPC = getClingEPC(); std::string canonicalLib; if (lookup) - canonicalLib = DLM->lookupLibrary(filename); + canonicalLib = EPC->lookupDylib(filename); - const std::string &library = lookup ? canonicalLib : filename; + const std::string& library = lookup ? canonicalLib : filename; if (!library.empty()) { - switch (DLM->loadLibrary(library, /*permanent*/false, /*resolved*/true)) { - case DynamicLibraryManager::kLoadLibSuccess: // Intentional fall through - case DynamicLibraryManager::kLoadLibAlreadyLoaded: - return kSuccess; - case DynamicLibraryManager::kLoadLibNotFound: - assert(0 && "Cannot find library with existing canonical name!"); - return kFailure; - default: - // Not a source file (canonical name is non-empty) but can't load. - return kFailure; + switch (EPC->loadDylib(library, /*permanent*/ false, /*resolved*/ true)) { + case llvm::orc::AutoLoadEPC::kLoadLibSuccess: // Intentional fall + // through + case llvm::orc::AutoLoadEPC::kLoadLibAlreadyLoaded: return kSuccess; + case llvm::orc::AutoLoadEPC::kLoadLibNotFound: + assert(0 && "Cannot find library with existing canonical name!"); + return kFailure; + default: + // Not a source file (canonical name is non-empty) but can't load. + return kFailure; } } return kMoreInputExpected; @@ -1677,13 +1678,15 @@ namespace cling { return m_Executor->getLLJIT(); } - const DynamicLibraryManager* Interpreter::getDynamicLibraryManager() const { + const llvm::orc::AutoLoadEPC* + Interpreter::getClingEPC() const { assert(m_Executor.get() && "We must have an executor"); - return &m_Executor->getDynamicLibraryManager(); + return m_Executor->getClingEPC(); } - DynamicLibraryManager* Interpreter::getDynamicLibraryManager() { - return const_cast(const_cast( - this)->getDynamicLibraryManager()); + + llvm::orc::AutoLoadEPC* Interpreter::getClingEPC() { + return const_cast( + const_cast(this)->getClingEPC()); } const Transaction* Interpreter::getFirstTransaction() const { diff --git a/interpreter/cling/lib/MetaProcessor/MetaSema.cpp b/interpreter/cling/lib/MetaProcessor/MetaSema.cpp index 3e20bae940bd5..24ae6367bd89f 100644 --- a/interpreter/cling/lib/MetaProcessor/MetaSema.cpp +++ b/interpreter/cling/lib/MetaProcessor/MetaSema.cpp @@ -7,7 +7,6 @@ // LICENSE.TXT for details. //------------------------------------------------------------------------------ -#include "cling/Interpreter/DynamicLibraryManager.h" #include "cling/Interpreter/Interpreter.h" #include "cling/Interpreter/Transaction.h" #include "cling/Interpreter/Value.h" @@ -24,6 +23,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/Orc/AutoLoadEPC.h" #include "llvm/ExecutionEngine/GenericValue.h" #include "llvm/Support/Casting.h" @@ -222,9 +222,9 @@ namespace cling { m_Interpreter.unload(/*numberOfTransactions=*/1); } - DynamicLibraryManager* DLM = m_Interpreter.getDynamicLibraryManager(); - if (DLM->isLibraryLoaded(pathname)) - DLM->unloadLibrary(pathname); + llvm::orc::AutoLoadEPC* EPC = m_Interpreter.getClingEPC(); + if (EPC->IsDylibLoaded(pathname)) + EPC->unloadDylib(pathname); } else { m_MetaProcessor.getOuts() << "!!!ERROR: Transaction for file: " << file << " has already been unloaded\n"; diff --git a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/AutoLoadEPC.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/AutoLoadEPC.h new file mode 100644 index 0000000000000..071613c9ded7f --- /dev/null +++ b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/AutoLoadEPC.h @@ -0,0 +1,127 @@ +//===- AutoLoadEPC.h -- Executor process control for auto-loading -*- 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_CLINGEPC_H +#define LLVM_EXECUTIONENGINE_ORC_CLINGEPC_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" +#include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" +#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibLookup.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" +#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" +#include "llvm/ExecutionEngine/Orc/TaskDispatch.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/MSVCErrorWorkarounds.h" +#include "llvm/TargetParser/Triple.h" + +#include +#include +#include + +namespace llvm { +namespace orc { + +// Temporary implementation for supporting callbacks in AutoLoadEPC. +class ORCCallbacks { +public: + virtual bool LibraryLoadingFailed(const std::string &, const std::string &, + bool, bool) { return 0; } + virtual void LibraryUnloaded(const void *, llvm::StringRef) {} + virtual void LibraryLoaded(const void *, llvm::StringRef) {} +}; + +/// A ExecutorProcessControl implementation targeting the current process. +class AutoLoadEPC : public ExecutorProcessControl, + private InProcessMemoryAccess { +public: + AutoLoadEPC( + std::shared_ptr SSP, std::unique_ptr D, + Triple TargetTriple, unsigned PageSize, + std::unique_ptr MemMgr); + + /// Create a AutoLoadEPC with the given symbol string pool + /// and memory manager. If no symbol string pool is given then one will be + /// created. If no memory manager is given a jitlink::InProcessMemoryManager + /// will be created and used by default. + static Expected> + Create(std::shared_ptr SSP = nullptr, + std::unique_ptr D = nullptr, + std::unique_ptr MemMgr = nullptr); + + /// Describes the result of loading a library. + enum LoadLibResult { + kLoadLibSuccess, ///< library loaded successfully + kLoadLibAlreadyLoaded, ///< library was already loaded + kLoadLibNotFound, ///< library was not found + kLoadLibLoadError, ///< loading the library failed + kLoadLibNumResults + }; + + Expected loadDylib(const char *DylibPath) override; + Expected loadDylib(const char *DylibPath, bool useDLOpen = true); + LoadLibResult loadDylib(StringRef DylibPath, bool Permanent, + bool Resolved = false); + void unloadDylib(StringRef DylibPath); + + Expected> + lookupSymbols(ArrayRef Request) override; + + void resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn F) override; + + Expected runAsMain(ExecutorAddr MainFnAddr, + ArrayRef Args) override; + + Expected runAsVoidFunction(ExecutorAddr VoidFnAddr) override; + + Expected runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override; + + void callWrapperAsync(ExecutorAddr WrapperFnAddr, + IncomingWFRHandler OnComplete, + ArrayRef ArgBuffer) override; + + Error disconnect() override; + + std::string lookupDylib(StringRef Path) { return DylibLookup.lookupLibrary(Path); } + AutoLoadDynamicLibraryLookup &getDylibLookup() { return DylibLookup; } + const AutoLoadDynamicLibraryLookup &getDylibLookup() const { return DylibLookup; } + + bool IsDylibLoaded(StringRef DylibPath) const { + // return LoadedLibraries.find(DylibLookup.normalizePath(DylibPath)) != + // LoadedLibraries.end(); + return DylibLookup.isLibraryLoaded(DylibPath); + } + + void setCallbacks(ORCCallbacks *C) { Callbacks = C; } + ORCCallbacks *getCallbacks() { return Callbacks; } + +private: + static shared::CWrapperFunctionResult + jitDispatchViaWrapperFunctionManager(void *Ctx, const void *FnTag, + const char *Data, size_t Size); + + using DylibHandle = void *; + using DynamicLibs = llvm::DenseMap; + + std::unique_ptr OwnedMemMgr; + AutoLoadDynamicLibraryLookup DylibLookup; + DynamicLibs Dylibs; + // llvm::StringSet<> LoadedLibraries; + ORCCallbacks *Callbacks; + char GlobalManglingPrefix = 0; +}; + +} // end namespace orc +} // end namespace llvm +#endif // LLVM_EXECUTIONENGINE_ORC_CLINGEPC_H \ No newline at end of file diff --git a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h index e56afe4fe656b..4f859c0bedb06 100644 --- a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h +++ b/interpreter/llvm-project/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,50 @@ class EPCDynamicLibrarySearchGenerator : public DefinitionGenerator { AddAbsoluteSymbolsFn AddAbsoluteSymbols; }; +class AutoLoadDynamicLibrarySearchGenerator : public DefinitionGenerator { +public: + using AddAbsoluteSymbolsFn = unique_function; + using IsAutoLoadingEnabledFn = 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, IsAutoLoadingEnabledFn A, AddAbsoluteSymbolsFn AddAbsoluteSymbols = nullptr) + : EPC(ES.getExecutorProcessControl()), + IsAutoLoadingEnabled(std::move(A)), + AddAbsoluteSymbols(std::move(AddAbsoluteSymbols)) {} + + /// Creates a AutoLoadDynamicLibrarySearchGenerator that searches for symbols + /// in the target process. + static Expected> + GetForTargetProcess(ExecutionSession &ES, + IsAutoLoadingEnabledFn A, + AddAbsoluteSymbolsFn AddAbsoluteSymbols = nullptr) { + return std::make_unique( + ES, std::move(A), 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; + IsAutoLoadingEnabledFn IsAutoLoadingEnabled; +}; } // end namespace orc } // end namespace llvm diff --git a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h index 6ee2deef04d09..809edf2d5e927 100644 --- a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h +++ b/interpreter/llvm-project/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 @@ -57,6 +58,17 @@ class EPCGenericDylibManager { Expected> lookup(tpctypes::DylibHandle H, const RemoteSymbolLookupSet &Lookup); + using ResolveSymbolsCompleteFn = + unique_function)>; + + /// Look up and resolve symbols across all available dynamic libraries. + void resolveAsync(const SymbolLookupSet &Lookup, + ResolveSymbolsCompleteFn Complete); + + /// Look up and resolve symbols across all available dynamic libraries. + void resolveAsync(const RemoteSymbolLookupSet &Lookup, + ResolveSymbolsCompleteFn Complete); + private: ExecutorProcessControl &EPC; SymbolAddrs SAs; diff --git a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h index 9e42d6dd615df..b6a26ff4e695a 100644 --- a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h +++ b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h @@ -15,6 +15,8 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" +#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibLookup.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" @@ -284,6 +286,12 @@ class ExecutorProcessControl { /// the target process. virtual Expected loadDylib(const char *DylibPath) = 0; + using ResolveSymbolsCompleteFn = unique_function>)>; + + virtual void resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn F) = 0; + /// Search for symbols in the target process. /// /// The result of the lookup is a 2-dimensional array of target addresses @@ -467,6 +475,11 @@ class UnsupportedExecutorProcessControl : public ExecutorProcessControl, llvm_unreachable("Unsupported"); } + void resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn F) override { + llvm_unreachable("Unsupported"); + } + Expected runAsMain(ExecutorAddr MainFnAddr, ArrayRef Args) override { llvm_unreachable("Unsupported"); @@ -513,6 +526,9 @@ class SelfExecutorProcessControl : public ExecutorProcessControl, Expected> lookupSymbols(ArrayRef Request) override; + void resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn F) override; + Expected runAsMain(ExecutorAddr MainFnAddr, ArrayRef Args) override; @@ -532,6 +548,7 @@ class SelfExecutorProcessControl : public ExecutorProcessControl, const char *Data, size_t Size); std::unique_ptr OwnedMemMgr; + std::optional DylibLookup; char GlobalManglingPrefix = 0; }; diff --git a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibLookup.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibLookup.h new file mode 100644 index 0000000000000..0ba142288eabb --- /dev/null +++ b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibLookup.h @@ -0,0 +1,157 @@ +//===------------ 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); + + /// Add loaded library. + void eraseLib(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/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h new file mode 100644 index 0000000000000..dd8b626b0a649 --- /dev/null +++ b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h @@ -0,0 +1,246 @@ +//===------ 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 +#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; +} + +/// Platform specific delimiter for splitting environment variables. +/// ':' on Unix, and ';' on Windows +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); + +/// Open a handle to a shared library. On Unix the lib is opened with +/// RTLD_LAZY|RTLD_GLOBAL flags. +void *DLOpen(const std::string &Path, std::string *Err = nullptr); + +void *DLSym(const std::string &Name, std::string *Err = nullptr); + +/// Close a handle to a shared library. +void DLClose(void *Lib, std::string *Err = nullptr); + +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(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))); + return (mask & BloomTable[n]) == mask; + } + + // Helper method to add a hash + void AddHash(uint32_t hash) { + 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))); + 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/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h index 0c549bcbf0130..b834ec415820c 100644 --- a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h +++ b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h @@ -13,6 +13,7 @@ #ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_ORCRTBRIDGE_H #define LLVM_EXECUTIONENGINE_ORC_SHARED_ORCRTBRIDGE_H +#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h" #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" #include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h" #include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h" @@ -25,6 +26,7 @@ namespace rt { extern const char *SimpleExecutorDylibManagerInstanceName; extern const char *SimpleExecutorDylibManagerOpenWrapperName; extern const char *SimpleExecutorDylibManagerLookupWrapperName; +extern const char *SimpleExecutorDylibManagerResolveWrapperName; extern const char *SimpleExecutorMemoryManagerInstanceName; extern const char *SimpleExecutorMemoryManagerReserveWrapperName; @@ -59,6 +61,11 @@ using SPSSimpleExecutorDylibManagerLookupSignature = shared::SPSExecutorAddr, shared::SPSExecutorAddr, shared::SPSRemoteSymbolLookupSet); +using SPSSimpleExecutorDylibManagerResolveSignature = shared::SPSExpected< + shared::SPSTuple, + shared::SPSSequence>>( + shared::SPSExecutorAddr, shared::SPSRemoteSymbolLookupSet); + using SPSSimpleExecutorMemoryManagerReserveSignature = shared::SPSExpected(shared::SPSExecutorAddr, uint64_t); diff --git a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h index 25b79be48810c..9bfed4fc6d880 100644 --- a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h +++ b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h @@ -74,6 +74,9 @@ class SimpleRemoteEPC : public ExecutorProcessControl, Expected> lookupSymbols(ArrayRef Request) override; + void resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn F) override; + Expected runAsMain(ExecutorAddr MainFnAddr, ArrayRef Args) override; diff --git a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.h index 00fd84e3ec142..f223c7dc4f27c 100644 --- a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.h +++ b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.h @@ -17,6 +17,8 @@ #define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_SIMPLEEXECUTORDYLIBMANAGER_H #include "llvm/ADT/DenseSet.h" +#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.h" +#include "llvm/ExecutionEngine/Orc/Shared/AutoLoadDylibLookup.h" #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" #include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h" #include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.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/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/AutoLoadEPC.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/AutoLoadEPC.cpp new file mode 100644 index 0000000000000..3aeea3db2b07e --- /dev/null +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/AutoLoadEPC.cpp @@ -0,0 +1,376 @@ +//===--------------- AutoLoadEPC.cpp - EPC for auto-loading ---------------===// +// +// 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/AutoLoadEPC.h" + +#include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Process.h" +#include "llvm/TargetParser/Host.h" + +// #if defined(LLVM_ON_UNIX) +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + +// // PATH_MAX +// #ifdef __APPLE__ +// #include +// #else +// #include +// #endif + +// namespace platform { + +// static void DLErr(std::string *Err) { +// if (!Err) +// return; +// if (const char *DyLibError = ::dlerror()) +// *Err = DyLibError; +// } + +// static void *DLOpen(const std::string &Path, std::string *Err) { +// void *Lib = dlopen(Path.c_str(), RTLD_LAZY | RTLD_GLOBAL); +// DLErr(Err); +// return Lib; +// } + +// static void DLClose(const void *Lib, std::string *Err) { +// ::dlclose(const_cast(Lib)); +// DLErr(Err); +// } + +// } // namespace platform +// #endif + +#define DEBUG_TYPE "orc" + +namespace llvm { +namespace orc { + +AutoLoadEPC::AutoLoadEPC( + std::shared_ptr SSP, std::unique_ptr D, + Triple TargetTriple, unsigned PageSize, + std::unique_ptr MemMgr) + : ExecutorProcessControl(std::move(SSP), std::move(D)), + InProcessMemoryAccess(TargetTriple.isArch64Bit()) { + + OwnedMemMgr = std::move(MemMgr); + if (!OwnedMemMgr) + OwnedMemMgr = std::make_unique( + sys::Process::getPageSizeEstimate()); + + this->TargetTriple = std::move(TargetTriple); + this->PageSize = PageSize; + this->MemMgr = OwnedMemMgr.get(); + this->MemAccess = this; + this->JDI = {ExecutorAddr::fromPtr(jitDispatchViaWrapperFunctionManager), + ExecutorAddr::fromPtr(this)}; + if (this->TargetTriple.isOSBinFormatMachO()) + GlobalManglingPrefix = '_'; + + this->BootstrapSymbols[rt::RegisterEHFrameSectionWrapperName] = + ExecutorAddr::fromPtr(&llvm_orc_registerEHFrameSectionWrapper); + this->BootstrapSymbols[rt::DeregisterEHFrameSectionWrapperName] = + ExecutorAddr::fromPtr(&llvm_orc_deregisterEHFrameSectionWrapper); + DylibLookup.initializeDynamicLoader([](llvm::StringRef) { /*ignore*/ + return false; + }); +} + +Expected> +AutoLoadEPC::Create(std::shared_ptr SSP, + std::unique_ptr D, + std::unique_ptr MemMgr) { + + if (!SSP) + SSP = std::make_shared(); + + if (!D) { +#if LLVM_ENABLE_THREADS + D = std::make_unique(); +#else + D = std::make_unique(); +#endif + } + + auto PageSize = sys::Process::getPageSize(); + if (!PageSize) + return PageSize.takeError(); + + Triple TT(sys::getProcessTriple()); + + return std::make_unique(std::move(SSP), std::move(D), + std::move(TT), *PageSize, + std::move(MemMgr)); +} + +Expected +AutoLoadEPC::loadDylib(const char *DylibPath) { + return loadDylib(DylibPath, false); +} + +Expected +AutoLoadEPC::loadDylib(const char *DylibPath, bool useDLOpen) { + std::string ErrMsg; + DylibHandle DylibH; + if (useDLOpen) { + DylibH = DLOpen(DylibPath, &ErrMsg); + if (!DylibH) + return make_error(std::move(ErrMsg), + inconvertibleErrorCode()); + } else { + sys::DynamicLibrary Dylib = + sys::DynamicLibrary::getPermanentLibrary(DylibPath, &ErrMsg); + if (!Dylib.isValid()) + return make_error(std::move(ErrMsg), + inconvertibleErrorCode()); + DylibH = Dylib.getOSSpecificHandle(); + } + return ExecutorAddr::fromPtr(DylibH); +} + +AutoLoadEPC::LoadLibResult +AutoLoadEPC::loadDylib(StringRef DylibPath, bool Permanent, bool resolved) { + + std::string canonicalLoadedLib; + if (resolved) { + canonicalLoadedLib = DylibPath.str(); + } else { + canonicalLoadedLib = DylibLookup.lookupLibrary(DylibPath); + if (canonicalLoadedLib.empty()) + return kLoadLibNotFound; + } + + if (DylibLookup.isLibraryLoaded(DylibPath)) + return kLoadLibAlreadyLoaded; + + DylibHandle dyLibHandle; + + // Helper function for handling library loading errors + auto handleLibraryLoadingError = + [&](const std::string &ErrMsg) -> AutoLoadEPC::LoadLibResult { + if (Callbacks && Callbacks->LibraryLoadingFailed(ErrMsg, DylibPath.str(), + Permanent, resolved)) { + return kLoadLibSuccess; + } + LLVM_DEBUG( + { dbgs() << "orc::AutoLoadEPC::loadLibrary(): " << ErrMsg << '\n'; }); + return kLoadLibLoadError; + }; + + std::string ErrMsg; + // if (Permanent) { + // auto Dylib = sys::DynamicLibrary::getPermanentLibrary( + // DylibPath.str().c_str(), &ErrMsg); + // if (!Dylib.isValid()) { + // return handleLibraryLoadingError(ErrMsg); + // } + // dyLibHandle = Dylib.getOSSpecificHandle(); + // } else { + dyLibHandle = DLOpen(canonicalLoadedLib, &ErrMsg); + if (!dyLibHandle) { + return handleLibraryLoadingError(ErrMsg); + } + // } + + // Notify callbacks if the library was loaded successfully + if (Callbacks) { + Callbacks->LibraryLoaded(dyLibHandle, canonicalLoadedLib); + } + + // Insert the loaded library into the dynamic libraries map + auto insertionResult = Dylibs.insert( + std::pair(dyLibHandle, canonicalLoadedLib)); + if (!insertionResult.second) { + return kLoadLibAlreadyLoaded; + } + + // Add the library to the lookup for future references + DylibLookup.addLoadedLib(canonicalLoadedLib); + + return kLoadLibSuccess; +} + +void AutoLoadEPC::unloadDylib(StringRef DylibPath) { + std::string canonicalLoadedLib = DylibLookup.lookupLibrary(DylibPath); + if (!DylibLookup.isLibraryLoaded(canonicalLoadedLib)) + return; + + DylibHandle dyLibHandle = nullptr; + for (DynamicLibs::const_iterator I = Dylibs.begin(), E = Dylibs.end(); I != E; + ++I) { + if (I->second == canonicalLoadedLib) { + dyLibHandle = I->first; + break; + } + } + + // TODO: !permanent case + + // std::string errMsg; + // DLClose(dyLibHandle, &errMsg); + // sys::DynamicLibrary Dylib(dyLibHandle); + // sys::DynamicLibrary::closeLibrary(Dylib); + + std::string errMsg; + DLClose(dyLibHandle, &errMsg); + if (Callbacks) + Callbacks->LibraryUnloaded(dyLibHandle, canonicalLoadedLib); + + Dylibs.erase(dyLibHandle); + DylibLookup.eraseLib(canonicalLoadedLib); + // LoadedLibraries.erase(canonicalLoadedLib); + return; +} + +Expected> +AutoLoadEPC::lookupSymbols(ArrayRef Request) { + std::vector R; + + for (auto &Elem : Request) { + sys::DynamicLibrary Dylib(Elem.Handle.toPtr()); + R.push_back(std::vector()); + for (auto &KV : Elem.Symbols) { + auto &Sym = KV.first; + std::string Tmp((*Sym).data() + !!GlobalManglingPrefix, + (*Sym).size() - !!GlobalManglingPrefix); + void *Addr = Dylib.getAddressOfSymbol(Tmp.c_str()); + if (!Addr && KV.second == SymbolLookupFlags::RequiredSymbol) { + // FIXME: Collect all failing symbols before erroring out. + SymbolNameVector MissingSymbols; + MissingSymbols.push_back(Sym); + return make_error(SSP, std::move(MissingSymbols)); + } + // FIXME: determine accurate JITSymbolFlags. + R.back().push_back( + {ExecutorAddr::fromPtr(Addr), JITSymbolFlags::Exported}); + } + } + + return R; +} + +void AutoLoadEPC::resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn Complete) { + std::vector R; + for (auto &Symbols : Request) { + R.emplace_back(); + BloomFilter Filter; + tpctypes::LookupResult &Result = R.back().SymbolDef; + for (auto &KV : Symbols) { + auto &Sym = KV.first; + std::string Tmp((*Sym).data() + !!GlobalManglingPrefix, + (*Sym).size() - !!GlobalManglingPrefix); + void *Addr = sys::DynamicLibrary::SearchForAddressOfSymbol(Tmp.c_str()); + + if (Addr) { + Result.push_back( + {ExecutorAddr::fromPtr(Addr), JITSymbolFlags::Exported}); + continue; + } + + auto lib = DylibLookup.searchLibrariesForSymbol(*Sym); + auto canonicalLoadedLib = DylibLookup.lookupLibrary(lib); + if (canonicalLoadedLib.empty()) { + if (!Filter.IsInitialized()) + DylibLookup.BuildGlobalBloomFilter(Filter); + Result.push_back(ExecutorSymbolDef()); + } else { + auto H = loadDylib(canonicalLoadedLib.c_str(), true); + if (!H) + return Complete(H.takeError()); + + DylibLookup.addLoadedLib(canonicalLoadedLib); + // LoadedLibraries.insert(canonicalLoadedLib); + sys::DynamicLibrary Dylib(H->toPtr()); + void *Addr = Dylib.getAddressOfSymbol(Tmp.c_str()); + if (!Addr && KV.second == SymbolLookupFlags::RequiredSymbol) { + // FIXME: Collect all failing symbols before erroring out. + SymbolNameVector MissingSymbols; + MissingSymbols.push_back(Sym); + return Complete( + make_error(SSP, std::move(MissingSymbols))); + } + Result.push_back( + {ExecutorAddr::fromPtr(Addr), JITSymbolFlags::Exported}); + } + } + if (Filter.IsInitialized()) + R.back().Filter.emplace(std::move(Filter)); + } + Complete(std::move(R)); +} + +Expected AutoLoadEPC::runAsMain(ExecutorAddr MainFnAddr, + ArrayRef Args) { + using MainTy = int (*)(int, char *[]); + return orc::runAsMain(MainFnAddr.toPtr(), Args); +} + +Expected AutoLoadEPC::runAsVoidFunction(ExecutorAddr VoidFnAddr) { + using VoidTy = int (*)(); + return orc::runAsVoidFunction(VoidFnAddr.toPtr()); +} + +Expected AutoLoadEPC::runAsIntFunction(ExecutorAddr IntFnAddr, + int Arg) { + using IntTy = int (*)(int); + return orc::runAsIntFunction(IntFnAddr.toPtr(), Arg); +} + +void AutoLoadEPC::callWrapperAsync(ExecutorAddr WrapperFnAddr, + IncomingWFRHandler SendResult, + ArrayRef ArgBuffer) { + using WrapperFnTy = + shared::CWrapperFunctionResult (*)(const char *Data, size_t Size); + auto *WrapperFn = WrapperFnAddr.toPtr(); + SendResult(WrapperFn(ArgBuffer.data(), ArgBuffer.size())); +} + +Error AutoLoadEPC::disconnect() { + D->shutdown(); + return Error::success(); +} + +shared::CWrapperFunctionResult +AutoLoadEPC::jitDispatchViaWrapperFunctionManager(void *Ctx, + const void *FnTag, + const char *Data, + size_t Size) { + + LLVM_DEBUG({ + dbgs() << "jit-dispatch call with tag " << FnTag << " and " << Size + << " byte payload.\n"; + }); + + std::promise ResultP; + auto ResultF = ResultP.get_future(); + static_cast(Ctx) + ->getExecutionSession() + .runJITDispatchHandler( + [ResultP = std::move(ResultP)]( + shared::WrapperFunctionResult Result) mutable { + ResultP.set_value(std::move(Result)); + }, + ExecutorAddr::fromPtr(FnTag), {Data, Size}); + + return ResultF.get().release(); +} + +} // end namespace orc +} // end namespace llvm \ No newline at end of file diff --git a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt index fdb628ac84d43..97262922b1bde 100644 --- a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt @@ -7,6 +7,7 @@ if( CMAKE_HOST_UNIX AND HAVE_LIBRT ) endif() add_llvm_component_library(LLVMOrcJIT + AutoLoadEPC.cpp COFFVCRuntimeSupport.cpp COFFPlatform.cpp CompileOnDemandLayer.cpp diff --git a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp index 460f4e1c448e6..bb968bc073142 100644 --- a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp @@ -7,6 +7,12 @@ //===----------------------------------------------------------------------===// #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" +#include "llvm/ExecutionEngine/Orc/DebugUtils.h" +#include "llvm/Support/Error.h" + +#include "llvm/ExecutionEngine/Orc/AutoLoadEPC.h" + +#define DEBUG_TYPE "orc" namespace llvm { namespace orc { @@ -67,5 +73,101 @@ Error EPCDynamicLibrarySearchGenerator::tryToGenerate( return JD.define(absoluteSymbols(std::move(NewSymbols))); } +Error AutoLoadDynamicLibrarySearchGenerator::tryToGenerate( + LookupState &LS, LookupKind K, JITDylib &JD, + JITDylibLookupFlags JDLookupFlags, const SymbolLookupSet &Symbols) { + + if (Symbols.empty()) + return Error::success(); + + if (!IsAutoLoadingEnabled()) + 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 { + if (!Result) { + LLVM_DEBUG({ + dbgs() << "AutoLoadDynamicLibrarySearchGenerator resolve failed due to " + "error"; + }); + 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() && + "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/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/EPCGenericDylibManager.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/EPCGenericDylibManager.cpp index da185c80c6c7d..98c05e335b2bc 100644 --- a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/EPCGenericDylibManager.cpp +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/EPCGenericDylibManager.cpp @@ -105,5 +105,36 @@ EPCGenericDylibManager::lookup(tpctypes::DylibHandle H, return Result; } +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/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp index f0c551cd77804..424dca6765e4a 100644 --- a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp @@ -116,6 +116,11 @@ SelfExecutorProcessControl::lookupSymbols(ArrayRef Request) { return R; } +void SelfExecutorProcessControl::resolveSymbolsAsync( + ArrayRef Request, ResolveSymbolsCompleteFn Complete) { + llvm_unreachable("Unsupported"); +} + Expected SelfExecutorProcessControl::runAsMain(ExecutorAddr MainFnAddr, ArrayRef Args) { diff --git a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDyLoader.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDyLoader.cpp new file mode 100644 index 0000000000000..bc6c071bac449 --- /dev/null +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDyLoader.cpp @@ -0,0 +1,1372 @@ +//===----------------- 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/Shared/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 + +#if defined(__FreeBSD__) +#include +#include +#include +#include + +// libprocstat pulls in sys/elf.h which seems to clash with +// llvm/BinaryFormat/ELF.h similar collision happens with ZFS. Defining ZFS +// disables this include. +#ifndef ZFS +#define ZFS +#define defined_ZFS_for_libprocstat +#endif +#include +#ifdef defined_ZFS_for_libprocstat +#undef ZFS +#undef defined_ZFS_for_libprocstat +#endif + +#include +#endif + +#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 +#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). +static 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 &AutoLoadDylibLookupMgr; + + /// 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) + : AutoLoadDylibLookupMgr(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 = AutoLoadDylibLookupMgr.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) { + // 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)); + } 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 = AutoLoadDylibLookupMgr.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); + } + } + + // 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"); +// If BloomFilter is empty then build it. + // Count Symbols and generate BloomFilter + uint32_t SymbolsCount = 0; + 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; + + // 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; + } + + 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; + } + + if (SymNameErr.get().empty()) + continue; + + ++SymbolsCount; + symbols.push_back(SymNameErr.get()); + } + + } 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) { + 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; + } + } + 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 (AutoLoadDylibLookupMgr.isLibraryLoaded(FileName)) + return true; + + 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 + << " message : " << Message << "\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 (!AutoLoadDylibLookupMgr.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) + delete Dyld; + + 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/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDylibLookUp.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDylibLookUp.cpp new file mode 100644 index 0000000000000..b832667ac531e --- /dev/null +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDylibLookUp.cpp @@ -0,0 +1,581 @@ +//===---------------- 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/Shared/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); +} + +void AutoLoadDynamicLibraryLookup::eraseLib(StringRef lib) { + LoadedLibraries.erase(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/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.cpp new file mode 100644 index 0000000000000..0b834821d6056 --- /dev/null +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/AutoLoadDylibUtils.cpp @@ -0,0 +1,245 @@ +//===---------------- 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" + +#if defined(LLVM_ON_UNIX) +#include +#endif + +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()); +} + +#if defined(LLVM_ON_UNIX) +static void DLErr(std::string *Err) { + if (!Err) + return; + if (const char *DyLibError = ::dlerror()) + *Err = DyLibError; +} + +void *DLOpen(const std::string &Path, std::string *Err /* = nullptr */) { + void *Lib = ::dlopen(Path.c_str(), RTLD_LAZY | RTLD_GLOBAL); + DLErr(Err); + return Lib; +} + +void *DLSym(const std::string &Name, std::string *Err /* = nullptr*/) { + if (void *Self = ::dlopen(nullptr, RTLD_GLOBAL)) { + // get dlopen error if there is one + DLErr(Err); + void *Sym = ::dlsym(Self, Name.c_str()); + // overwrite error if dlsym caused one + DLErr(Err); + // only get dlclose error if dlopen & dlsym haven't emited one + DLClose(Self, Err && Err->empty() ? Err : nullptr); + return Sym; + } + DLErr(Err); + return nullptr; +} + +void DLClose(void *Lib, std::string *Err /* = nullptr*/) { + ::dlclose(Lib); + DLErr(Err); +} +#elif defined(_WIN32) + +void *DLOpen(const std::string &Path, std::string *Err /* = nullptr */) { + auto lib = llvm::sys::DynamicLibrary::getLibrary(Path.c_str(), Err); + return lib.getOSSpecificHandle(); +} + +void DLClose(void *Lib, std::string *Err /* = nullptr*/) { + auto dl = llvm::sys::DynamicLibrary(Lib); + llvm::sys::DynamicLibrary::closeLibrary(dl); + if (Err) { + *Err = std::string(); + } +} +#endif + +// } // namespace platform + +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/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt index 985ac6f1409e6..70d194634f050 100644 --- a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt @@ -1,5 +1,8 @@ add_llvm_component_library(LLVMOrcShared AllocationActions.cpp + AutoLoadDylibLookUp.cpp + AutoLoadDylibUtils.cpp + AutoLoadDyLoader.cpp ObjectFormats.cpp OrcError.cpp OrcRTBridge.cpp @@ -14,5 +17,6 @@ add_llvm_component_library(LLVMOrcShared ${LLVM_PTHREAD_LIB} LINK_COMPONENTS + Object Support ) diff --git a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp index ae39b1d1bfaa9..bb2e31346d25b 100644 --- a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp +++ b/interpreter/llvm-project/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/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp index 3d3ca891d8810..6bf638af49acf 100644 --- a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp @@ -45,6 +45,40 @@ SimpleRemoteEPC::lookupSymbols(ArrayRef Request) { return std::move(Result); } +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::resolveSymbolsAsync(ArrayRef Request, + ResolveSymbolsCompleteFn Complete) { + resolveSymbolsAsyncHelper(*DylibMgr, Request, {}, std::move(Complete)); +} + Expected SimpleRemoteEPC::runAsMain(ExecutorAddr MainFnAddr, ArrayRef Args) { int64_t Result = 0; diff --git a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp index b7e256a826ca4..96acbf081ce21 100644 --- a/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.cpp @@ -78,6 +78,76 @@ 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); + + sys::DynamicLibrary Dl(H.get().toPtr()); + void *Addr = Dl.getAddressOfSymbol(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; @@ -97,6 +167,8 @@ void SimpleExecutorDylibManager::addBootstrapSymbols( ExecutorAddr::fromPtr(&openWrapper); M[rt::SimpleExecutorDylibManagerLookupWrapperName] = ExecutorAddr::fromPtr(&lookupWrapper); + M[rt::SimpleExecutorDylibManagerResolveWrapperName] = + ExecutorAddr::fromPtr(&resolveWrapper); } llvm::orc::shared::CWrapperFunctionResult @@ -119,6 +191,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