//===-- OrcRPCTPCServer.h -- OrcRPCTargetProcessControl 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 // //===----------------------------------------------------------------------===// // // OrcRPCTargetProcessControl server class. // //===----------------------------------------------------------------------===// #ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_ORCRPCTPCSERVER_H #define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_ORCRPCTPCSERVER_H #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h" #include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h" #include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/TargetProcessControl.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Host.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Memory.h" #include "llvm/Support/Process.h" #include namespace llvm { namespace orc { namespace orcrpctpc { enum WireProtectionFlags : uint8_t { WPF_None = 0, WPF_Read = 1U << 0, WPF_Write = 1U << 1, WPF_Exec = 1U << 2, LLVM_MARK_AS_BITMASK_ENUM(WPF_Exec) }; /// Convert from sys::Memory::ProtectionFlags inline WireProtectionFlags toWireProtectionFlags(sys::Memory::ProtectionFlags PF) { WireProtectionFlags WPF = WPF_None; if (PF & sys::Memory::MF_READ) WPF |= WPF_Read; if (PF & sys::Memory::MF_WRITE) WPF |= WPF_Write; if (PF & sys::Memory::MF_EXEC) WPF |= WPF_Exec; return WPF; } inline sys::Memory::ProtectionFlags fromWireProtectionFlags(WireProtectionFlags WPF) { int PF = 0; if (WPF & WPF_Read) PF |= sys::Memory::MF_READ; if (WPF & WPF_Write) PF |= sys::Memory::MF_WRITE; if (WPF & WPF_Exec) PF |= sys::Memory::MF_EXEC; return static_cast(PF); } struct ReserveMemRequestElement { WireProtectionFlags Prot = WPF_None; uint64_t Size = 0; uint64_t Alignment = 0; }; using ReserveMemRequest = std::vector; struct ReserveMemResultElement { WireProtectionFlags Prot = WPF_None; JITTargetAddress Address = 0; uint64_t AllocatedSize = 0; }; using ReserveMemResult = std::vector; struct ReleaseOrFinalizeMemRequestElement { WireProtectionFlags Prot = WPF_None; JITTargetAddress Address = 0; uint64_t Size = 0; }; using ReleaseOrFinalizeMemRequest = std::vector; } // end namespace orcrpctpc namespace shared { template <> class SerializationTypeName { public: static const char *getName() { return "UInt8Write"; } }; template <> class SerializationTypeName { public: static const char *getName() { return "UInt16Write"; } }; template <> class SerializationTypeName { public: static const char *getName() { return "UInt32Write"; } }; template <> class SerializationTypeName { public: static const char *getName() { return "UInt64Write"; } }; template <> class SerializationTypeName { public: static const char *getName() { return "BufferWrite"; } }; template <> class SerializationTypeName { public: static const char *getName() { return "ReserveMemRequestElement"; } }; template <> class SerializationTypeName { public: static const char *getName() { return "ReserveMemResultElement"; } }; template <> class SerializationTypeName { public: static const char *getName() { return "ReleaseOrFinalizeMemRequestElement"; } }; template <> class SerializationTypeName { public: static const char *getName() { return "WrapperFunctionResult"; } }; template class SerializationTraits< ChannelT, WriteT, WriteT, std::enable_if_t::value || std::is_same::value || std::is_same::value || std::is_same::value>> { public: static Error serialize(ChannelT &C, const WriteT &W) { return serializeSeq(C, W.Address, W.Value); } static Error deserialize(ChannelT &C, WriteT &W) { return deserializeSeq(C, W.Address, W.Value); } }; template class SerializationTraits< ChannelT, tpctypes::BufferWrite, tpctypes::BufferWrite, std::enable_if_t::value>> { public: static Error serialize(ChannelT &C, const tpctypes::BufferWrite &W) { uint64_t Size = W.Buffer.size(); if (auto Err = serializeSeq(C, W.Address, Size)) return Err; return C.appendBytes(W.Buffer.data(), Size); } static Error deserialize(ChannelT &C, tpctypes::BufferWrite &W) { JITTargetAddress Address; uint64_t Size; if (auto Err = deserializeSeq(C, Address, Size)) return Err; char *Buffer = jitTargetAddressToPointer(Address); if (auto Err = C.readBytes(Buffer, Size)) return Err; W = {Address, StringRef(Buffer, Size)}; return Error::success(); } }; template class SerializationTraits { public: static Error serialize(ChannelT &C, const orcrpctpc::ReserveMemRequestElement &E) { return serializeSeq(C, static_cast(E.Prot), E.Size, E.Alignment); } static Error deserialize(ChannelT &C, orcrpctpc::ReserveMemRequestElement &E) { return deserializeSeq(C, *reinterpret_cast(&E.Prot), E.Size, E.Alignment); } }; template class SerializationTraits { public: static Error serialize(ChannelT &C, const orcrpctpc::ReserveMemResultElement &E) { return serializeSeq(C, static_cast(E.Prot), E.Address, E.AllocatedSize); } static Error deserialize(ChannelT &C, orcrpctpc::ReserveMemResultElement &E) { return deserializeSeq(C, *reinterpret_cast(&E.Prot), E.Address, E.AllocatedSize); } }; template class SerializationTraits { public: static Error serialize(ChannelT &C, const orcrpctpc::ReleaseOrFinalizeMemRequestElement &E) { return serializeSeq(C, static_cast(E.Prot), E.Address, E.Size); } static Error deserialize(ChannelT &C, orcrpctpc::ReleaseOrFinalizeMemRequestElement &E) { return deserializeSeq(C, *reinterpret_cast(&E.Prot), E.Address, E.Size); } }; template class SerializationTraits< ChannelT, tpctypes::WrapperFunctionResult, tpctypes::WrapperFunctionResult, std::enable_if_t::value>> { public: static Error serialize(ChannelT &C, const tpctypes::WrapperFunctionResult &E) { auto Data = E.getData(); if (auto Err = serializeSeq(C, static_cast(Data.size()))) return Err; if (Data.size() == 0) return Error::success(); return C.appendBytes(reinterpret_cast(Data.data()), Data.size()); } static Error deserialize(ChannelT &C, tpctypes::WrapperFunctionResult &E) { tpctypes::CWrapperFunctionResult R; R.Size = 0; R.Data.ValuePtr = nullptr; R.Destroy = nullptr; if (auto Err = deserializeSeq(C, R.Size)) return Err; if (R.Size == 0) return Error::success(); R.Data.ValuePtr = new uint8_t[R.Size]; if (auto Err = C.readBytes(reinterpret_cast(R.Data.ValuePtr), R.Size)) { R.Destroy = tpctypes::WrapperFunctionResult::destroyWithDeleteArray; return Err; } E = tpctypes::WrapperFunctionResult(R); return Error::success(); } }; } // end namespace shared namespace orcrpctpc { using RemoteSymbolLookupSet = std::vector>; using RemoteLookupRequest = std::pair; class GetTargetTriple : public shared::RPCFunction { public: static const char *getName() { return "GetTargetTriple"; } }; class GetPageSize : public shared::RPCFunction { public: static const char *getName() { return "GetPageSize"; } }; class ReserveMem : public shared::RPCFunction( ReserveMemRequest)> { public: static const char *getName() { return "ReserveMem"; } }; class FinalizeMem : public shared::RPCFunction { public: static const char *getName() { return "FinalizeMem"; } }; class ReleaseMem : public shared::RPCFunction { public: static const char *getName() { return "ReleaseMem"; } }; class WriteUInt8s : public shared::RPCFunction)> { public: static const char *getName() { return "WriteUInt8s"; } }; class WriteUInt16s : public shared::RPCFunction)> { public: static const char *getName() { return "WriteUInt16s"; } }; class WriteUInt32s : public shared::RPCFunction)> { public: static const char *getName() { return "WriteUInt32s"; } }; class WriteUInt64s : public shared::RPCFunction)> { public: static const char *getName() { return "WriteUInt64s"; } }; class WriteBuffers : public shared::RPCFunction)> { public: static const char *getName() { return "WriteBuffers"; } }; class LoadDylib : public shared::RPCFunction( std::string DylibPath)> { public: static const char *getName() { return "LoadDylib"; } }; class LookupSymbols : public shared::RPCFunction>( std::vector)> { public: static const char *getName() { return "LookupSymbols"; } }; class RunMain : public shared::RPCFunction Args)> { public: static const char *getName() { return "RunMain"; } }; class RunWrapper : public shared::RPCFunction)> { public: static const char *getName() { return "RunWrapper"; } }; class CloseConnection : public shared::RPCFunction { public: static const char *getName() { return "CloseConnection"; } }; } // end namespace orcrpctpc /// TargetProcessControl for a process connected via an ORC RPC Endpoint. template class OrcRPCTPCServer { public: /// Create an OrcRPCTPCServer from the given endpoint. OrcRPCTPCServer(RPCEndpointT &EP) : EP(EP) { using ThisT = OrcRPCTPCServer; TripleStr = sys::getProcessTriple(); PageSize = sys::Process::getPageSizeEstimate(); EP.template addHandler(*this, &ThisT::getTargetTriple); EP.template addHandler(*this, &ThisT::getPageSize); EP.template addHandler(*this, &ThisT::reserveMemory); EP.template addHandler(*this, &ThisT::finalizeMemory); EP.template addHandler(*this, &ThisT::releaseMemory); EP.template addHandler( handleWriteUInt); EP.template addHandler( handleWriteUInt); EP.template addHandler( handleWriteUInt); EP.template addHandler( handleWriteUInt); EP.template addHandler(handleWriteBuffer); EP.template addHandler(*this, &ThisT::loadDylib); EP.template addHandler(*this, &ThisT::lookupSymbols); EP.template addHandler(*this, &ThisT::runMain); EP.template addHandler(*this, &ThisT::runWrapper); EP.template addHandler(*this, &ThisT::closeConnection); } /// Set the ProgramName to be used as the first argv element when running /// functions via runAsMain. void setProgramName(Optional ProgramName = None) { this->ProgramName = std::move(ProgramName); } /// Get the RPC endpoint for this server. RPCEndpointT &getEndpoint() { return EP; } /// Run the server loop. Error run() { while (!Finished) { if (auto Err = EP.handleOne()) return Err; } return Error::success(); } private: std::string getTargetTriple() { return TripleStr; } uint64_t getPageSize() { return PageSize; } template static void handleWriteUInt(const std::vector &Ws) { using ValueT = decltype(std::declval().Value); for (auto &W : Ws) *jitTargetAddressToPointer(W.Address) = W.Value; } std::string getProtStr(orcrpctpc::WireProtectionFlags WPF) { std::string Result; Result += (WPF & orcrpctpc::WPF_Read) ? 'R' : '-'; Result += (WPF & orcrpctpc::WPF_Write) ? 'W' : '-'; Result += (WPF & orcrpctpc::WPF_Exec) ? 'X' : '-'; return Result; } static void handleWriteBuffer(const std::vector &Ws) { for (auto &W : Ws) { memcpy(jitTargetAddressToPointer(W.Address), W.Buffer.data(), W.Buffer.size()); } } Expected reserveMemory(const orcrpctpc::ReserveMemRequest &Request) { orcrpctpc::ReserveMemResult Allocs; auto PF = sys::Memory::MF_READ | sys::Memory::MF_WRITE; uint64_t TotalSize = 0; for (const auto &E : Request) { uint64_t Size = alignTo(E.Size, PageSize); uint16_t Align = E.Alignment; if ((Align > PageSize) || (PageSize % Align)) return make_error( "Page alignmen does not satisfy requested alignment", inconvertibleErrorCode()); TotalSize += Size; } // Allocate memory slab. std::error_code EC; auto MB = sys::Memory::allocateMappedMemory(TotalSize, nullptr, PF, EC); if (EC) return make_error("Unable to allocate memory: " + EC.message(), inconvertibleErrorCode()); // Zero-fill the whole thing. memset(MB.base(), 0, MB.allocatedSize()); // Carve up sections to return. uint64_t SectionBase = 0; for (const auto &E : Request) { uint64_t SectionSize = alignTo(E.Size, PageSize); Allocs.push_back({E.Prot, pointerToJITTargetAddress(MB.base()) + SectionBase, SectionSize}); SectionBase += SectionSize; } return Allocs; } Error finalizeMemory(const orcrpctpc::ReleaseOrFinalizeMemRequest &FMR) { for (const auto &E : FMR) { sys::MemoryBlock MB(jitTargetAddressToPointer(E.Address), E.Size); auto PF = orcrpctpc::fromWireProtectionFlags(E.Prot); if (auto EC = sys::Memory::protectMappedMemory(MB, static_cast(PF))) return make_error("error protecting memory: " + EC.message(), inconvertibleErrorCode()); } return Error::success(); } Error releaseMemory(const orcrpctpc::ReleaseOrFinalizeMemRequest &RMR) { for (const auto &E : RMR) { sys::MemoryBlock MB(jitTargetAddressToPointer(E.Address), E.Size); if (auto EC = sys::Memory::releaseMappedMemory(MB)) return make_error("error release memory: " + EC.message(), inconvertibleErrorCode()); } return Error::success(); } Expected loadDylib(const std::string &Path) { std::string ErrMsg; const char *DLPath = !Path.empty() ? Path.c_str() : nullptr; auto DL = sys::DynamicLibrary::getPermanentLibrary(DLPath, &ErrMsg); if (!DL.isValid()) return make_error(std::move(ErrMsg), inconvertibleErrorCode()); tpctypes::DylibHandle H = Dylibs.size(); Dylibs[H] = std::move(DL); return H; } Expected> lookupSymbols(const std::vector &Request) { std::vector Result; for (const auto &E : Request) { auto I = Dylibs.find(E.first); if (I == Dylibs.end()) return make_error("Unrecognized handle", inconvertibleErrorCode()); auto &DL = I->second; Result.push_back({}); for (const auto &KV : E.second) { auto &SymString = KV.first; bool WeakReference = KV.second; const char *Sym = SymString.c_str(); #ifdef __APPLE__ if (*Sym == '_') ++Sym; #endif void *Addr = DL.getAddressOfSymbol(Sym); if (!Addr && !WeakReference) return make_error(Twine("Missing definition for ") + Sym, inconvertibleErrorCode()); Result.back().push_back(pointerToJITTargetAddress(Addr)); } } return Result; } int32_t runMain(JITTargetAddress MainFnAddr, const std::vector &Args) { Optional ProgramNameOverride; if (ProgramName) ProgramNameOverride = *ProgramName; return runAsMain( jitTargetAddressToFunction(MainFnAddr), Args, ProgramNameOverride); } tpctypes::WrapperFunctionResult runWrapper(JITTargetAddress WrapperFnAddr, const std::vector &ArgBuffer) { using WrapperFnTy = tpctypes::CWrapperFunctionResult (*)( const uint8_t *Data, uint64_t Size); auto *WrapperFn = jitTargetAddressToFunction(WrapperFnAddr); return WrapperFn(ArgBuffer.data(), ArgBuffer.size()); } void closeConnection() { Finished = true; } std::string TripleStr; uint64_t PageSize = 0; Optional ProgramName; RPCEndpointT &EP; std::atomic Finished{false}; DenseMap Dylibs; }; } // end namespace orc } // end namespace llvm #endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_ORCRPCTPCSERVER_H