| //===---- OrcRemoteTargetServer.h - Orc Remote-target Server ----*- 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 OrcRemoteTargetServer class. It can be used to build a |
| // JIT server that can execute code sent from an OrcRemoteTargetClient. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H |
| #define LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H |
| |
| #include "OrcRemoteTargetRPCAPI.h" |
| #include "llvm/ExecutionEngine/RTDyldMemoryManager.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/Format.h" |
| #include "llvm/Support/Process.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <map> |
| |
| #define DEBUG_TYPE "orc-remote" |
| |
| namespace llvm { |
| namespace orc { |
| namespace remote { |
| |
| template <typename ChannelT, typename TargetT> |
| class OrcRemoteTargetServer : public OrcRemoteTargetRPCAPI { |
| public: |
| typedef std::function<TargetAddress(const std::string &Name)> |
| SymbolLookupFtor; |
| |
| OrcRemoteTargetServer(ChannelT &Channel, SymbolLookupFtor SymbolLookup) |
| : Channel(Channel), SymbolLookup(std::move(SymbolLookup)) {} |
| |
| std::error_code getNextProcId(JITProcId &Id) { |
| return deserialize(Channel, Id); |
| } |
| |
| std::error_code handleKnownProcedure(JITProcId Id) { |
| typedef OrcRemoteTargetServer ThisT; |
| |
| DEBUG(dbgs() << "Handling known proc: " << getJITProcIdName(Id) << "\n"); |
| |
| switch (Id) { |
| case CallIntVoidId: |
| return handle<CallIntVoid>(Channel, *this, &ThisT::handleCallIntVoid); |
| case CallMainId: |
| return handle<CallMain>(Channel, *this, &ThisT::handleCallMain); |
| case CallVoidVoidId: |
| return handle<CallVoidVoid>(Channel, *this, &ThisT::handleCallVoidVoid); |
| case CreateRemoteAllocatorId: |
| return handle<CreateRemoteAllocator>(Channel, *this, |
| &ThisT::handleCreateRemoteAllocator); |
| case CreateIndirectStubsOwnerId: |
| return handle<CreateIndirectStubsOwner>( |
| Channel, *this, &ThisT::handleCreateIndirectStubsOwner); |
| case DestroyRemoteAllocatorId: |
| return handle<DestroyRemoteAllocator>( |
| Channel, *this, &ThisT::handleDestroyRemoteAllocator); |
| case DestroyIndirectStubsOwnerId: |
| return handle<DestroyIndirectStubsOwner>( |
| Channel, *this, &ThisT::handleDestroyIndirectStubsOwner); |
| case EmitIndirectStubsId: |
| return handle<EmitIndirectStubs>(Channel, *this, |
| &ThisT::handleEmitIndirectStubs); |
| case EmitResolverBlockId: |
| return handle<EmitResolverBlock>(Channel, *this, |
| &ThisT::handleEmitResolverBlock); |
| case EmitTrampolineBlockId: |
| return handle<EmitTrampolineBlock>(Channel, *this, |
| &ThisT::handleEmitTrampolineBlock); |
| case GetSymbolAddressId: |
| return handle<GetSymbolAddress>(Channel, *this, |
| &ThisT::handleGetSymbolAddress); |
| case GetRemoteInfoId: |
| return handle<GetRemoteInfo>(Channel, *this, &ThisT::handleGetRemoteInfo); |
| case ReadMemId: |
| return handle<ReadMem>(Channel, *this, &ThisT::handleReadMem); |
| case ReserveMemId: |
| return handle<ReserveMem>(Channel, *this, &ThisT::handleReserveMem); |
| case SetProtectionsId: |
| return handle<SetProtections>(Channel, *this, |
| &ThisT::handleSetProtections); |
| case WriteMemId: |
| return handle<WriteMem>(Channel, *this, &ThisT::handleWriteMem); |
| case WritePtrId: |
| return handle<WritePtr>(Channel, *this, &ThisT::handleWritePtr); |
| default: |
| return orcError(OrcErrorCode::UnexpectedRPCCall); |
| } |
| |
| llvm_unreachable("Unhandled JIT RPC procedure Id."); |
| } |
| |
| std::error_code requestCompile(TargetAddress &CompiledFnAddr, |
| TargetAddress TrampolineAddr) { |
| if (auto EC = call<RequestCompile>(Channel, TrampolineAddr)) |
| return EC; |
| |
| while (1) { |
| JITProcId Id = InvalidId; |
| if (auto EC = getNextProcId(Id)) |
| return EC; |
| |
| switch (Id) { |
| case RequestCompileResponseId: |
| return handle<RequestCompileResponse>(Channel, |
| readArgs(CompiledFnAddr)); |
| default: |
| if (auto EC = handleKnownProcedure(Id)) |
| return EC; |
| } |
| } |
| |
| llvm_unreachable("Fell through request-compile command loop."); |
| } |
| |
| private: |
| struct Allocator { |
| Allocator() = default; |
| Allocator(Allocator &&Other) : Allocs(std::move(Other.Allocs)) {} |
| Allocator &operator=(Allocator &&Other) { |
| Allocs = std::move(Other.Allocs); |
| return *this; |
| } |
| |
| ~Allocator() { |
| for (auto &Alloc : Allocs) |
| sys::Memory::releaseMappedMemory(Alloc.second); |
| } |
| |
| std::error_code allocate(void *&Addr, size_t Size, uint32_t Align) { |
| std::error_code EC; |
| sys::MemoryBlock MB = sys::Memory::allocateMappedMemory( |
| Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC); |
| if (EC) |
| return EC; |
| |
| Addr = MB.base(); |
| assert(Allocs.find(MB.base()) == Allocs.end() && "Duplicate alloc"); |
| Allocs[MB.base()] = std::move(MB); |
| return std::error_code(); |
| } |
| |
| std::error_code setProtections(void *block, unsigned Flags) { |
| auto I = Allocs.find(block); |
| if (I == Allocs.end()) |
| return orcError(OrcErrorCode::RemoteMProtectAddrUnrecognized); |
| return sys::Memory::protectMappedMemory(I->second, Flags); |
| } |
| |
| private: |
| std::map<void *, sys::MemoryBlock> Allocs; |
| }; |
| |
| static std::error_code doNothing() { return std::error_code(); } |
| |
| static TargetAddress reenter(void *JITTargetAddr, void *TrampolineAddr) { |
| TargetAddress CompiledFnAddr = 0; |
| |
| auto T = static_cast<OrcRemoteTargetServer *>(JITTargetAddr); |
| auto EC = T->requestCompile( |
| CompiledFnAddr, static_cast<TargetAddress>( |
| reinterpret_cast<uintptr_t>(TrampolineAddr))); |
| assert(!EC && "Compile request failed"); |
| (void)EC; |
| return CompiledFnAddr; |
| } |
| |
| std::error_code handleCallIntVoid(TargetAddress Addr) { |
| typedef int (*IntVoidFnTy)(); |
| IntVoidFnTy Fn = |
| reinterpret_cast<IntVoidFnTy>(static_cast<uintptr_t>(Addr)); |
| |
| DEBUG(dbgs() << " Calling " |
| << reinterpret_cast<void *>(reinterpret_cast<intptr_t>(Fn)) |
| << "\n"); |
| int Result = Fn(); |
| DEBUG(dbgs() << " Result = " << Result << "\n"); |
| |
| return call<CallIntVoidResponse>(Channel, Result); |
| } |
| |
| std::error_code handleCallMain(TargetAddress Addr, |
| std::vector<std::string> Args) { |
| typedef int (*MainFnTy)(int, const char *[]); |
| |
| MainFnTy Fn = reinterpret_cast<MainFnTy>(static_cast<uintptr_t>(Addr)); |
| int ArgC = Args.size() + 1; |
| int Idx = 1; |
| std::unique_ptr<const char *[]> ArgV(new const char *[ArgC + 1]); |
| ArgV[0] = "<jit process>"; |
| for (auto &Arg : Args) |
| ArgV[Idx++] = Arg.c_str(); |
| |
| DEBUG(dbgs() << " Calling " << reinterpret_cast<void *>(Fn) << "\n"); |
| int Result = Fn(ArgC, ArgV.get()); |
| DEBUG(dbgs() << " Result = " << Result << "\n"); |
| |
| return call<CallMainResponse>(Channel, Result); |
| } |
| |
| std::error_code handleCallVoidVoid(TargetAddress Addr) { |
| typedef void (*VoidVoidFnTy)(); |
| VoidVoidFnTy Fn = |
| reinterpret_cast<VoidVoidFnTy>(static_cast<uintptr_t>(Addr)); |
| |
| DEBUG(dbgs() << " Calling " << reinterpret_cast<void *>(Fn) << "\n"); |
| Fn(); |
| DEBUG(dbgs() << " Complete.\n"); |
| |
| return call<CallVoidVoidResponse>(Channel); |
| } |
| |
| std::error_code handleCreateRemoteAllocator(ResourceIdMgr::ResourceId Id) { |
| auto I = Allocators.find(Id); |
| if (I != Allocators.end()) |
| return orcError(OrcErrorCode::RemoteAllocatorIdAlreadyInUse); |
| DEBUG(dbgs() << " Created allocator " << Id << "\n"); |
| Allocators[Id] = Allocator(); |
| return std::error_code(); |
| } |
| |
| std::error_code handleCreateIndirectStubsOwner(ResourceIdMgr::ResourceId Id) { |
| auto I = IndirectStubsOwners.find(Id); |
| if (I != IndirectStubsOwners.end()) |
| return orcError(OrcErrorCode::RemoteIndirectStubsOwnerIdAlreadyInUse); |
| DEBUG(dbgs() << " Create indirect stubs owner " << Id << "\n"); |
| IndirectStubsOwners[Id] = ISBlockOwnerList(); |
| return std::error_code(); |
| } |
| |
| std::error_code handleDestroyRemoteAllocator(ResourceIdMgr::ResourceId Id) { |
| auto I = Allocators.find(Id); |
| if (I == Allocators.end()) |
| return orcError(OrcErrorCode::RemoteAllocatorDoesNotExist); |
| Allocators.erase(I); |
| DEBUG(dbgs() << " Destroyed allocator " << Id << "\n"); |
| return std::error_code(); |
| } |
| |
| std::error_code |
| handleDestroyIndirectStubsOwner(ResourceIdMgr::ResourceId Id) { |
| auto I = IndirectStubsOwners.find(Id); |
| if (I == IndirectStubsOwners.end()) |
| return orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist); |
| IndirectStubsOwners.erase(I); |
| return std::error_code(); |
| } |
| |
| std::error_code handleEmitIndirectStubs(ResourceIdMgr::ResourceId Id, |
| uint32_t NumStubsRequired) { |
| DEBUG(dbgs() << " ISMgr " << Id << " request " << NumStubsRequired |
| << " stubs.\n"); |
| |
| auto StubOwnerItr = IndirectStubsOwners.find(Id); |
| if (StubOwnerItr == IndirectStubsOwners.end()) |
| return orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist); |
| |
| typename TargetT::IndirectStubsInfo IS; |
| if (auto EC = |
| TargetT::emitIndirectStubsBlock(IS, NumStubsRequired, nullptr)) |
| return EC; |
| |
| TargetAddress StubsBase = |
| static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(IS.getStub(0))); |
| TargetAddress PtrsBase = |
| static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(IS.getPtr(0))); |
| uint32_t NumStubsEmitted = IS.getNumStubs(); |
| |
| auto &BlockList = StubOwnerItr->second; |
| BlockList.push_back(std::move(IS)); |
| |
| return call<EmitIndirectStubsResponse>(Channel, StubsBase, PtrsBase, |
| NumStubsEmitted); |
| } |
| |
| std::error_code handleEmitResolverBlock() { |
| std::error_code EC; |
| ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory( |
| TargetT::ResolverCodeSize, nullptr, |
| sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC)); |
| if (EC) |
| return EC; |
| |
| TargetT::writeResolverCode(static_cast<uint8_t *>(ResolverBlock.base()), |
| &reenter, this); |
| |
| return sys::Memory::protectMappedMemory(ResolverBlock.getMemoryBlock(), |
| sys::Memory::MF_READ | |
| sys::Memory::MF_EXEC); |
| } |
| |
| std::error_code handleEmitTrampolineBlock() { |
| std::error_code EC; |
| auto TrampolineBlock = |
| sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory( |
| sys::Process::getPageSize(), nullptr, |
| sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC)); |
| if (EC) |
| return EC; |
| |
| unsigned NumTrampolines = |
| (sys::Process::getPageSize() - TargetT::PointerSize) / |
| TargetT::TrampolineSize; |
| |
| uint8_t *TrampolineMem = static_cast<uint8_t *>(TrampolineBlock.base()); |
| TargetT::writeTrampolines(TrampolineMem, ResolverBlock.base(), |
| NumTrampolines); |
| |
| EC = sys::Memory::protectMappedMemory(TrampolineBlock.getMemoryBlock(), |
| sys::Memory::MF_READ | |
| sys::Memory::MF_EXEC); |
| |
| TrampolineBlocks.push_back(std::move(TrampolineBlock)); |
| |
| return call<EmitTrampolineBlockResponse>( |
| Channel, |
| static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(TrampolineMem)), |
| NumTrampolines); |
| } |
| |
| std::error_code handleGetSymbolAddress(const std::string &Name) { |
| TargetAddress Addr = SymbolLookup(Name); |
| DEBUG(dbgs() << " Symbol '" << Name << "' = " << format("0x%016x", Addr) |
| << "\n"); |
| return call<GetSymbolAddressResponse>(Channel, Addr); |
| } |
| |
| std::error_code handleGetRemoteInfo() { |
| std::string ProcessTriple = sys::getProcessTriple(); |
| uint32_t PointerSize = TargetT::PointerSize; |
| uint32_t PageSize = sys::Process::getPageSize(); |
| uint32_t TrampolineSize = TargetT::TrampolineSize; |
| uint32_t IndirectStubSize = TargetT::IndirectStubsInfo::StubSize; |
| DEBUG(dbgs() << " Remote info:\n" |
| << " triple = '" << ProcessTriple << "'\n" |
| << " pointer size = " << PointerSize << "\n" |
| << " page size = " << PageSize << "\n" |
| << " trampoline size = " << TrampolineSize << "\n" |
| << " indirect stub size = " << IndirectStubSize << "\n"); |
| return call<GetRemoteInfoResponse>(Channel, ProcessTriple, PointerSize, |
| PageSize, TrampolineSize, |
| IndirectStubSize); |
| } |
| |
| std::error_code handleReadMem(TargetAddress RSrc, uint64_t Size) { |
| char *Src = reinterpret_cast<char *>(static_cast<uintptr_t>(RSrc)); |
| |
| DEBUG(dbgs() << " Reading " << Size << " bytes from " |
| << static_cast<void *>(Src) << "\n"); |
| |
| if (auto EC = call<ReadMemResponse>(Channel)) |
| return EC; |
| |
| if (auto EC = Channel.appendBytes(Src, Size)) |
| return EC; |
| |
| return Channel.send(); |
| } |
| |
| std::error_code handleReserveMem(ResourceIdMgr::ResourceId Id, uint64_t Size, |
| uint32_t Align) { |
| auto I = Allocators.find(Id); |
| if (I == Allocators.end()) |
| return orcError(OrcErrorCode::RemoteAllocatorDoesNotExist); |
| auto &Allocator = I->second; |
| void *LocalAllocAddr = nullptr; |
| if (auto EC = Allocator.allocate(LocalAllocAddr, Size, Align)) |
| return EC; |
| |
| DEBUG(dbgs() << " Allocator " << Id << " reserved " << LocalAllocAddr |
| << " (" << Size << " bytes, alignment " << Align << ")\n"); |
| |
| TargetAddress AllocAddr = |
| static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(LocalAllocAddr)); |
| |
| return call<ReserveMemResponse>(Channel, AllocAddr); |
| } |
| |
| std::error_code handleSetProtections(ResourceIdMgr::ResourceId Id, |
| TargetAddress Addr, uint32_t Flags) { |
| auto I = Allocators.find(Id); |
| if (I == Allocators.end()) |
| return orcError(OrcErrorCode::RemoteAllocatorDoesNotExist); |
| auto &Allocator = I->second; |
| void *LocalAddr = reinterpret_cast<void *>(static_cast<uintptr_t>(Addr)); |
| DEBUG(dbgs() << " Allocator " << Id << " set permissions on " << LocalAddr |
| << " to " << (Flags & sys::Memory::MF_READ ? 'R' : '-') |
| << (Flags & sys::Memory::MF_WRITE ? 'W' : '-') |
| << (Flags & sys::Memory::MF_EXEC ? 'X' : '-') << "\n"); |
| return Allocator.setProtections(LocalAddr, Flags); |
| } |
| |
| std::error_code handleWriteMem(TargetAddress RDst, uint64_t Size) { |
| char *Dst = reinterpret_cast<char *>(static_cast<uintptr_t>(RDst)); |
| DEBUG(dbgs() << " Writing " << Size << " bytes to " |
| << format("0x%016x", RDst) << "\n"); |
| return Channel.readBytes(Dst, Size); |
| } |
| |
| std::error_code handleWritePtr(TargetAddress Addr, TargetAddress PtrVal) { |
| DEBUG(dbgs() << " Writing pointer *" << format("0x%016x", Addr) << " = " |
| << format("0x%016x", PtrVal) << "\n"); |
| uintptr_t *Ptr = |
| reinterpret_cast<uintptr_t *>(static_cast<uintptr_t>(Addr)); |
| *Ptr = static_cast<uintptr_t>(PtrVal); |
| return std::error_code(); |
| } |
| |
| ChannelT &Channel; |
| SymbolLookupFtor SymbolLookup; |
| std::map<ResourceIdMgr::ResourceId, Allocator> Allocators; |
| typedef std::vector<typename TargetT::IndirectStubsInfo> ISBlockOwnerList; |
| std::map<ResourceIdMgr::ResourceId, ISBlockOwnerList> IndirectStubsOwners; |
| sys::OwningMemoryBlock ResolverBlock; |
| std::vector<sys::OwningMemoryBlock> TrampolineBlocks; |
| }; |
| |
| } // end namespace remote |
| } // end namespace orc |
| } // end namespace llvm |
| |
| #undef DEBUG_TYPE |
| |
| #endif |