//===- OrcRemoteTargetServer.h - Orc Remote-target Server -------*- 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 // //===----------------------------------------------------------------------===// // // 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 "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/Orc/OrcError.h" #include "llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/Host.h" #include "llvm/Support/Memory.h" #include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG_TYPE "orc-remote" namespace llvm { namespace orc { namespace remote { template class OrcRemoteTargetServer : public rpc::SingleThreadedRPCEndpoint { public: using SymbolLookupFtor = std::function; using EHFrameRegistrationFtor = std::function; OrcRemoteTargetServer(ChannelT &Channel, SymbolLookupFtor SymbolLookup, EHFrameRegistrationFtor EHFramesRegister, EHFrameRegistrationFtor EHFramesDeregister) : rpc::SingleThreadedRPCEndpoint(Channel, true), SymbolLookup(std::move(SymbolLookup)), EHFramesRegister(std::move(EHFramesRegister)), EHFramesDeregister(std::move(EHFramesDeregister)) { using ThisT = typename std::remove_reference::type; addHandler(*this, &ThisT::handleCallIntVoid); addHandler(*this, &ThisT::handleCallMain); addHandler(*this, &ThisT::handleCallVoidVoid); addHandler(*this, &ThisT::handleCreateRemoteAllocator); addHandler( *this, &ThisT::handleDestroyRemoteAllocator); addHandler(*this, &ThisT::handleReadMem); addHandler(*this, &ThisT::handleReserveMem); addHandler(*this, &ThisT::handleSetProtections); addHandler(*this, &ThisT::handleWriteMem); addHandler(*this, &ThisT::handleWritePtr); addHandler(*this, &ThisT::handleRegisterEHFrames); addHandler(*this, &ThisT::handleDeregisterEHFrames); addHandler( *this, &ThisT::handleCreateIndirectStubsOwner); addHandler( *this, &ThisT::handleDestroyIndirectStubsOwner); addHandler(*this, &ThisT::handleEmitIndirectStubs); addHandler(*this, &ThisT::handleEmitResolverBlock); addHandler(*this, &ThisT::handleEmitTrampolineBlock); addHandler(*this, &ThisT::handleGetSymbolAddress); addHandler(*this, &ThisT::handleGetRemoteInfo); addHandler(*this, &ThisT::handleTerminateSession); } // FIXME: Remove move/copy ops once MSVC supports synthesizing move ops. OrcRemoteTargetServer(const OrcRemoteTargetServer &) = delete; OrcRemoteTargetServer &operator=(const OrcRemoteTargetServer &) = delete; OrcRemoteTargetServer(OrcRemoteTargetServer &&Other) = default; OrcRemoteTargetServer &operator=(OrcRemoteTargetServer &&) = delete; Expected requestCompile(JITTargetAddress TrampolineAddr) { return callB(TrampolineAddr); } bool receivedTerminate() const { return TerminateFlag; } 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 errorCodeToError(orcError(OrcErrorCode::RemoteMProtectAddrUnrecognized)); return errorCodeToError( sys::Memory::protectMappedMemory(I->second, Flags)); } private: std::map Allocs; }; static Error doNothing() { return Error::success(); } static JITTargetAddress 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(JITTargetAddress Addr) { using IntVoidFnTy = int (*)(); IntVoidFnTy Fn = reinterpret_cast(static_cast(Addr)); LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n"); int Result = Fn(); LLVM_DEBUG(dbgs() << " Result = " << Result << "\n"); return Result; } Expected handleCallMain(JITTargetAddress Addr, std::vector Args) { using MainFnTy = int (*)(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(); ArgV[ArgC] = 0; LLVM_DEBUG(for (int Idx = 0; Idx < ArgC; ++Idx) { llvm::dbgs() << "Arg " << Idx << ": " << ArgV[Idx] << "\n"; }); LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n"); int Result = Fn(ArgC, ArgV.get()); LLVM_DEBUG(dbgs() << " Result = " << Result << "\n"); return Result; } Error handleCallVoidVoid(JITTargetAddress Addr) { using VoidVoidFnTy = void (*)(); VoidVoidFnTy Fn = reinterpret_cast(static_cast(Addr)); LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n"); Fn(); LLVM_DEBUG(dbgs() << " Complete.\n"); return Error::success(); } Error handleCreateRemoteAllocator(ResourceIdMgr::ResourceId Id) { auto I = Allocators.find(Id); if (I != Allocators.end()) return errorCodeToError( orcError(OrcErrorCode::RemoteAllocatorIdAlreadyInUse)); LLVM_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 errorCodeToError( orcError(OrcErrorCode::RemoteIndirectStubsOwnerIdAlreadyInUse)); LLVM_DEBUG(dbgs() << " Create indirect stubs owner " << Id << "\n"); IndirectStubsOwners[Id] = ISBlockOwnerList(); return Error::success(); } Error handleDeregisterEHFrames(JITTargetAddress TAddr, uint32_t Size) { uint8_t *Addr = reinterpret_cast(static_cast(TAddr)); LLVM_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 errorCodeToError( orcError(OrcErrorCode::RemoteAllocatorDoesNotExist)); Allocators.erase(I); LLVM_DEBUG(dbgs() << " Destroyed allocator " << Id << "\n"); return Error::success(); } Error handleDestroyIndirectStubsOwner(ResourceIdMgr::ResourceId Id) { auto I = IndirectStubsOwners.find(Id); if (I == IndirectStubsOwners.end()) return errorCodeToError( orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist)); IndirectStubsOwners.erase(I); return Error::success(); } Expected> handleEmitIndirectStubs(ResourceIdMgr::ResourceId Id, uint32_t NumStubsRequired) { LLVM_DEBUG(dbgs() << " ISMgr " << Id << " request " << NumStubsRequired << " stubs.\n"); auto StubOwnerItr = IndirectStubsOwners.find(Id); if (StubOwnerItr == IndirectStubsOwners.end()) return errorCodeToError( orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist)); typename TargetT::IndirectStubsInfo IS; if (auto Err = TargetT::emitIndirectStubsBlock(IS, NumStubsRequired, nullptr)) return std::move(Err); JITTargetAddress StubsBase = static_cast( reinterpret_cast(IS.getStub(0))); JITTargetAddress 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::getPageSizeEstimate(), nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC)); if (EC) return errorCodeToError(EC); uint32_t NumTrampolines = (sys::Process::getPageSizeEstimate() - 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) { JITTargetAddress Addr = SymbolLookup(Name); LLVM_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::getPageSizeEstimate(); uint32_t TrampolineSize = TargetT::TrampolineSize; uint32_t IndirectStubSize = TargetT::IndirectStubsInfo::StubSize; LLVM_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(JITTargetAddress RSrc, uint64_t Size) { uint8_t *Src = reinterpret_cast(static_cast(RSrc)); LLVM_DEBUG(dbgs() << " Reading " << Size << " bytes from " << format("0x%016x", RSrc) << "\n"); std::vector Buffer; Buffer.resize(Size); for (uint8_t *P = Src; Size != 0; --Size) Buffer.push_back(*P++); return Buffer; } Error handleRegisterEHFrames(JITTargetAddress TAddr, uint32_t Size) { uint8_t *Addr = reinterpret_cast(static_cast(TAddr)); LLVM_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 errorCodeToError( orcError(OrcErrorCode::RemoteAllocatorDoesNotExist)); auto &Allocator = I->second; void *LocalAllocAddr = nullptr; if (auto Err = Allocator.allocate(LocalAllocAddr, Size, Align)) return std::move(Err); LLVM_DEBUG(dbgs() << " Allocator " << Id << " reserved " << LocalAllocAddr << " (" << Size << " bytes, alignment " << Align << ")\n"); JITTargetAddress AllocAddr = static_cast( reinterpret_cast(LocalAllocAddr)); return AllocAddr; } Error handleSetProtections(ResourceIdMgr::ResourceId Id, JITTargetAddress Addr, uint32_t Flags) { auto I = Allocators.find(Id); if (I == Allocators.end()) return errorCodeToError( orcError(OrcErrorCode::RemoteAllocatorDoesNotExist)); auto &Allocator = I->second; void *LocalAddr = reinterpret_cast(static_cast(Addr)); LLVM_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 handleTerminateSession() { TerminateFlag = true; return Error::success(); } Error handleWriteMem(DirectBufferWriter DBW) { LLVM_DEBUG(dbgs() << " Writing " << DBW.getSize() << " bytes to " << format("0x%016x", DBW.getDst()) << "\n"); return Error::success(); } Error handleWritePtr(JITTargetAddress Addr, JITTargetAddress PtrVal) { LLVM_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(); } SymbolLookupFtor SymbolLookup; EHFrameRegistrationFtor EHFramesRegister, EHFramesDeregister; std::map Allocators; using ISBlockOwnerList = std::vector; std::map IndirectStubsOwners; sys::OwningMemoryBlock ResolverBlock; std::vector TrampolineBlocks; bool TerminateFlag = false; }; } // end namespace remote } // end namespace orc } // end namespace llvm #undef DEBUG_TYPE #endif // LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H