//===---- 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 #define DEBUG_TYPE "orc-remote" namespace llvm { namespace orc { namespace remote { template class OrcRemoteTargetServer : public OrcRemoteTargetRPCAPI { public: typedef std::function SymbolLookupFtor; typedef std::function EHFrameRegistrationFtor; OrcRemoteTargetServer(ChannelT &Channel, SymbolLookupFtor SymbolLookup, EHFrameRegistrationFtor EHFramesRegister, EHFrameRegistrationFtor EHFramesDeregister) : Channel(Channel), SymbolLookup(std::move(SymbolLookup)), EHFramesRegister(std::move(EHFramesRegister)), EHFramesDeregister(std::move(EHFramesDeregister)) {} // FIXME: Remove move/copy ops once MSVC supports synthesizing move ops. OrcRemoteTargetServer(const OrcRemoteTargetServer &) = delete; OrcRemoteTargetServer &operator=(const OrcRemoteTargetServer &) = delete; OrcRemoteTargetServer(OrcRemoteTargetServer &&Other) : Channel(Other.Channel), SymbolLookup(std::move(Other.SymbolLookup)), EHFramesRegister(std::move(Other.EHFramesRegister)), EHFramesDeregister(std::move(Other.EHFramesDeregister)) {} OrcRemoteTargetServer &operator=(OrcRemoteTargetServer &&) = delete; Error handleKnownFunction(JITFuncId Id) { typedef OrcRemoteTargetServer ThisT; DEBUG(dbgs() << "Handling known proc: " << getJITFuncIdName(Id) << "\n"); switch (Id) { case CallIntVoidId: return handle(Channel, *this, &ThisT::handleCallIntVoid); case CallMainId: return handle(Channel, *this, &ThisT::handleCallMain); case CallVoidVoidId: return handle(Channel, *this, &ThisT::handleCallVoidVoid); case CreateRemoteAllocatorId: return handle(Channel, *this, &ThisT::handleCreateRemoteAllocator); case CreateIndirectStubsOwnerId: return handle( Channel, *this, &ThisT::handleCreateIndirectStubsOwner); case DeregisterEHFramesId: return handle(Channel, *this, &ThisT::handleDeregisterEHFrames); case DestroyRemoteAllocatorId: return handle( Channel, *this, &ThisT::handleDestroyRemoteAllocator); case DestroyIndirectStubsOwnerId: return handle( Channel, *this, &ThisT::handleDestroyIndirectStubsOwner); case EmitIndirectStubsId: return handle(Channel, *this, &ThisT::handleEmitIndirectStubs); case EmitResolverBlockId: return handle(Channel, *this, &ThisT::handleEmitResolverBlock); case EmitTrampolineBlockId: return handle(Channel, *this, &ThisT::handleEmitTrampolineBlock); case GetSymbolAddressId: return handle(Channel, *this, &ThisT::handleGetSymbolAddress); case GetRemoteInfoId: return handle(Channel, *this, &ThisT::handleGetRemoteInfo); case ReadMemId: return handle(Channel, *this, &ThisT::handleReadMem); case RegisterEHFramesId: return handle(Channel, *this, &ThisT::handleRegisterEHFrames); case ReserveMemId: return handle(Channel, *this, &ThisT::handleReserveMem); case SetProtectionsId: return handle(Channel, *this, &ThisT::handleSetProtections); case WriteMemId: return handle(Channel, *this, &ThisT::handleWriteMem); case WritePtrId: return handle(Channel, *this, &ThisT::handleWritePtr); default: return orcError(OrcErrorCode::UnexpectedRPCCall); } llvm_unreachable("Unhandled JIT RPC procedure Id."); } Expected requestCompile(TargetAddress TrampolineAddr) { auto Listen = [&](RPCChannel &C, uint32_t Id) { return handleKnownFunction(static_cast(Id)); }; return callSTHandling(Channel, Listen, TrampolineAddr); } Error handleTerminateSession() { return handle(Channel, []() { return Error::success(); }); } 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); } Error 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 errorCodeToError(EC); Addr = MB.base(); assert(Allocs.find(MB.base()) == Allocs.end() && "Duplicate alloc"); Allocs[MB.base()] = std::move(MB); return Error::success(); } Error setProtections(void *block, unsigned Flags) { auto I = Allocs.find(block); if (I == Allocs.end()) return orcError(OrcErrorCode::RemoteMProtectAddrUnrecognized); return errorCodeToError( sys::Memory::protectMappedMemory(I->second, Flags)); } private: std::map Allocs; }; static Error doNothing() { return Error::success(); } static TargetAddress reenter(void *JITTargetAddr, void *TrampolineAddr) { auto T = static_cast(JITTargetAddr); auto AddrOrErr = T->requestCompile(static_cast( reinterpret_cast(TrampolineAddr))); // FIXME: Allow customizable failure substitution functions. assert(AddrOrErr && "Compile request failed"); return *AddrOrErr; } Expected handleCallIntVoid(TargetAddress Addr) { typedef int (*IntVoidFnTy)(); IntVoidFnTy Fn = reinterpret_cast(static_cast(Addr)); DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n"); int Result = Fn(); DEBUG(dbgs() << " Result = " << Result << "\n"); return Result; } Expected handleCallMain(TargetAddress Addr, std::vector Args) { typedef int (*MainFnTy)(int, const char *[]); MainFnTy Fn = reinterpret_cast(static_cast(Addr)); int ArgC = Args.size() + 1; int Idx = 1; std::unique_ptr ArgV(new const char *[ArgC + 1]); ArgV[0] = ""; for (auto &Arg : Args) ArgV[Idx++] = Arg.c_str(); DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n"); int Result = Fn(ArgC, ArgV.get()); DEBUG(dbgs() << " Result = " << Result << "\n"); return Result; } Error handleCallVoidVoid(TargetAddress Addr) { typedef void (*VoidVoidFnTy)(); VoidVoidFnTy Fn = reinterpret_cast(static_cast(Addr)); DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n"); Fn(); DEBUG(dbgs() << " Complete.\n"); return Error::success(); } Error 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 Error::success(); } Error 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 Error::success(); } Error handleDeregisterEHFrames(TargetAddress TAddr, uint32_t Size) { uint8_t *Addr = reinterpret_cast(static_cast(TAddr)); DEBUG(dbgs() << " Registering EH frames at " << format("0x%016x", TAddr) << ", Size = " << Size << " bytes\n"); EHFramesDeregister(Addr, Size); return Error::success(); } Error 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 Error::success(); } Error handleDestroyIndirectStubsOwner(ResourceIdMgr::ResourceId Id) { auto I = IndirectStubsOwners.find(Id); if (I == IndirectStubsOwners.end()) return orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist); IndirectStubsOwners.erase(I); return Error::success(); } Expected> 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 Err = TargetT::emitIndirectStubsBlock(IS, NumStubsRequired, nullptr)) return std::move(Err); TargetAddress StubsBase = static_cast(reinterpret_cast(IS.getStub(0))); TargetAddress PtrsBase = static_cast(reinterpret_cast(IS.getPtr(0))); uint32_t NumStubsEmitted = IS.getNumStubs(); auto &BlockList = StubOwnerItr->second; BlockList.push_back(std::move(IS)); return std::make_tuple(StubsBase, PtrsBase, NumStubsEmitted); } Error 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 errorCodeToError(EC); TargetT::writeResolverCode(static_cast(ResolverBlock.base()), &reenter, this); return errorCodeToError(sys::Memory::protectMappedMemory( ResolverBlock.getMemoryBlock(), sys::Memory::MF_READ | sys::Memory::MF_EXEC)); } Expected> 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 errorCodeToError(EC); uint32_t NumTrampolines = (sys::Process::getPageSize() - TargetT::PointerSize) / TargetT::TrampolineSize; uint8_t *TrampolineMem = static_cast(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)); auto TrampolineBaseAddr = static_cast(reinterpret_cast(TrampolineMem)); return std::make_tuple(TrampolineBaseAddr, NumTrampolines); } Expected handleGetSymbolAddress(const std::string &Name) { TargetAddress Addr = SymbolLookup(Name); DEBUG(dbgs() << " Symbol '" << Name << "' = " << format("0x%016x", Addr) << "\n"); return Addr; } Expected> 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 std::make_tuple(ProcessTriple, PointerSize, PageSize, TrampolineSize, IndirectStubSize); } Expected> handleReadMem(TargetAddress RSrc, uint64_t Size) { char *Src = reinterpret_cast(static_cast(RSrc)); DEBUG(dbgs() << " Reading " << Size << " bytes from " << format("0x%016x", RSrc) << "\n"); std::vector Buffer; Buffer.resize(Size); for (char *P = Src; Size != 0; --Size) Buffer.push_back(*P++); return Buffer; } Error handleRegisterEHFrames(TargetAddress TAddr, uint32_t Size) { uint8_t *Addr = reinterpret_cast(static_cast(TAddr)); DEBUG(dbgs() << " Registering EH frames at " << format("0x%016x", TAddr) << ", Size = " << Size << " bytes\n"); EHFramesRegister(Addr, Size); return Error::success(); } Expected 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 Err = Allocator.allocate(LocalAllocAddr, Size, Align)) return std::move(Err); DEBUG(dbgs() << " Allocator " << Id << " reserved " << LocalAllocAddr << " (" << Size << " bytes, alignment " << Align << ")\n"); TargetAddress AllocAddr = static_cast(reinterpret_cast(LocalAllocAddr)); return AllocAddr; } Error 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(static_cast(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); } Error handleWriteMem(DirectBufferWriter DBW) { DEBUG(dbgs() << " Writing " << DBW.getSize() << " bytes to " << format("0x%016x", DBW.getDst()) << "\n"); return Error::success(); } Error handleWritePtr(TargetAddress Addr, TargetAddress PtrVal) { DEBUG(dbgs() << " Writing pointer *" << format("0x%016x", Addr) << " = " << format("0x%016x", PtrVal) << "\n"); uintptr_t *Ptr = reinterpret_cast(static_cast(Addr)); *Ptr = static_cast(PtrVal); return Error::success(); } ChannelT &Channel; SymbolLookupFtor SymbolLookup; EHFrameRegistrationFtor EHFramesRegister, EHFramesDeregister; std::map Allocators; typedef std::vector ISBlockOwnerList; std::map IndirectStubsOwners; sys::OwningMemoryBlock ResolverBlock; std::vector TrampolineBlocks; }; } // end namespace remote } // end namespace orc } // end namespace llvm #undef DEBUG_TYPE #endif