| //===- elfnix_platform.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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file contains code required to load the rest of the ELF-on-*IX runtime. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "elfnix_platform.h" | |
| #include "common.h" | |
| #include "compiler.h" | |
| #include "error.h" | |
| #include "jit_dispatch.h" | |
| #include "record_section_tracker.h" | |
| #include "wrapper_function_utils.h" | |
| #include <algorithm> | |
| #include <map> | |
| #include <mutex> | |
| #include <sstream> | |
| #include <string_view> | |
| #include <unordered_map> | |
| #include <vector> | |
| using namespace orc_rt; | |
| using namespace orc_rt::elfnix; | |
| // Declare function tags for functions in the JIT process. | |
| ORC_RT_JIT_DISPATCH_TAG(__orc_rt_elfnix_push_initializers_tag) | |
| ORC_RT_JIT_DISPATCH_TAG(__orc_rt_elfnix_symbol_lookup_tag) | |
| // eh-frame registration functions, made available via aliases | |
| // installed by the Platform | |
| extern "C" void __register_frame(const void *); | |
| extern "C" void __deregister_frame(const void *); | |
| extern "C" void | |
| __unw_add_dynamic_eh_frame_section(const void *) ORC_RT_WEAK_IMPORT; | |
| extern "C" void | |
| __unw_remove_dynamic_eh_frame_section(const void *) ORC_RT_WEAK_IMPORT; | |
| namespace { | |
| struct TLSInfoEntry { | |
| unsigned long Key = 0; | |
| unsigned long DataAddress = 0; | |
| }; | |
| struct TLSDescriptor { | |
| void (*Resolver)(void *); | |
| TLSInfoEntry *InfoEntry; | |
| }; | |
| class ELFNixPlatformRuntimeState { | |
| private: | |
| struct AtExitEntry { | |
| void (*Func)(void *); | |
| void *Arg; | |
| }; | |
| using AtExitsVector = std::vector<AtExitEntry>; | |
| struct PerJITDylibState { | |
| std::string Name; | |
| void *Header = nullptr; | |
| size_t RefCount = 0; | |
| size_t LinkedAgainstRefCount = 0; | |
| bool AllowReinitialization = false; | |
| AtExitsVector AtExits; | |
| std::vector<PerJITDylibState *> Deps; | |
| RecordSectionsTracker<void (*)()> RecordedInits; | |
| RecordSectionsTracker<void (*)()> RecordedFinis; | |
| bool referenced() const { | |
| return LinkedAgainstRefCount != 0 || RefCount != 0; | |
| } | |
| }; | |
| public: | |
| static void initialize(void *DSOHandle); | |
| static ELFNixPlatformRuntimeState &get(); | |
| static void destroy(); | |
| ELFNixPlatformRuntimeState(void *DSOHandle); | |
| // Delete copy and move constructors. | |
| ELFNixPlatformRuntimeState(const ELFNixPlatformRuntimeState &) = delete; | |
| ELFNixPlatformRuntimeState & | |
| operator=(const ELFNixPlatformRuntimeState &) = delete; | |
| ELFNixPlatformRuntimeState(ELFNixPlatformRuntimeState &&) = delete; | |
| ELFNixPlatformRuntimeState &operator=(ELFNixPlatformRuntimeState &&) = delete; | |
| Error registerObjectSections(ELFNixPerObjectSectionsToRegister POSR); | |
| Error registerJITDylib(std::string &Name, void *Handle); | |
| Error deregisterJITDylib(void *Handle); | |
| Error registerInits(ExecutorAddr HeaderAddr, | |
| std::vector<ExecutorAddrRange> Inits); | |
| Error deregisterInits(ExecutorAddr HeaderAddr, | |
| std::vector<ExecutorAddrRange> Inits); | |
| Error registerFinis(ExecutorAddr HeaderAddr, | |
| std::vector<ExecutorAddrRange> Finis); | |
| Error deregisterFinis(ExecutorAddr HeaderAddr, | |
| std::vector<ExecutorAddrRange> Finis); | |
| Error deregisterObjectSections(ELFNixPerObjectSectionsToRegister POSR); | |
| const char *dlerror(); | |
| void *dlopen(std::string_view Name, int Mode); | |
| int dlupdate(void *DSOHandle); | |
| int dlclose(void *DSOHandle); | |
| void *dlsym(void *DSOHandle, std::string_view Symbol); | |
| int registerAtExit(void (*F)(void *), void *Arg, void *DSOHandle); | |
| void runAtExits(void *DSOHandle); | |
| void runAtExits(std::unique_lock<std::recursive_mutex> &JDStateLock, | |
| PerJITDylibState &JDS); | |
| /// Returns the base address of the section containing ThreadData. | |
| Expected<std::pair<const char *, size_t>> | |
| getThreadDataSectionFor(const char *ThreadData); | |
| void *getPlatformJDDSOHandle() { return PlatformJDDSOHandle; } | |
| private: | |
| PerJITDylibState *getJITDylibStateByHeaderAddr(void *DSOHandle); | |
| PerJITDylibState *getJITDylibStateByName(std::string_view Path); | |
| Error registerThreadDataSection(span<const char> ThreadDataSection); | |
| Expected<ExecutorAddr> lookupSymbolInJITDylib(void *DSOHandle, | |
| std::string_view Symbol); | |
| Error runInits(std::unique_lock<std::recursive_mutex> &JDStatesLock, | |
| PerJITDylibState &JDS); | |
| Error runFinis(std::unique_lock<std::recursive_mutex> &JDStatesLock, | |
| PerJITDylibState &JDS); | |
| Expected<void *> dlopenImpl(std::string_view Path, int Mode); | |
| Error dlopenFull(std::unique_lock<std::recursive_mutex> &JDStatesLock, | |
| PerJITDylibState &JDS); | |
| Error dlopenInitialize(std::unique_lock<std::recursive_mutex> &JDStatesLock, | |
| PerJITDylibState &JDS, | |
| ELFNixJITDylibDepInfoMap &DepInfo); | |
| Error dlupdateImpl(void *DSOHandle); | |
| Error dlupdateFull(std::unique_lock<std::recursive_mutex> &JDStatesLock, | |
| PerJITDylibState &JDS); | |
| Error dlcloseImpl(void *DSOHandle); | |
| Error dlcloseInitialize(std::unique_lock<std::recursive_mutex> &JDStatesLock, | |
| PerJITDylibState &JDS); | |
| static ELFNixPlatformRuntimeState *MOPS; | |
| void *PlatformJDDSOHandle; | |
| // Frame registration functions: | |
| void (*registerEHFrameSection)(const void *) = nullptr; | |
| void (*deregisterEHFrameSection)(const void *) = nullptr; | |
| // FIXME: Move to thread-state. | |
| std::string DLFcnError; | |
| std::recursive_mutex JDStatesMutex; | |
| std::unordered_map<void *, PerJITDylibState> JDStates; | |
| std::unordered_map<std::string, void *> JDNameToHeader; | |
| std::mutex ThreadDataSectionsMutex; | |
| std::map<const char *, size_t> ThreadDataSections; | |
| }; | |
| ELFNixPlatformRuntimeState *ELFNixPlatformRuntimeState::MOPS = nullptr; | |
| void ELFNixPlatformRuntimeState::initialize(void *DSOHandle) { | |
| assert(!MOPS && "ELFNixPlatformRuntimeState should be null"); | |
| MOPS = new ELFNixPlatformRuntimeState(DSOHandle); | |
| } | |
| ELFNixPlatformRuntimeState &ELFNixPlatformRuntimeState::get() { | |
| assert(MOPS && "ELFNixPlatformRuntimeState not initialized"); | |
| return *MOPS; | |
| } | |
| void ELFNixPlatformRuntimeState::destroy() { | |
| assert(MOPS && "ELFNixPlatformRuntimeState not initialized"); | |
| delete MOPS; | |
| } | |
| ELFNixPlatformRuntimeState::ELFNixPlatformRuntimeState(void *DSOHandle) | |
| : PlatformJDDSOHandle(DSOHandle) { | |
| if (__unw_add_dynamic_eh_frame_section && | |
| __unw_remove_dynamic_eh_frame_section) { | |
| registerEHFrameSection = __unw_add_dynamic_eh_frame_section; | |
| deregisterEHFrameSection = __unw_remove_dynamic_eh_frame_section; | |
| } else { | |
| registerEHFrameSection = __register_frame; | |
| deregisterEHFrameSection = __deregister_frame; | |
| } | |
| } | |
| Error ELFNixPlatformRuntimeState::registerObjectSections( | |
| ELFNixPerObjectSectionsToRegister POSR) { | |
| if (POSR.EHFrameSection.Start) | |
| registerEHFrameSection(POSR.EHFrameSection.Start.toPtr<const char *>()); | |
| if (POSR.ThreadDataSection.Start) { | |
| if (auto Err = registerThreadDataSection( | |
| POSR.ThreadDataSection.toSpan<const char>())) | |
| return Err; | |
| } | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::deregisterObjectSections( | |
| ELFNixPerObjectSectionsToRegister POSR) { | |
| if (POSR.EHFrameSection.Start) | |
| deregisterEHFrameSection(POSR.EHFrameSection.Start.toPtr<const char *>()); | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::registerJITDylib(std::string &Name, | |
| void *Handle) { | |
| std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); | |
| if (JDStates.count(Handle)) { | |
| std::ostringstream ErrStream; | |
| ErrStream << "Duplicate JITDylib registration for header " << Handle | |
| << " (name = " << Name << ")"; | |
| return make_error<StringError>(ErrStream.str()); | |
| } | |
| if (JDNameToHeader.count(Name)) { | |
| std::ostringstream ErrStream; | |
| ErrStream << "Duplicate JITDylib registration for header " << Handle | |
| << " (header = " << Handle << ")"; | |
| return make_error<StringError>(ErrStream.str()); | |
| } | |
| auto &JD = JDStates[Handle]; | |
| JD.Header = Handle; | |
| JD.Name = std::move(Name); | |
| JDNameToHeader[JD.Name] = Handle; | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::deregisterJITDylib(void *Handle) { | |
| std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); | |
| auto I = JDStates.find(Handle); | |
| if (I == JDStates.end()) { | |
| std::ostringstream ErrStream; | |
| ErrStream << "Attempted to deregister unrecognized header " << Handle; | |
| return make_error<StringError>(ErrStream.str()); | |
| } | |
| auto J = JDNameToHeader.find( | |
| std::string(I->second.Name.data(), I->second.Name.size())); | |
| assert(J != JDNameToHeader.end() && | |
| "Missing JDNameToHeader entry for JITDylib"); | |
| JDNameToHeader.erase(J); | |
| JDStates.erase(I); | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::registerInits( | |
| ExecutorAddr HeaderAddr, std::vector<ExecutorAddrRange> Inits) { | |
| std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); | |
| PerJITDylibState *JDS = | |
| getJITDylibStateByHeaderAddr(HeaderAddr.toPtr<void *>()); | |
| if (!JDS) { | |
| std::ostringstream ErrStream; | |
| ErrStream << "Could not register object platform sections for " | |
| "unrecognized header " | |
| << HeaderAddr.toPtr<void *>(); | |
| return make_error<StringError>(ErrStream.str()); | |
| } | |
| for (auto &I : Inits) { | |
| JDS->RecordedInits.add(I.toSpan<void (*)()>()); | |
| } | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::deregisterInits( | |
| ExecutorAddr HeaderAddr, std::vector<ExecutorAddrRange> Inits) { | |
| std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); | |
| PerJITDylibState *JDS = | |
| getJITDylibStateByHeaderAddr(HeaderAddr.toPtr<void *>()); | |
| if (!JDS) { | |
| std::ostringstream ErrStream; | |
| ErrStream << "Could not register object platform sections for unrecognized " | |
| "header " | |
| << HeaderAddr.toPtr<void *>(); | |
| return make_error<StringError>(ErrStream.str()); | |
| } | |
| for (auto &I : Inits) { | |
| JDS->RecordedInits.removeIfPresent(I); | |
| } | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::registerFinis( | |
| ExecutorAddr HeaderAddr, std::vector<ExecutorAddrRange> Finis) { | |
| std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); | |
| PerJITDylibState *JDS = | |
| getJITDylibStateByHeaderAddr(HeaderAddr.toPtr<void *>()); | |
| if (!JDS) { | |
| std::ostringstream ErrStream; | |
| ErrStream << "Could not register fini sections for unrecognized header " | |
| << HeaderAddr.toPtr<void *>(); | |
| return make_error<StringError>(ErrStream.str()); | |
| } | |
| for (auto &F : Finis) { | |
| JDS->RecordedFinis.add(F.toSpan<void (*)()>()); | |
| } | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::deregisterFinis( | |
| ExecutorAddr HeaderAddr, std::vector<ExecutorAddrRange> Finis) { | |
| std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); | |
| PerJITDylibState *JDS = | |
| getJITDylibStateByHeaderAddr(HeaderAddr.toPtr<void *>()); | |
| if (!JDS) { | |
| std::ostringstream ErrStream; | |
| ErrStream << "Could not deregister fini sections for unrecognized header " | |
| << HeaderAddr.toPtr<void *>(); | |
| return make_error<StringError>(ErrStream.str()); | |
| } | |
| for (auto &F : Finis) { | |
| JDS->RecordedFinis.removeIfPresent(F); | |
| } | |
| return Error::success(); | |
| } | |
| const char *ELFNixPlatformRuntimeState::dlerror() { return DLFcnError.c_str(); } | |
| void *ELFNixPlatformRuntimeState::dlopen(std::string_view Path, int Mode) { | |
| if (auto H = dlopenImpl(Path, Mode)) | |
| return *H; | |
| else { | |
| // FIXME: Make dlerror thread safe. | |
| DLFcnError = toString(H.takeError()); | |
| return nullptr; | |
| } | |
| } | |
| int ELFNixPlatformRuntimeState::dlupdate(void *DSOHandle) { | |
| if (auto Err = dlupdateImpl(DSOHandle)) { | |
| // FIXME: Make dlerror thread safe. | |
| DLFcnError = toString(std::move(Err)); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| int ELFNixPlatformRuntimeState::dlclose(void *DSOHandle) { | |
| if (auto Err = dlcloseImpl(DSOHandle)) { | |
| DLFcnError = toString(std::move(Err)); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| void *ELFNixPlatformRuntimeState::dlsym(void *DSOHandle, | |
| std::string_view Symbol) { | |
| auto Addr = lookupSymbolInJITDylib(DSOHandle, Symbol); | |
| if (!Addr) { | |
| DLFcnError = toString(Addr.takeError()); | |
| return 0; | |
| } | |
| return Addr->toPtr<void *>(); | |
| } | |
| int ELFNixPlatformRuntimeState::registerAtExit(void (*F)(void *), void *Arg, | |
| void *DSOHandle) { | |
| // FIXME: Handle out-of-memory errors, returning -1 if OOM. | |
| std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex); | |
| auto *JDS = getJITDylibStateByHeaderAddr(DSOHandle); | |
| assert(JDS && "JITDylib state not initialized"); | |
| JDS->AtExits.push_back({F, Arg}); | |
| return 0; | |
| } | |
| void ELFNixPlatformRuntimeState::runAtExits(void *DSOHandle) { | |
| std::unique_lock<std::recursive_mutex> Lock(JDStatesMutex); | |
| PerJITDylibState *JDS = getJITDylibStateByHeaderAddr(DSOHandle); | |
| if (JDS) | |
| runAtExits(Lock, *JDS); | |
| } | |
| void ELFNixPlatformRuntimeState::runAtExits( | |
| std::unique_lock<std::recursive_mutex> &JDStateLock, | |
| PerJITDylibState &JDS) { | |
| AtExitsVector V = std::move(JDS.AtExits); | |
| while (!V.empty()) { | |
| auto &AE = V.back(); | |
| AE.Func(AE.Arg); | |
| V.pop_back(); | |
| } | |
| } | |
| Expected<std::pair<const char *, size_t>> | |
| ELFNixPlatformRuntimeState::getThreadDataSectionFor(const char *ThreadData) { | |
| std::lock_guard<std::mutex> Lock(ThreadDataSectionsMutex); | |
| auto I = ThreadDataSections.upper_bound(ThreadData); | |
| // Check that we have a valid entry conovering this address. | |
| if (I == ThreadDataSections.begin()) | |
| return make_error<StringError>("No thread local data section for key"); | |
| I = std::prev(I); | |
| if (ThreadData >= I->first + I->second) | |
| return make_error<StringError>("No thread local data section for key"); | |
| return *I; | |
| } | |
| ELFNixPlatformRuntimeState::PerJITDylibState * | |
| ELFNixPlatformRuntimeState::getJITDylibStateByHeaderAddr(void *DSOHandle) { | |
| auto I = JDStates.find(DSOHandle); | |
| if (I == JDStates.end()) | |
| return nullptr; | |
| return &I->second; | |
| } | |
| ELFNixPlatformRuntimeState::PerJITDylibState * | |
| ELFNixPlatformRuntimeState::getJITDylibStateByName(std::string_view Name) { | |
| // FIXME: Avoid creating string copy here. | |
| auto I = JDNameToHeader.find(std::string(Name.data(), Name.size())); | |
| if (I == JDNameToHeader.end()) | |
| return nullptr; | |
| void *H = I->second; | |
| auto J = JDStates.find(H); | |
| assert(J != JDStates.end() && | |
| "JITDylib has name map entry but no header map entry"); | |
| return &J->second; | |
| } | |
| Error ELFNixPlatformRuntimeState::registerThreadDataSection( | |
| span<const char> ThreadDataSection) { | |
| std::lock_guard<std::mutex> Lock(ThreadDataSectionsMutex); | |
| auto I = ThreadDataSections.upper_bound(ThreadDataSection.data()); | |
| if (I != ThreadDataSections.begin()) { | |
| auto J = std::prev(I); | |
| if (J->first + J->second > ThreadDataSection.data()) | |
| return make_error<StringError>("Overlapping .tdata sections"); | |
| } | |
| ThreadDataSections.insert( | |
| I, std::make_pair(ThreadDataSection.data(), ThreadDataSection.size())); | |
| return Error::success(); | |
| } | |
| Expected<ExecutorAddr> | |
| ELFNixPlatformRuntimeState::lookupSymbolInJITDylib(void *DSOHandle, | |
| std::string_view Sym) { | |
| Expected<ExecutorAddr> Result((ExecutorAddr())); | |
| if (auto Err = WrapperFunction<SPSExpected<SPSExecutorAddr>( | |
| SPSExecutorAddr, | |
| SPSString)>::call(JITDispatch(&__orc_rt_elfnix_symbol_lookup_tag), | |
| Result, ExecutorAddr::fromPtr(DSOHandle), Sym)) | |
| return std::move(Err); | |
| return Result; | |
| } | |
| Error ELFNixPlatformRuntimeState::runInits( | |
| std::unique_lock<std::recursive_mutex> &JDStatesLock, | |
| PerJITDylibState &JDS) { | |
| std::vector<span<void (*)()>> InitSections; | |
| InitSections.reserve(JDS.RecordedInits.numNewSections()); | |
| JDS.RecordedInits.processNewSections( | |
| [&](span<void (*)()> Inits) { InitSections.push_back(Inits); }); | |
| JDStatesLock.unlock(); | |
| for (auto Sec : InitSections) | |
| for (auto *Init : Sec) | |
| Init(); | |
| JDStatesLock.lock(); | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::runFinis( | |
| std::unique_lock<std::recursive_mutex> &JDStatesLock, | |
| PerJITDylibState &JDS) { | |
| std::vector<span<void (*)()>> FiniSections; | |
| // Collect all fini sections (reset to move all to "new" for processing) | |
| JDS.RecordedFinis.reset(); | |
| FiniSections.reserve(JDS.RecordedFinis.numNewSections()); | |
| JDS.RecordedFinis.processNewSections( | |
| [&](span<void (*)()> Finis) { FiniSections.push_back(Finis); }); | |
| JDStatesLock.unlock(); | |
| // Run in forward order - sections are already sorted correctly by the JIT: | |
| // .dtors first (in order), then .fini_array (in descending priority order) | |
| for (auto Sec : FiniSections) | |
| for (auto *Fini : Sec) | |
| Fini(); | |
| JDStatesLock.lock(); | |
| return Error::success(); | |
| } | |
| Expected<void *> ELFNixPlatformRuntimeState::dlopenImpl(std::string_view Path, | |
| int Mode) { | |
| std::unique_lock<std::recursive_mutex> Lock(JDStatesMutex); | |
| PerJITDylibState *JDS = getJITDylibStateByName(Path); | |
| if (!JDS) | |
| return make_error<StringError>("No registered JTIDylib for path " + | |
| std::string(Path.data(), Path.size())); | |
| if (auto Err = dlopenFull(Lock, *JDS)) | |
| return std::move(Err); | |
| ++JDS->RefCount; | |
| return JDS->Header; | |
| } | |
| Error ELFNixPlatformRuntimeState::dlopenFull( | |
| std::unique_lock<std::recursive_mutex> &JDStateLock, | |
| PerJITDylibState &JDS) { | |
| Expected<ELFNixJITDylibDepInfoMap> DepInfo((ELFNixJITDylibDepInfoMap())); | |
| JDStateLock.unlock(); | |
| if (auto Err = WrapperFunction<SPSExpected<SPSELFNixJITDylibDepInfoMap>( | |
| SPSExecutorAddr)>:: | |
| call(JITDispatch(&__orc_rt_elfnix_push_initializers_tag), DepInfo, | |
| ExecutorAddr::fromPtr(JDS.Header))) | |
| return Err; | |
| JDStateLock.lock(); | |
| if (!DepInfo) | |
| return DepInfo.takeError(); | |
| if (auto Err = dlopenInitialize(JDStateLock, JDS, *DepInfo)) | |
| return Err; | |
| if (!DepInfo->empty()) { | |
| std::ostringstream ErrStream; | |
| ErrStream << "Encountered unrecognized dep-info key headers " | |
| "while processing dlopen of " | |
| << JDS.Name; | |
| return make_error<StringError>(ErrStream.str()); | |
| } | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::dlopenInitialize( | |
| std::unique_lock<std::recursive_mutex> &JDStatesLock, PerJITDylibState &JDS, | |
| ELFNixJITDylibDepInfoMap &DepInfo) { | |
| auto I = DepInfo.find(ExecutorAddr::fromPtr(JDS.Header)); | |
| if (I == DepInfo.end()) | |
| return Error::success(); | |
| auto Deps = std::move(I->second); | |
| DepInfo.erase(I); | |
| std::vector<PerJITDylibState *> OldDeps; | |
| std::swap(JDS.Deps, OldDeps); | |
| JDS.Deps.reserve(Deps.size()); | |
| for (auto H : Deps) { | |
| PerJITDylibState *DepJDS = getJITDylibStateByHeaderAddr(H.toPtr<void *>()); | |
| if (!DepJDS) { | |
| std::ostringstream ErrStream; | |
| ErrStream << "Encountered unrecognized dep header " << H.toPtr<void *>() | |
| << " while initializing " << JDS.Name; | |
| return make_error<StringError>(ErrStream.str()); | |
| } | |
| ++DepJDS->LinkedAgainstRefCount; | |
| if (auto Err = dlopenInitialize(JDStatesLock, *DepJDS, DepInfo)) | |
| return Err; | |
| } | |
| if (auto Err = runInits(JDStatesLock, JDS)) | |
| return Err; | |
| for (auto *DepJDS : OldDeps) { | |
| --DepJDS->LinkedAgainstRefCount; | |
| if (!DepJDS->referenced()) | |
| if (auto Err = dlcloseInitialize(JDStatesLock, *DepJDS)) | |
| return Err; | |
| } | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::dlupdateImpl(void *DSOHandle) { | |
| std::unique_lock<std::recursive_mutex> Lock(JDStatesMutex); | |
| // Try to find JITDylib state by name. | |
| auto *JDS = getJITDylibStateByHeaderAddr(DSOHandle); | |
| if (!JDS) { | |
| std::ostringstream ErrStream; | |
| ErrStream << "No registered JITDylib for " << DSOHandle; | |
| return make_error<StringError>(ErrStream.str()); | |
| } | |
| if (!JDS->referenced()) | |
| return make_error<StringError>("dlupdate failed, JITDylib must be open."); | |
| if (auto Err = dlupdateFull(Lock, *JDS)) | |
| return Err; | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::dlupdateFull( | |
| std::unique_lock<std::recursive_mutex> &JDStatesLock, | |
| PerJITDylibState &JDS) { | |
| // Call back to the JIT to push the initializers. | |
| Expected<ELFNixJITDylibDepInfoMap> DepInfo((ELFNixJITDylibDepInfoMap())); | |
| // Unlock so that we can accept the initializer update. | |
| JDStatesLock.unlock(); | |
| if (auto Err = WrapperFunction<SPSExpected<SPSELFNixJITDylibDepInfoMap>( | |
| SPSExecutorAddr)>:: | |
| call(JITDispatch(&__orc_rt_elfnix_push_initializers_tag), DepInfo, | |
| ExecutorAddr::fromPtr(JDS.Header))) | |
| return Err; | |
| JDStatesLock.lock(); | |
| if (!DepInfo) | |
| return DepInfo.takeError(); | |
| if (auto Err = runInits(JDStatesLock, JDS)) | |
| return Err; | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::dlcloseImpl(void *DSOHandle) { | |
| std::unique_lock<std::recursive_mutex> Lock(JDStatesMutex); | |
| PerJITDylibState *JDS = getJITDylibStateByHeaderAddr(DSOHandle); | |
| if (!JDS) { | |
| std::ostringstream ErrStream; | |
| ErrStream << "No registered JITDylib for " << DSOHandle; | |
| return make_error<StringError>(ErrStream.str()); | |
| } | |
| --JDS->RefCount; | |
| if (!JDS->referenced()) | |
| return dlcloseInitialize(Lock, *JDS); | |
| return Error::success(); | |
| } | |
| Error ELFNixPlatformRuntimeState::dlcloseInitialize( | |
| std::unique_lock<std::recursive_mutex> &JDStatesLock, | |
| PerJITDylibState &JDS) { | |
| // Run fini sections BEFORE atexits (mirrors static dtor order) | |
| if (auto Err = runFinis(JDStatesLock, JDS)) | |
| return Err; | |
| runAtExits(JDStatesLock, JDS); | |
| JDS.RecordedInits.reset(); | |
| JDS.RecordedFinis.reset(); | |
| for (auto *DepJDS : JDS.Deps) | |
| if (!JDS.referenced()) | |
| if (auto Err = dlcloseInitialize(JDStatesLock, *DepJDS)) | |
| return Err; | |
| return Error::success(); | |
| } | |
| class ELFNixPlatformRuntimeTLVManager { | |
| public: | |
| void *getInstance(const char *ThreadData); | |
| private: | |
| std::unordered_map<const char *, char *> Instances; | |
| std::unordered_map<const char *, std::unique_ptr<char[]>> AllocatedSections; | |
| }; | |
| void *ELFNixPlatformRuntimeTLVManager::getInstance(const char *ThreadData) { | |
| auto I = Instances.find(ThreadData); | |
| if (I != Instances.end()) | |
| return I->second; | |
| auto TDS = | |
| ELFNixPlatformRuntimeState::get().getThreadDataSectionFor(ThreadData); | |
| if (!TDS) { | |
| __orc_rt_log_error(toString(TDS.takeError()).c_str()); | |
| return nullptr; | |
| } | |
| auto &Allocated = AllocatedSections[TDS->first]; | |
| if (!Allocated) { | |
| Allocated = std::make_unique<char[]>(TDS->second); | |
| memcpy(Allocated.get(), TDS->first, TDS->second); | |
| } | |
| size_t ThreadDataDelta = ThreadData - TDS->first; | |
| assert(ThreadDataDelta <= TDS->second && "ThreadData outside section bounds"); | |
| char *Instance = Allocated.get() + ThreadDataDelta; | |
| Instances[ThreadData] = Instance; | |
| return Instance; | |
| } | |
| void destroyELFNixTLVMgr(void *ELFNixTLVMgr) { | |
| delete static_cast<ELFNixPlatformRuntimeTLVManager *>(ELFNixTLVMgr); | |
| } | |
| } // end anonymous namespace | |
| //------------------------------------------------------------------------------ | |
| // JIT entry points | |
| //------------------------------------------------------------------------------ | |
| ORC_RT_INTERFACE orc_rt_WrapperFunctionResult | |
| __orc_rt_elfnix_platform_bootstrap(char *ArgData, size_t ArgSize) { | |
| return WrapperFunction<SPSError(SPSExecutorAddr)>::handle( | |
| ArgData, ArgSize, | |
| [](ExecutorAddr DSOHandle) { | |
| ELFNixPlatformRuntimeState::initialize( | |
| DSOHandle.toPtr<void *>()); | |
| return Error::success(); | |
| }) | |
| .release(); | |
| } | |
| ORC_RT_INTERFACE orc_rt_WrapperFunctionResult | |
| __orc_rt_elfnix_platform_shutdown(char *ArgData, size_t ArgSize) { | |
| return WrapperFunction<SPSError()>::handle( | |
| ArgData, ArgSize, | |
| []() { | |
| ELFNixPlatformRuntimeState::destroy(); | |
| return Error::success(); | |
| }) | |
| .release(); | |
| } | |
| ORC_RT_INTERFACE orc_rt_WrapperFunctionResult | |
| __orc_rt_elfnix_register_jitdylib(char *ArgData, size_t ArgSize) { | |
| return WrapperFunction<SPSError(SPSString, SPSExecutorAddr)>::handle( | |
| ArgData, ArgSize, | |
| [](std::string &JDName, ExecutorAddr HeaderAddr) { | |
| return ELFNixPlatformRuntimeState::get().registerJITDylib( | |
| JDName, HeaderAddr.toPtr<void *>()); | |
| }) | |
| .release(); | |
| } | |
| ORC_RT_INTERFACE orc_rt_WrapperFunctionResult | |
| __orc_rt_elfnix_deregister_jitdylib(char *ArgData, size_t ArgSize) { | |
| return WrapperFunction<SPSError(SPSExecutorAddr)>::handle( | |
| ArgData, ArgSize, | |
| [](ExecutorAddr HeaderAddr) { | |
| return ELFNixPlatformRuntimeState::get().deregisterJITDylib( | |
| HeaderAddr.toPtr<void *>()); | |
| }) | |
| .release(); | |
| } | |
| ORC_RT_INTERFACE orc_rt_WrapperFunctionResult | |
| __orc_rt_elfnix_register_init_sections(char *ArgData, size_t ArgSize) { | |
| return WrapperFunction<SPSError(SPSExecutorAddr, | |
| SPSSequence<SPSExecutorAddrRange>)>:: | |
| handle(ArgData, ArgSize, | |
| [](ExecutorAddr HeaderAddr, | |
| std::vector<ExecutorAddrRange> &Inits) { | |
| return ELFNixPlatformRuntimeState::get().registerInits( | |
| HeaderAddr, std::move(Inits)); | |
| }) | |
| .release(); | |
| } | |
| ORC_RT_INTERFACE orc_rt_WrapperFunctionResult | |
| __orc_rt_elfnix_deregister_init_sections(char *ArgData, size_t ArgSize) { | |
| return WrapperFunction<SPSError(SPSExecutorAddr, | |
| SPSSequence<SPSExecutorAddrRange>)>:: | |
| handle(ArgData, ArgSize, | |
| [](ExecutorAddr HeaderAddr, | |
| std::vector<ExecutorAddrRange> &Inits) { | |
| return ELFNixPlatformRuntimeState::get().deregisterInits( | |
| HeaderAddr, std::move(Inits)); | |
| }) | |
| .release(); | |
| } | |
| ORC_RT_INTERFACE orc_rt_WrapperFunctionResult | |
| __orc_rt_elfnix_register_fini_sections(char *ArgData, size_t ArgSize) { | |
| return WrapperFunction<SPSError(SPSExecutorAddr, | |
| SPSSequence<SPSExecutorAddrRange>)>:: | |
| handle(ArgData, ArgSize, | |
| [](ExecutorAddr HeaderAddr, | |
| std::vector<ExecutorAddrRange> &Finis) { | |
| return ELFNixPlatformRuntimeState::get().registerFinis( | |
| HeaderAddr, std::move(Finis)); | |
| }) | |
| .release(); | |
| } | |
| ORC_RT_INTERFACE orc_rt_WrapperFunctionResult | |
| __orc_rt_elfnix_deregister_fini_sections(char *ArgData, size_t ArgSize) { | |
| return WrapperFunction<SPSError(SPSExecutorAddr, | |
| SPSSequence<SPSExecutorAddrRange>)>:: | |
| handle(ArgData, ArgSize, | |
| [](ExecutorAddr HeaderAddr, | |
| std::vector<ExecutorAddrRange> &Finis) { | |
| return ELFNixPlatformRuntimeState::get().deregisterFinis( | |
| HeaderAddr, std::move(Finis)); | |
| }) | |
| .release(); | |
| } | |
| /// Wrapper function for registering metadata on a per-object basis. | |
| ORC_RT_INTERFACE orc_rt_WrapperFunctionResult | |
| __orc_rt_elfnix_register_object_sections(char *ArgData, size_t ArgSize) { | |
| return WrapperFunction<SPSError(SPSELFNixPerObjectSectionsToRegister)>:: | |
| handle(ArgData, ArgSize, | |
| [](ELFNixPerObjectSectionsToRegister &POSR) { | |
| return ELFNixPlatformRuntimeState::get().registerObjectSections( | |
| std::move(POSR)); | |
| }) | |
| .release(); | |
| } | |
| /// Wrapper for releasing per-object metadat. | |
| ORC_RT_INTERFACE orc_rt_WrapperFunctionResult | |
| __orc_rt_elfnix_deregister_object_sections(char *ArgData, size_t ArgSize) { | |
| return WrapperFunction<SPSError(SPSELFNixPerObjectSectionsToRegister)>:: | |
| handle(ArgData, ArgSize, | |
| [](ELFNixPerObjectSectionsToRegister &POSR) { | |
| return ELFNixPlatformRuntimeState::get() | |
| .deregisterObjectSections(std::move(POSR)); | |
| }) | |
| .release(); | |
| } | |
| //------------------------------------------------------------------------------ | |
| // TLV support | |
| //------------------------------------------------------------------------------ | |
| ORC_RT_INTERFACE void *__orc_rt_elfnix_tls_get_addr_impl(TLSInfoEntry *D) { | |
| auto *TLVMgr = static_cast<ELFNixPlatformRuntimeTLVManager *>( | |
| pthread_getspecific(D->Key)); | |
| if (!TLVMgr) | |
| TLVMgr = new ELFNixPlatformRuntimeTLVManager(); | |
| if (pthread_setspecific(D->Key, TLVMgr)) { | |
| __orc_rt_log_error("Call to pthread_setspecific failed"); | |
| return nullptr; | |
| } | |
| return TLVMgr->getInstance( | |
| reinterpret_cast<char *>(static_cast<uintptr_t>(D->DataAddress))); | |
| } | |
| ORC_RT_INTERFACE ptrdiff_t ___orc_rt_elfnix_tlsdesc_resolver_impl( | |
| TLSDescriptor *D, const char *ThreadPointer) { | |
| const char *TLVPtr = reinterpret_cast<const char *>( | |
| __orc_rt_elfnix_tls_get_addr_impl(D->InfoEntry)); | |
| return TLVPtr - ThreadPointer; | |
| } | |
| ORC_RT_INTERFACE orc_rt_WrapperFunctionResult | |
| __orc_rt_elfnix_create_pthread_key(char *ArgData, size_t ArgSize) { | |
| return WrapperFunction<SPSExpected<uint64_t>(void)>::handle( | |
| ArgData, ArgSize, | |
| []() -> Expected<uint64_t> { | |
| pthread_key_t Key; | |
| if (int Err = pthread_key_create(&Key, destroyELFNixTLVMgr)) { | |
| __orc_rt_log_error("Call to pthread_key_create failed"); | |
| return make_error<StringError>(strerror(Err)); | |
| } | |
| return static_cast<uint64_t>(Key); | |
| }) | |
| .release(); | |
| } | |
| //------------------------------------------------------------------------------ | |
| // cxa_atexit support | |
| //------------------------------------------------------------------------------ | |
| int __orc_rt_elfnix_cxa_atexit(void (*func)(void *), void *arg, | |
| void *dso_handle) { | |
| return ELFNixPlatformRuntimeState::get().registerAtExit(func, arg, | |
| dso_handle); | |
| } | |
| int __orc_rt_elfnix_atexit(void (*func)(void *)) { | |
| auto &PlatformRTState = ELFNixPlatformRuntimeState::get(); | |
| return ELFNixPlatformRuntimeState::get().registerAtExit( | |
| func, NULL, PlatformRTState.getPlatformJDDSOHandle()); | |
| } | |
| void __orc_rt_elfnix_cxa_finalize(void *dso_handle) { | |
| ELFNixPlatformRuntimeState::get().runAtExits(dso_handle); | |
| } | |
| //------------------------------------------------------------------------------ | |
| // JIT'd dlfcn alternatives. | |
| //------------------------------------------------------------------------------ | |
| const char *__orc_rt_elfnix_jit_dlerror() { | |
| return ELFNixPlatformRuntimeState::get().dlerror(); | |
| } | |
| void *__orc_rt_elfnix_jit_dlopen(const char *path, int mode) { | |
| return ELFNixPlatformRuntimeState::get().dlopen(path, mode); | |
| } | |
| int __orc_rt_elfnix_jit_dlupdate(void *dso_handle) { | |
| return ELFNixPlatformRuntimeState::get().dlupdate(dso_handle); | |
| } | |
| int __orc_rt_elfnix_jit_dlclose(void *dso_handle) { | |
| return ELFNixPlatformRuntimeState::get().dlclose(dso_handle); | |
| } | |
| void *__orc_rt_elfnix_jit_dlsym(void *dso_handle, const char *symbol) { | |
| return ELFNixPlatformRuntimeState::get().dlsym(dso_handle, symbol); | |
| } | |
| //------------------------------------------------------------------------------ | |
| // ELFNix Run Program | |
| //------------------------------------------------------------------------------ | |
| ORC_RT_INTERFACE int64_t __orc_rt_elfnix_run_program( | |
| const char *JITDylibName, const char *EntrySymbolName, int argc, | |
| char *argv[]) { | |
| using MainTy = int (*)(int, char *[]); | |
| void *H = __orc_rt_elfnix_jit_dlopen(JITDylibName, | |
| orc_rt::elfnix::ORC_RT_RTLD_LAZY); | |
| if (!H) { | |
| __orc_rt_log_error(__orc_rt_elfnix_jit_dlerror()); | |
| return -1; | |
| } | |
| auto *Main = | |
| reinterpret_cast<MainTy>(__orc_rt_elfnix_jit_dlsym(H, EntrySymbolName)); | |
| if (!Main) { | |
| __orc_rt_log_error(__orc_rt_elfnix_jit_dlerror()); | |
| return -1; | |
| } | |
| int Result = Main(argc, argv); | |
| if (__orc_rt_elfnix_jit_dlclose(H) == -1) | |
| __orc_rt_log_error(__orc_rt_elfnix_jit_dlerror()); | |
| return Result; | |
| } |