| //===---- OrcRemoteTargetClient.h - Orc Remote-target Client ----*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines the OrcRemoteTargetClient class and helpers. This class |
| // can be used to communicate over an RPCChannel with an OrcRemoteTargetServer |
| // instance to support remote-JITing. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETCLIENT_H |
| #define LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETCLIENT_H |
| |
| #include "IndirectionUtils.h" |
| #include "OrcRemoteTargetRPCAPI.h" |
| #include <system_error> |
| |
| #define DEBUG_TYPE "orc-remote" |
| |
| namespace llvm { |
| namespace orc { |
| namespace remote { |
| |
| /// This class provides utilities (including memory manager, indirect stubs |
| /// manager, and compile callback manager types) that support remote JITing |
| /// in ORC. |
| /// |
| /// Each of the utility classes talks to a JIT server (an instance of the |
| /// OrcRemoteTargetServer class) via an RPC system (see RPCUtils.h) to carry out |
| /// its actions. |
| template <typename ChannelT> |
| class OrcRemoteTargetClient : public OrcRemoteTargetRPCAPI { |
| public: |
| /// Remote memory manager. |
| class RCMemoryManager : public RuntimeDyld::MemoryManager { |
| public: |
| RCMemoryManager(OrcRemoteTargetClient &Client, ResourceIdMgr::ResourceId Id) |
| : Client(Client), Id(Id) { |
| DEBUG(dbgs() << "Created remote allocator " << Id << "\n"); |
| } |
| |
| RCMemoryManager(RCMemoryManager &&Other) |
| : Client(std::move(Other.Client)), Id(std::move(Other.Id)), |
| Unmapped(std::move(Other.Unmapped)), |
| Unfinalized(std::move(Other.Unfinalized)) {} |
| |
| RCMemoryManager operator=(RCMemoryManager &&Other) { |
| Client = std::move(Other.Client); |
| Id = std::move(Other.Id); |
| Unmapped = std::move(Other.Unmapped); |
| Unfinalized = std::move(Other.Unfinalized); |
| return *this; |
| } |
| |
| ~RCMemoryManager() { |
| Client.destroyRemoteAllocator(Id); |
| DEBUG(dbgs() << "Destroyed remote allocator " << Id << "\n"); |
| } |
| |
| uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, |
| unsigned SectionID, |
| StringRef SectionName) override { |
| Unmapped.back().CodeAllocs.emplace_back(Size, Alignment); |
| uint8_t *Alloc = reinterpret_cast<uint8_t *>( |
| Unmapped.back().CodeAllocs.back().getLocalAddress()); |
| DEBUG(dbgs() << "Allocator " << Id << " allocated code for " |
| << SectionName << ": " << Alloc << " (" << Size |
| << " bytes, alignment " << Alignment << ")\n"); |
| return Alloc; |
| } |
| |
| uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, |
| unsigned SectionID, StringRef SectionName, |
| bool IsReadOnly) override { |
| if (IsReadOnly) { |
| Unmapped.back().RODataAllocs.emplace_back(Size, Alignment); |
| uint8_t *Alloc = reinterpret_cast<uint8_t *>( |
| Unmapped.back().RODataAllocs.back().getLocalAddress()); |
| DEBUG(dbgs() << "Allocator " << Id << " allocated ro-data for " |
| << SectionName << ": " << Alloc << " (" << Size |
| << " bytes, alignment " << Alignment << ")\n"); |
| return Alloc; |
| } // else... |
| |
| Unmapped.back().RWDataAllocs.emplace_back(Size, Alignment); |
| uint8_t *Alloc = reinterpret_cast<uint8_t *>( |
| Unmapped.back().RWDataAllocs.back().getLocalAddress()); |
| DEBUG(dbgs() << "Allocator " << Id << " allocated rw-data for " |
| << SectionName << ": " << Alloc << " (" << Size |
| << " bytes, alignment " << Alignment << ")\n"); |
| return Alloc; |
| } |
| |
| void reserveAllocationSpace(uintptr_t CodeSize, uint32_t CodeAlign, |
| uintptr_t RODataSize, uint32_t RODataAlign, |
| uintptr_t RWDataSize, |
| uint32_t RWDataAlign) override { |
| Unmapped.push_back(ObjectAllocs()); |
| |
| DEBUG(dbgs() << "Allocator " << Id << " reserved:\n"); |
| |
| if (CodeSize != 0) { |
| std::error_code EC = Client.reserveMem(Unmapped.back().RemoteCodeAddr, |
| Id, CodeSize, CodeAlign); |
| // FIXME; Add error to poll. |
| assert(!EC && "Failed reserving remote memory."); |
| (void)EC; |
| DEBUG(dbgs() << " code: " |
| << format("0x%016x", Unmapped.back().RemoteCodeAddr) |
| << " (" << CodeSize << " bytes, alignment " << CodeAlign |
| << ")\n"); |
| } |
| |
| if (RODataSize != 0) { |
| std::error_code EC = Client.reserveMem(Unmapped.back().RemoteRODataAddr, |
| Id, RODataSize, RODataAlign); |
| // FIXME; Add error to poll. |
| assert(!EC && "Failed reserving remote memory."); |
| (void)EC; |
| DEBUG(dbgs() << " ro-data: " |
| << format("0x%016x", Unmapped.back().RemoteRODataAddr) |
| << " (" << RODataSize << " bytes, alignment " |
| << RODataAlign << ")\n"); |
| } |
| |
| if (RWDataSize != 0) { |
| std::error_code EC = Client.reserveMem(Unmapped.back().RemoteRWDataAddr, |
| Id, RWDataSize, RWDataAlign); |
| // FIXME; Add error to poll. |
| assert(!EC && "Failed reserving remote memory."); |
| (void)EC; |
| DEBUG(dbgs() << " rw-data: " |
| << format("0x%016x", Unmapped.back().RemoteRWDataAddr) |
| << " (" << RWDataSize << " bytes, alignment " |
| << RWDataAlign << ")\n"); |
| } |
| } |
| |
| bool needsToReserveAllocationSpace() override { return true; } |
| |
| void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr, |
| size_t Size) override {} |
| |
| void deregisterEHFrames(uint8_t *addr, uint64_t LoadAddr, |
| size_t Size) override {} |
| |
| void notifyObjectLoaded(RuntimeDyld &Dyld, |
| const object::ObjectFile &Obj) override { |
| DEBUG(dbgs() << "Allocator " << Id << " applied mappings:\n"); |
| for (auto &ObjAllocs : Unmapped) { |
| { |
| TargetAddress NextCodeAddr = ObjAllocs.RemoteCodeAddr; |
| for (auto &Alloc : ObjAllocs.CodeAllocs) { |
| NextCodeAddr = RoundUpToAlignment(NextCodeAddr, Alloc.getAlign()); |
| Dyld.mapSectionAddress(Alloc.getLocalAddress(), NextCodeAddr); |
| DEBUG(dbgs() << " code: " |
| << static_cast<void *>(Alloc.getLocalAddress()) |
| << " -> " << format("0x%016x", NextCodeAddr) << "\n"); |
| Alloc.setRemoteAddress(NextCodeAddr); |
| NextCodeAddr += Alloc.getSize(); |
| } |
| } |
| { |
| TargetAddress NextRODataAddr = ObjAllocs.RemoteRODataAddr; |
| for (auto &Alloc : ObjAllocs.RODataAllocs) { |
| NextRODataAddr = |
| RoundUpToAlignment(NextRODataAddr, Alloc.getAlign()); |
| Dyld.mapSectionAddress(Alloc.getLocalAddress(), NextRODataAddr); |
| DEBUG(dbgs() << " ro-data: " |
| << static_cast<void *>(Alloc.getLocalAddress()) |
| << " -> " << format("0x%016x", NextRODataAddr) |
| << "\n"); |
| Alloc.setRemoteAddress(NextRODataAddr); |
| NextRODataAddr += Alloc.getSize(); |
| } |
| } |
| { |
| TargetAddress NextRWDataAddr = ObjAllocs.RemoteRWDataAddr; |
| for (auto &Alloc : ObjAllocs.RWDataAllocs) { |
| NextRWDataAddr = |
| RoundUpToAlignment(NextRWDataAddr, Alloc.getAlign()); |
| Dyld.mapSectionAddress(Alloc.getLocalAddress(), NextRWDataAddr); |
| DEBUG(dbgs() << " rw-data: " |
| << static_cast<void *>(Alloc.getLocalAddress()) |
| << " -> " << format("0x%016x", NextRWDataAddr) |
| << "\n"); |
| Alloc.setRemoteAddress(NextRWDataAddr); |
| NextRWDataAddr += Alloc.getSize(); |
| } |
| } |
| Unfinalized.push_back(std::move(ObjAllocs)); |
| } |
| Unmapped.clear(); |
| } |
| |
| bool finalizeMemory(std::string *ErrMsg = nullptr) override { |
| DEBUG(dbgs() << "Allocator " << Id << " finalizing:\n"); |
| |
| for (auto &ObjAllocs : Unfinalized) { |
| |
| for (auto &Alloc : ObjAllocs.CodeAllocs) { |
| DEBUG(dbgs() << " copying code: " |
| << static_cast<void *>(Alloc.getLocalAddress()) << " -> " |
| << format("0x%016x", Alloc.getRemoteAddress()) << " (" |
| << Alloc.getSize() << " bytes)\n"); |
| Client.writeMem(Alloc.getRemoteAddress(), Alloc.getLocalAddress(), |
| Alloc.getSize()); |
| } |
| |
| if (ObjAllocs.RemoteCodeAddr) { |
| DEBUG(dbgs() << " setting R-X permissions on code block: " |
| << format("0x%016x", ObjAllocs.RemoteCodeAddr) << "\n"); |
| Client.setProtections(Id, ObjAllocs.RemoteCodeAddr, |
| sys::Memory::MF_READ | sys::Memory::MF_EXEC); |
| } |
| |
| for (auto &Alloc : ObjAllocs.RODataAllocs) { |
| DEBUG(dbgs() << " copying ro-data: " |
| << static_cast<void *>(Alloc.getLocalAddress()) << " -> " |
| << format("0x%016x", Alloc.getRemoteAddress()) << " (" |
| << Alloc.getSize() << " bytes)\n"); |
| Client.writeMem(Alloc.getRemoteAddress(), Alloc.getLocalAddress(), |
| Alloc.getSize()); |
| } |
| |
| if (ObjAllocs.RemoteRODataAddr) { |
| DEBUG(dbgs() << " setting R-- permissions on ro-data block: " |
| << format("0x%016x", ObjAllocs.RemoteRODataAddr) |
| << "\n"); |
| Client.setProtections(Id, ObjAllocs.RemoteRODataAddr, |
| sys::Memory::MF_READ); |
| } |
| |
| for (auto &Alloc : ObjAllocs.RWDataAllocs) { |
| DEBUG(dbgs() << " copying rw-data: " |
| << static_cast<void *>(Alloc.getLocalAddress()) << " -> " |
| << format("0x%016x", Alloc.getRemoteAddress()) << " (" |
| << Alloc.getSize() << " bytes)\n"); |
| Client.writeMem(Alloc.getRemoteAddress(), Alloc.getLocalAddress(), |
| Alloc.getSize()); |
| } |
| |
| if (ObjAllocs.RemoteRWDataAddr) { |
| DEBUG(dbgs() << " setting RW- permissions on rw-data block: " |
| << format("0x%016x", ObjAllocs.RemoteRWDataAddr) |
| << "\n"); |
| Client.setProtections(Id, ObjAllocs.RemoteRWDataAddr, |
| sys::Memory::MF_READ | sys::Memory::MF_WRITE); |
| } |
| } |
| Unfinalized.clear(); |
| |
| return false; |
| } |
| |
| private: |
| class Alloc { |
| public: |
| Alloc(uint64_t Size, unsigned Align) |
| : Size(Size), Align(Align), Contents(new char[Size + Align - 1]), |
| RemoteAddr(0) {} |
| |
| Alloc(Alloc &&Other) |
| : Size(std::move(Other.Size)), Align(std::move(Other.Align)), |
| Contents(std::move(Other.Contents)), |
| RemoteAddr(std::move(Other.RemoteAddr)) {} |
| |
| Alloc &operator=(Alloc &&Other) { |
| Size = std::move(Other.Size); |
| Align = std::move(Other.Align); |
| Contents = std::move(Other.Contents); |
| RemoteAddr = std::move(Other.RemoteAddr); |
| return *this; |
| } |
| |
| uint64_t getSize() const { return Size; } |
| |
| unsigned getAlign() const { return Align; } |
| |
| char *getLocalAddress() const { |
| uintptr_t LocalAddr = reinterpret_cast<uintptr_t>(Contents.get()); |
| LocalAddr = RoundUpToAlignment(LocalAddr, Align); |
| return reinterpret_cast<char *>(LocalAddr); |
| } |
| |
| void setRemoteAddress(TargetAddress RemoteAddr) { |
| this->RemoteAddr = RemoteAddr; |
| } |
| |
| TargetAddress getRemoteAddress() const { return RemoteAddr; } |
| |
| private: |
| uint64_t Size; |
| unsigned Align; |
| std::unique_ptr<char[]> Contents; |
| TargetAddress RemoteAddr; |
| }; |
| |
| struct ObjectAllocs { |
| ObjectAllocs() |
| : RemoteCodeAddr(0), RemoteRODataAddr(0), RemoteRWDataAddr(0) {} |
| |
| ObjectAllocs(ObjectAllocs &&Other) |
| : RemoteCodeAddr(std::move(Other.RemoteCodeAddr)), |
| RemoteRODataAddr(std::move(Other.RemoteRODataAddr)), |
| RemoteRWDataAddr(std::move(Other.RemoteRWDataAddr)), |
| CodeAllocs(std::move(Other.CodeAllocs)), |
| RODataAllocs(std::move(Other.RODataAllocs)), |
| RWDataAllocs(std::move(Other.RWDataAllocs)) {} |
| |
| ObjectAllocs &operator=(ObjectAllocs &&Other) { |
| RemoteCodeAddr = std::move(Other.RemoteCodeAddr); |
| RemoteRODataAddr = std::move(Other.RemoteRODataAddr); |
| RemoteRWDataAddr = std::move(Other.RemoteRWDataAddr); |
| CodeAllocs = std::move(Other.CodeAllocs); |
| RODataAllocs = std::move(Other.RODataAllocs); |
| RWDataAllocs = std::move(Other.RWDataAllocs); |
| return *this; |
| } |
| |
| TargetAddress RemoteCodeAddr; |
| TargetAddress RemoteRODataAddr; |
| TargetAddress RemoteRWDataAddr; |
| std::vector<Alloc> CodeAllocs, RODataAllocs, RWDataAllocs; |
| }; |
| |
| OrcRemoteTargetClient &Client; |
| ResourceIdMgr::ResourceId Id; |
| std::vector<ObjectAllocs> Unmapped; |
| std::vector<ObjectAllocs> Unfinalized; |
| }; |
| |
| /// Remote indirect stubs manager. |
| class RCIndirectStubsManager : public IndirectStubsManager { |
| public: |
| RCIndirectStubsManager(OrcRemoteTargetClient &Remote, |
| ResourceIdMgr::ResourceId Id) |
| : Remote(Remote), Id(Id) {} |
| |
| ~RCIndirectStubsManager() { Remote.destroyIndirectStubsManager(Id); } |
| |
| std::error_code createStub(StringRef StubName, TargetAddress StubAddr, |
| JITSymbolFlags StubFlags) override { |
| if (auto EC = reserveStubs(1)) |
| return EC; |
| |
| return createStubInternal(StubName, StubAddr, StubFlags); |
| } |
| |
| std::error_code createStubs(const StubInitsMap &StubInits) override { |
| if (auto EC = reserveStubs(StubInits.size())) |
| return EC; |
| |
| for (auto &Entry : StubInits) |
| if (auto EC = createStubInternal(Entry.first(), Entry.second.first, |
| Entry.second.second)) |
| return EC; |
| |
| return std::error_code(); |
| } |
| |
| JITSymbol findStub(StringRef Name, bool ExportedStubsOnly) override { |
| auto I = StubIndexes.find(Name); |
| if (I == StubIndexes.end()) |
| return nullptr; |
| auto Key = I->second.first; |
| auto Flags = I->second.second; |
| auto StubSymbol = JITSymbol(getStubAddr(Key), Flags); |
| if (ExportedStubsOnly && !StubSymbol.isExported()) |
| return nullptr; |
| return StubSymbol; |
| } |
| |
| JITSymbol findPointer(StringRef Name) override { |
| auto I = StubIndexes.find(Name); |
| if (I == StubIndexes.end()) |
| return nullptr; |
| auto Key = I->second.first; |
| auto Flags = I->second.second; |
| return JITSymbol(getPtrAddr(Key), Flags); |
| } |
| |
| std::error_code updatePointer(StringRef Name, |
| TargetAddress NewAddr) override { |
| auto I = StubIndexes.find(Name); |
| assert(I != StubIndexes.end() && "No stub pointer for symbol"); |
| auto Key = I->second.first; |
| return Remote.writePointer(getPtrAddr(Key), NewAddr); |
| } |
| |
| private: |
| struct RemoteIndirectStubsInfo { |
| RemoteIndirectStubsInfo(TargetAddress StubBase, TargetAddress PtrBase, |
| unsigned NumStubs) |
| : StubBase(StubBase), PtrBase(PtrBase), NumStubs(NumStubs) {} |
| TargetAddress StubBase; |
| TargetAddress PtrBase; |
| unsigned NumStubs; |
| }; |
| |
| OrcRemoteTargetClient &Remote; |
| ResourceIdMgr::ResourceId Id; |
| std::vector<RemoteIndirectStubsInfo> RemoteIndirectStubsInfos; |
| typedef std::pair<uint16_t, uint16_t> StubKey; |
| std::vector<StubKey> FreeStubs; |
| StringMap<std::pair<StubKey, JITSymbolFlags>> StubIndexes; |
| |
| std::error_code reserveStubs(unsigned NumStubs) { |
| if (NumStubs <= FreeStubs.size()) |
| return std::error_code(); |
| |
| unsigned NewStubsRequired = NumStubs - FreeStubs.size(); |
| TargetAddress StubBase; |
| TargetAddress PtrBase; |
| unsigned NumStubsEmitted; |
| |
| Remote.emitIndirectStubs(StubBase, PtrBase, NumStubsEmitted, Id, |
| NewStubsRequired); |
| |
| unsigned NewBlockId = RemoteIndirectStubsInfos.size(); |
| RemoteIndirectStubsInfos.push_back( |
| RemoteIndirectStubsInfo(StubBase, PtrBase, NumStubsEmitted)); |
| |
| for (unsigned I = 0; I < NumStubsEmitted; ++I) |
| FreeStubs.push_back(std::make_pair(NewBlockId, I)); |
| |
| return std::error_code(); |
| } |
| |
| std::error_code createStubInternal(StringRef StubName, |
| TargetAddress InitAddr, |
| JITSymbolFlags StubFlags) { |
| auto Key = FreeStubs.back(); |
| FreeStubs.pop_back(); |
| StubIndexes[StubName] = std::make_pair(Key, StubFlags); |
| return Remote.writePointer(getPtrAddr(Key), InitAddr); |
| } |
| |
| TargetAddress getStubAddr(StubKey K) { |
| assert(RemoteIndirectStubsInfos[K.first].StubBase != 0 && |
| "Missing stub address"); |
| return RemoteIndirectStubsInfos[K.first].StubBase + |
| K.second * Remote.getIndirectStubSize(); |
| } |
| |
| TargetAddress getPtrAddr(StubKey K) { |
| assert(RemoteIndirectStubsInfos[K.first].PtrBase != 0 && |
| "Missing pointer address"); |
| return RemoteIndirectStubsInfos[K.first].PtrBase + |
| K.second * Remote.getPointerSize(); |
| } |
| }; |
| |
| /// Remote compile callback manager. |
| class RCCompileCallbackManager : public JITCompileCallbackManager { |
| public: |
| RCCompileCallbackManager(TargetAddress ErrorHandlerAddress, |
| OrcRemoteTargetClient &Remote) |
| : JITCompileCallbackManager(ErrorHandlerAddress), Remote(Remote) { |
| assert(!Remote.CompileCallback && "Compile callback already set"); |
| Remote.CompileCallback = [this](TargetAddress TrampolineAddr) { |
| return executeCompileCallback(TrampolineAddr); |
| }; |
| Remote.emitResolverBlock(); |
| } |
| |
| private: |
| void grow() { |
| TargetAddress BlockAddr = 0; |
| uint32_t NumTrampolines = 0; |
| auto EC = Remote.emitTrampolineBlock(BlockAddr, NumTrampolines); |
| assert(!EC && "Failed to create trampolines"); |
| |
| uint32_t TrampolineSize = Remote.getTrampolineSize(); |
| for (unsigned I = 0; I < NumTrampolines; ++I) |
| this->AvailableTrampolines.push_back(BlockAddr + (I * TrampolineSize)); |
| } |
| |
| OrcRemoteTargetClient &Remote; |
| }; |
| |
| /// Create an OrcRemoteTargetClient. |
| /// Channel is the ChannelT instance to communicate on. It is assumed that |
| /// the channel is ready to be read from and written to. |
| static ErrorOr<OrcRemoteTargetClient> Create(ChannelT &Channel) { |
| std::error_code EC; |
| OrcRemoteTargetClient H(Channel, EC); |
| if (EC) |
| return EC; |
| return H; |
| } |
| |
| /// Call the int(void) function at the given address in the target and return |
| /// its result. |
| std::error_code callIntVoid(int &Result, TargetAddress Addr) { |
| DEBUG(dbgs() << "Calling int(*)(void) " << format("0x%016x", Addr) << "\n"); |
| |
| if (auto EC = call<CallIntVoid>(Channel, Addr)) |
| return EC; |
| |
| unsigned NextProcId; |
| if (auto EC = listenForCompileRequests(NextProcId)) |
| return EC; |
| |
| if (NextProcId != CallIntVoidResponseId) |
| return orcError(OrcErrorCode::UnexpectedRPCCall); |
| |
| return handle<CallIntVoidResponse>(Channel, [&](int R) { |
| Result = R; |
| DEBUG(dbgs() << "Result: " << R << "\n"); |
| return std::error_code(); |
| }); |
| } |
| |
| /// Call the int(int, char*[]) function at the given address in the target and |
| /// return its result. |
| std::error_code callMain(int &Result, TargetAddress Addr, |
| const std::vector<std::string> &Args) { |
| DEBUG(dbgs() << "Calling int(*)(int, char*[]) " << format("0x%016x", Addr) |
| << "\n"); |
| |
| if (auto EC = call<CallMain>(Channel, Addr, Args)) |
| return EC; |
| |
| unsigned NextProcId; |
| if (auto EC = listenForCompileRequests(NextProcId)) |
| return EC; |
| |
| if (NextProcId != CallMainResponseId) |
| return orcError(OrcErrorCode::UnexpectedRPCCall); |
| |
| return handle<CallMainResponse>(Channel, [&](int R) { |
| Result = R; |
| DEBUG(dbgs() << "Result: " << R << "\n"); |
| return std::error_code(); |
| }); |
| } |
| |
| /// Call the void() function at the given address in the target and wait for |
| /// it to finish. |
| std::error_code callVoidVoid(TargetAddress Addr) { |
| DEBUG(dbgs() << "Calling void(*)(void) " << format("0x%016x", Addr) |
| << "\n"); |
| |
| if (auto EC = call<CallVoidVoid>(Channel, Addr)) |
| return EC; |
| |
| unsigned NextProcId; |
| if (auto EC = listenForCompileRequests(NextProcId)) |
| return EC; |
| |
| if (NextProcId != CallVoidVoidResponseId) |
| return orcError(OrcErrorCode::UnexpectedRPCCall); |
| |
| return handle<CallVoidVoidResponse>(Channel, doNothing); |
| } |
| |
| /// Create an RCMemoryManager which will allocate its memory on the remote |
| /// target. |
| std::error_code |
| createRemoteMemoryManager(std::unique_ptr<RCMemoryManager> &MM) { |
| assert(!MM && "MemoryManager should be null before creation."); |
| |
| auto Id = AllocatorIds.getNext(); |
| if (auto EC = call<CreateRemoteAllocator>(Channel, Id)) |
| return EC; |
| MM = llvm::make_unique<RCMemoryManager>(*this, Id); |
| return std::error_code(); |
| } |
| |
| /// Create an RCIndirectStubsManager that will allocate stubs on the remote |
| /// target. |
| std::error_code |
| createIndirectStubsManager(std::unique_ptr<RCIndirectStubsManager> &I) { |
| assert(!I && "Indirect stubs manager should be null before creation."); |
| auto Id = IndirectStubOwnerIds.getNext(); |
| if (auto EC = call<CreateIndirectStubsOwner>(Channel, Id)) |
| return EC; |
| I = llvm::make_unique<RCIndirectStubsManager>(*this, Id); |
| return std::error_code(); |
| } |
| |
| /// Search for symbols in the remote process. Note: This should be used by |
| /// symbol resolvers *after* they've searched the local symbol table in the |
| /// JIT stack. |
| std::error_code getSymbolAddress(TargetAddress &Addr, StringRef Name) { |
| // Check for an 'out-of-band' error, e.g. from an MM destructor. |
| if (ExistingError) |
| return ExistingError; |
| |
| // Request remote symbol address. |
| if (auto EC = call<GetSymbolAddress>(Channel, Name)) |
| return EC; |
| |
| return expect<GetSymbolAddressResponse>(Channel, [&](TargetAddress &A) { |
| Addr = A; |
| DEBUG(dbgs() << "Remote address lookup " << Name << " = " |
| << format("0x%016x", Addr) << "\n"); |
| return std::error_code(); |
| }); |
| } |
| |
| /// Get the triple for the remote target. |
| const std::string &getTargetTriple() const { return RemoteTargetTriple; } |
| |
| std::error_code terminateSession() { return call<TerminateSession>(Channel); } |
| |
| private: |
| OrcRemoteTargetClient(ChannelT &Channel, std::error_code &EC) |
| : Channel(Channel), RemotePointerSize(0), RemotePageSize(0), |
| RemoteTrampolineSize(0), RemoteIndirectStubSize(0) { |
| if ((EC = call<GetRemoteInfo>(Channel))) |
| return; |
| |
| EC = expect<GetRemoteInfoResponse>( |
| Channel, readArgs(RemoteTargetTriple, RemotePointerSize, RemotePageSize, |
| RemoteTrampolineSize, RemoteIndirectStubSize)); |
| } |
| |
| void destroyRemoteAllocator(ResourceIdMgr::ResourceId Id) { |
| if (auto EC = call<DestroyRemoteAllocator>(Channel, Id)) { |
| // FIXME: This will be triggered by a removeModuleSet call: Propagate |
| // error return up through that. |
| llvm_unreachable("Failed to destroy remote allocator."); |
| AllocatorIds.release(Id); |
| } |
| } |
| |
| std::error_code destroyIndirectStubsManager(ResourceIdMgr::ResourceId Id) { |
| IndirectStubOwnerIds.release(Id); |
| return call<DestroyIndirectStubsOwner>(Channel, Id); |
| } |
| |
| std::error_code emitIndirectStubs(TargetAddress &StubBase, |
| TargetAddress &PtrBase, |
| uint32_t &NumStubsEmitted, |
| ResourceIdMgr::ResourceId Id, |
| uint32_t NumStubsRequired) { |
| if (auto EC = call<EmitIndirectStubs>(Channel, Id, NumStubsRequired)) |
| return EC; |
| |
| return expect<EmitIndirectStubsResponse>( |
| Channel, readArgs(StubBase, PtrBase, NumStubsEmitted)); |
| } |
| |
| std::error_code emitResolverBlock() { |
| // Check for an 'out-of-band' error, e.g. from an MM destructor. |
| if (ExistingError) |
| return ExistingError; |
| |
| return call<EmitResolverBlock>(Channel); |
| } |
| |
| std::error_code emitTrampolineBlock(TargetAddress &BlockAddr, |
| uint32_t &NumTrampolines) { |
| // Check for an 'out-of-band' error, e.g. from an MM destructor. |
| if (ExistingError) |
| return ExistingError; |
| |
| if (auto EC = call<EmitTrampolineBlock>(Channel)) |
| return EC; |
| |
| return expect<EmitTrampolineBlockResponse>( |
| Channel, [&](TargetAddress BAddr, uint32_t NTrampolines) { |
| BlockAddr = BAddr; |
| NumTrampolines = NTrampolines; |
| return std::error_code(); |
| }); |
| } |
| |
| uint32_t getIndirectStubSize() const { return RemoteIndirectStubSize; } |
| uint32_t getPageSize() const { return RemotePageSize; } |
| uint32_t getPointerSize() const { return RemotePointerSize; } |
| |
| uint32_t getTrampolineSize() const { return RemoteTrampolineSize; } |
| |
| std::error_code listenForCompileRequests(uint32_t &NextId) { |
| // Check for an 'out-of-band' error, e.g. from an MM destructor. |
| if (ExistingError) |
| return ExistingError; |
| |
| if (auto EC = getNextProcId(Channel, NextId)) |
| return EC; |
| |
| while (NextId == RequestCompileId) { |
| TargetAddress TrampolineAddr = 0; |
| if (auto EC = handle<RequestCompile>(Channel, readArgs(TrampolineAddr))) |
| return EC; |
| |
| TargetAddress ImplAddr = CompileCallback(TrampolineAddr); |
| if (auto EC = call<RequestCompileResponse>(Channel, ImplAddr)) |
| return EC; |
| |
| if (auto EC = getNextProcId(Channel, NextId)) |
| return EC; |
| } |
| |
| return std::error_code(); |
| } |
| |
| std::error_code readMem(char *Dst, TargetAddress Src, uint64_t Size) { |
| // Check for an 'out-of-band' error, e.g. from an MM destructor. |
| if (ExistingError) |
| return ExistingError; |
| |
| if (auto EC = call<ReadMem>(Channel, Src, Size)) |
| return EC; |
| |
| if (auto EC = expect<ReadMemResponse>( |
| Channel, [&]() { return Channel.readBytes(Dst, Size); })) |
| return EC; |
| |
| return std::error_code(); |
| } |
| |
| std::error_code reserveMem(TargetAddress &RemoteAddr, |
| ResourceIdMgr::ResourceId Id, uint64_t Size, |
| uint32_t Align) { |
| |
| // Check for an 'out-of-band' error, e.g. from an MM destructor. |
| if (ExistingError) |
| return ExistingError; |
| |
| if (std::error_code EC = call<ReserveMem>(Channel, Id, Size, Align)) |
| return EC; |
| |
| return expect<ReserveMemResponse>(Channel, readArgs(RemoteAddr)); |
| } |
| |
| std::error_code setProtections(ResourceIdMgr::ResourceId Id, |
| TargetAddress RemoteSegAddr, |
| unsigned ProtFlags) { |
| return call<SetProtections>(Channel, Id, RemoteSegAddr, ProtFlags); |
| } |
| |
| std::error_code writeMem(TargetAddress Addr, const char *Src, uint64_t Size) { |
| // Check for an 'out-of-band' error, e.g. from an MM destructor. |
| if (ExistingError) |
| return ExistingError; |
| |
| // Make the send call. |
| if (auto EC = call<WriteMem>(Channel, Addr, Size)) |
| return EC; |
| |
| // Follow this up with the section contents. |
| if (auto EC = Channel.appendBytes(Src, Size)) |
| return EC; |
| |
| return Channel.send(); |
| } |
| |
| std::error_code writePointer(TargetAddress Addr, TargetAddress PtrVal) { |
| // Check for an 'out-of-band' error, e.g. from an MM destructor. |
| if (ExistingError) |
| return ExistingError; |
| |
| return call<WritePtr>(Channel, Addr, PtrVal); |
| } |
| |
| static std::error_code doNothing() { return std::error_code(); } |
| |
| ChannelT &Channel; |
| std::error_code ExistingError; |
| std::string RemoteTargetTriple; |
| uint32_t RemotePointerSize; |
| uint32_t RemotePageSize; |
| uint32_t RemoteTrampolineSize; |
| uint32_t RemoteIndirectStubSize; |
| ResourceIdMgr AllocatorIds, IndirectStubOwnerIds; |
| std::function<TargetAddress(TargetAddress)> CompileCallback; |
| }; |
| |
| } // end namespace remote |
| } // end namespace orc |
| } // end namespace llvm |
| |
| #undef DEBUG_TYPE |
| |
| #endif |