1 //===---- OrcRemoteTargetServer.h - Orc Remote-target Server ----*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines the OrcRemoteTargetServer class. It can be used to build a
11 // JIT server that can execute code sent from an OrcRemoteTargetClient.
13 //===----------------------------------------------------------------------===//
15 #ifndef LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
16 #define LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
18 #include "OrcRemoteTargetRPCAPI.h"
19 #include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
20 #include "llvm/Support/Debug.h"
21 #include "llvm/Support/Format.h"
22 #include "llvm/Support/Process.h"
23 #include "llvm/Support/raw_ostream.h"
26 #define DEBUG_TYPE "orc-remote"
32 template <typename ChannelT, typename TargetT>
33 class OrcRemoteTargetServer : public OrcRemoteTargetRPCAPI {
35 typedef std::function<TargetAddress(const std::string &Name)>
38 OrcRemoteTargetServer(ChannelT &Channel, SymbolLookupFtor SymbolLookup)
39 : Channel(Channel), SymbolLookup(std::move(SymbolLookup)) {}
41 std::error_code getNextProcId(JITProcId &Id) {
42 return deserialize(Channel, Id);
45 std::error_code handleKnownProcedure(JITProcId Id) {
46 DEBUG(dbgs() << "Handling known proc: " << getJITProcIdName(Id) << "\n");
50 return handleCallIntVoid();
52 return handleCallMain();
54 return handleCallVoidVoid();
55 case CreateRemoteAllocatorId:
56 return handleCreateRemoteAllocator();
57 case CreateIndirectStubsOwnerId:
58 return handleCreateIndirectStubsOwner();
59 case DestroyRemoteAllocatorId:
60 return handleDestroyRemoteAllocator();
61 case EmitIndirectStubsId:
62 return handleEmitIndirectStubs();
63 case EmitResolverBlockId:
64 return handleEmitResolverBlock();
65 case EmitTrampolineBlockId:
66 return handleEmitTrampolineBlock();
67 case GetSymbolAddressId:
68 return handleGetSymbolAddress();
70 return handleGetRemoteInfo();
72 return handleReadMem();
74 return handleReserveMem();
75 case SetProtectionsId:
76 return handleSetProtections();
78 return handleWriteMem();
80 return handleWritePtr();
82 return orcError(OrcErrorCode::UnexpectedRPCCall);
85 llvm_unreachable("Unhandled JIT RPC procedure Id.");
88 std::error_code requestCompile(TargetAddress &CompiledFnAddr,
89 TargetAddress TrampolineAddr) {
90 if (auto EC = call<RequestCompile>(Channel, TrampolineAddr))
94 JITProcId Id = InvalidId;
95 if (auto EC = getNextProcId(Id))
99 case RequestCompileResponseId:
100 return handle<RequestCompileResponse>(Channel,
101 readArgs(CompiledFnAddr));
103 if (auto EC = handleKnownProcedure(Id))
108 llvm_unreachable("Fell through request-compile command loop.");
113 Allocator() = default;
114 Allocator(Allocator &&Other) : Allocs(std::move(Other.Allocs)) {}
115 Allocator &operator=(Allocator &&Other) {
116 Allocs = std::move(Other.Allocs);
121 for (auto &Alloc : Allocs)
122 sys::Memory::releaseMappedMemory(Alloc.second);
125 std::error_code allocate(void *&Addr, size_t Size, uint32_t Align) {
127 sys::MemoryBlock MB = sys::Memory::allocateMappedMemory(
128 Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
133 assert(Allocs.find(MB.base()) == Allocs.end() && "Duplicate alloc");
134 Allocs[MB.base()] = std::move(MB);
135 return std::error_code();
138 std::error_code setProtections(void *block, unsigned Flags) {
139 auto I = Allocs.find(block);
140 if (I == Allocs.end())
141 return orcError(OrcErrorCode::RemoteMProtectAddrUnrecognized);
142 return sys::Memory::protectMappedMemory(I->second, Flags);
146 std::map<void *, sys::MemoryBlock> Allocs;
149 static std::error_code doNothing() { return std::error_code(); }
151 static TargetAddress reenter(void *JITTargetAddr, void *TrampolineAddr) {
152 TargetAddress CompiledFnAddr = 0;
154 auto T = static_cast<OrcRemoteTargetServer *>(JITTargetAddr);
155 auto EC = T->requestCompile(
156 CompiledFnAddr, static_cast<TargetAddress>(
157 reinterpret_cast<uintptr_t>(TrampolineAddr)));
158 assert(!EC && "Compile request failed");
159 return CompiledFnAddr;
162 std::error_code handleCallIntVoid() {
163 typedef int (*IntVoidFnTy)();
165 IntVoidFnTy Fn = nullptr;
166 if (std::error_code EC =
167 handle<CallIntVoid>(Channel, [&](TargetAddress Addr) {
168 Fn = reinterpret_cast<IntVoidFnTy>(static_cast<uintptr_t>(Addr));
169 return std::error_code();
173 DEBUG(dbgs() << " Calling "
174 << reinterpret_cast<void *>(reinterpret_cast<intptr_t>(Fn))
177 DEBUG(dbgs() << " Result = " << Result << "\n");
179 return call<CallIntVoidResponse>(Channel, Result);
182 std::error_code handleCallMain() {
183 typedef int (*MainFnTy)(int, const char *[]);
185 MainFnTy Fn = nullptr;
186 std::vector<std::string> Args;
187 if (std::error_code EC = handle<CallMain>(
188 Channel, [&](TargetAddress Addr, std::vector<std::string> &A) {
189 Fn = reinterpret_cast<MainFnTy>(static_cast<uintptr_t>(Addr));
191 return std::error_code();
195 int ArgC = Args.size() + 1;
197 std::unique_ptr<const char *[]> ArgV(new const char *[ArgC + 1]);
198 ArgV[0] = "<jit process>";
199 for (auto &Arg : Args)
200 ArgV[Idx++] = Arg.c_str();
202 DEBUG(dbgs() << " Calling " << reinterpret_cast<void *>(Fn) << "\n");
203 int Result = Fn(ArgC, ArgV.get());
204 DEBUG(dbgs() << " Result = " << Result << "\n");
206 return call<CallMainResponse>(Channel, Result);
209 std::error_code handleCallVoidVoid() {
210 typedef void (*VoidVoidFnTy)();
212 VoidVoidFnTy Fn = nullptr;
213 if (std::error_code EC =
214 handle<CallIntVoid>(Channel, [&](TargetAddress Addr) {
215 Fn = reinterpret_cast<VoidVoidFnTy>(static_cast<uintptr_t>(Addr));
216 return std::error_code();
220 DEBUG(dbgs() << " Calling " << reinterpret_cast<void *>(Fn) << "\n");
222 DEBUG(dbgs() << " Complete.\n");
224 return call<CallVoidVoidResponse>(Channel);
227 std::error_code handleCreateRemoteAllocator() {
228 return handle<CreateRemoteAllocator>(
229 Channel, [&](ResourceIdMgr::ResourceId Id) {
230 auto I = Allocators.find(Id);
231 if (I != Allocators.end())
232 return orcError(OrcErrorCode::RemoteAllocatorIdAlreadyInUse);
233 DEBUG(dbgs() << " Created allocator " << Id << "\n");
234 Allocators[Id] = Allocator();
235 return std::error_code();
239 std::error_code handleCreateIndirectStubsOwner() {
240 return handle<CreateIndirectStubsOwner>(
241 Channel, [&](ResourceIdMgr::ResourceId Id) {
242 auto I = IndirectStubsOwners.find(Id);
243 if (I != IndirectStubsOwners.end())
245 OrcErrorCode::RemoteIndirectStubsOwnerIdAlreadyInUse);
246 DEBUG(dbgs() << " Create indirect stubs owner " << Id << "\n");
247 IndirectStubsOwners[Id] = ISBlockOwnerList();
248 return std::error_code();
252 std::error_code handleDestroyRemoteAllocator() {
253 return handle<DestroyRemoteAllocator>(
254 Channel, [&](ResourceIdMgr::ResourceId Id) {
255 auto I = Allocators.find(Id);
256 if (I == Allocators.end())
257 return orcError(OrcErrorCode::RemoteAllocatorDoesNotExist);
259 DEBUG(dbgs() << " Destroyed allocator " << Id << "\n");
260 return std::error_code();
264 std::error_code handleDestroyIndirectStubsOwner() {
265 return handle<DestroyIndirectStubsOwner>(
266 Channel, [&](ResourceIdMgr::ResourceId Id) {
267 auto I = IndirectStubsOwners.find(Id);
268 if (I == IndirectStubsOwners.end())
269 return orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist);
270 IndirectStubsOwners.erase(I);
271 return std::error_code();
275 std::error_code handleEmitIndirectStubs() {
276 ResourceIdMgr::ResourceId ISOwnerId = ~0U;
277 uint32_t NumStubsRequired = 0;
279 if (auto EC = handle<EmitIndirectStubs>(
280 Channel, readArgs(ISOwnerId, NumStubsRequired)))
283 DEBUG(dbgs() << " ISMgr " << ISOwnerId << " request " << NumStubsRequired
286 auto StubOwnerItr = IndirectStubsOwners.find(ISOwnerId);
287 if (StubOwnerItr == IndirectStubsOwners.end())
288 return orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist);
290 typename TargetT::IndirectStubsInfo IS;
292 TargetT::emitIndirectStubsBlock(IS, NumStubsRequired, nullptr))
295 TargetAddress StubsBase =
296 static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(IS.getStub(0)));
297 TargetAddress PtrsBase =
298 static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(IS.getPtr(0)));
299 uint32_t NumStubsEmitted = IS.getNumStubs();
301 auto &BlockList = StubOwnerItr->second;
302 BlockList.push_back(std::move(IS));
304 return call<EmitIndirectStubsResponse>(Channel, StubsBase, PtrsBase,
308 std::error_code handleEmitResolverBlock() {
309 if (auto EC = handle<EmitResolverBlock>(Channel, doNothing))
313 ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
314 TargetT::ResolverCodeSize, nullptr,
315 sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
319 TargetT::writeResolverCode(static_cast<uint8_t *>(ResolverBlock.base()),
322 return sys::Memory::protectMappedMemory(ResolverBlock.getMemoryBlock(),
323 sys::Memory::MF_READ |
324 sys::Memory::MF_EXEC);
327 std::error_code handleEmitTrampolineBlock() {
328 if (auto EC = handle<EmitTrampolineBlock>(Channel, doNothing))
333 auto TrampolineBlock =
334 sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
335 sys::Process::getPageSize(), nullptr,
336 sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
340 unsigned NumTrampolines =
341 (sys::Process::getPageSize() - TargetT::PointerSize) /
342 TargetT::TrampolineSize;
344 uint8_t *TrampolineMem = static_cast<uint8_t *>(TrampolineBlock.base());
345 TargetT::writeTrampolines(TrampolineMem, ResolverBlock.base(),
348 EC = sys::Memory::protectMappedMemory(TrampolineBlock.getMemoryBlock(),
349 sys::Memory::MF_READ |
350 sys::Memory::MF_EXEC);
352 TrampolineBlocks.push_back(std::move(TrampolineBlock));
354 return call<EmitTrampolineBlockResponse>(
356 static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(TrampolineMem)),
360 std::error_code handleGetSymbolAddress() {
361 std::string SymbolName;
362 if (auto EC = handle<GetSymbolAddress>(Channel, readArgs(SymbolName)))
365 TargetAddress SymbolAddr = SymbolLookup(SymbolName);
366 DEBUG(dbgs() << " Symbol '" << SymbolName
367 << "' = " << format("0x%016x", SymbolAddr) << "\n");
368 return call<GetSymbolAddressResponse>(Channel, SymbolAddr);
371 std::error_code handleGetRemoteInfo() {
372 if (auto EC = handle<GetRemoteInfo>(Channel, doNothing))
375 std::string ProcessTriple = sys::getProcessTriple();
376 uint32_t PointerSize = TargetT::PointerSize;
377 uint32_t PageSize = sys::Process::getPageSize();
378 uint32_t TrampolineSize = TargetT::TrampolineSize;
379 uint32_t IndirectStubSize = TargetT::IndirectStubsInfo::StubSize;
380 DEBUG(dbgs() << " Remote info:\n"
381 << " triple = '" << ProcessTriple << "'\n"
382 << " pointer size = " << PointerSize << "\n"
383 << " page size = " << PageSize << "\n"
384 << " trampoline size = " << TrampolineSize << "\n"
385 << " indirect stub size = " << IndirectStubSize << "\n");
386 return call<GetRemoteInfoResponse>(Channel, ProcessTriple, PointerSize,
387 PageSize, TrampolineSize,
391 std::error_code handleReadMem() {
394 if (std::error_code EC =
395 handle<ReadMem>(Channel, [&](TargetAddress RSrc, uint64_t RSize) {
396 Src = reinterpret_cast<char *>(static_cast<uintptr_t>(RSrc));
398 return std::error_code();
402 DEBUG(dbgs() << " Reading " << Size << " bytes from "
403 << static_cast<void *>(Src) << "\n");
405 if (auto EC = call<ReadMemResponse>(Channel))
408 if (auto EC = Channel.appendBytes(Src, Size))
411 return Channel.send();
414 std::error_code handleReserveMem() {
415 void *LocalAllocAddr = nullptr;
417 if (std::error_code EC =
418 handle<ReserveMem>(Channel, [&](ResourceIdMgr::ResourceId Id,
419 uint64_t Size, uint32_t Align) {
420 auto I = Allocators.find(Id);
421 if (I == Allocators.end())
422 return orcError(OrcErrorCode::RemoteAllocatorDoesNotExist);
423 auto &Allocator = I->second;
424 auto EC2 = Allocator.allocate(LocalAllocAddr, Size, Align);
425 DEBUG(dbgs() << " Allocator " << Id << " reserved "
426 << LocalAllocAddr << " (" << Size
427 << " bytes, alignment " << Align << ")\n");
432 TargetAddress AllocAddr =
433 static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(LocalAllocAddr));
435 return call<ReserveMemResponse>(Channel, AllocAddr);
438 std::error_code handleSetProtections() {
439 return handle<ReserveMem>(Channel, [&](ResourceIdMgr::ResourceId Id,
440 TargetAddress Addr, uint32_t Flags) {
441 auto I = Allocators.find(Id);
442 if (I == Allocators.end())
443 return orcError(OrcErrorCode::RemoteAllocatorDoesNotExist);
444 auto &Allocator = I->second;
445 void *LocalAddr = reinterpret_cast<void *>(static_cast<uintptr_t>(Addr));
446 DEBUG(dbgs() << " Allocator " << Id << " set permissions on "
447 << LocalAddr << " to "
448 << (Flags & sys::Memory::MF_READ ? 'R' : '-')
449 << (Flags & sys::Memory::MF_WRITE ? 'W' : '-')
450 << (Flags & sys::Memory::MF_EXEC ? 'X' : '-') << "\n");
451 return Allocator.setProtections(LocalAddr, Flags);
455 std::error_code handleWriteMem() {
456 return handle<WriteMem>(Channel, [&](TargetAddress RDst, uint64_t Size) {
457 char *Dst = reinterpret_cast<char *>(static_cast<uintptr_t>(RDst));
458 return Channel.readBytes(Dst, Size);
462 std::error_code handleWritePtr() {
463 return handle<WritePtr>(
464 Channel, [&](TargetAddress Addr, TargetAddress PtrVal) {
466 reinterpret_cast<uintptr_t *>(static_cast<uintptr_t>(Addr));
467 *Ptr = static_cast<uintptr_t>(PtrVal);
468 return std::error_code();
473 SymbolLookupFtor SymbolLookup;
474 std::map<ResourceIdMgr::ResourceId, Allocator> Allocators;
475 typedef std::vector<typename TargetT::IndirectStubsInfo> ISBlockOwnerList;
476 std::map<ResourceIdMgr::ResourceId, ISBlockOwnerList> IndirectStubsOwners;
477 sys::OwningMemoryBlock ResolverBlock;
478 std::vector<sys::OwningMemoryBlock> TrampolineBlocks;
481 } // end namespace remote
482 } // end namespace orc
483 } // end namespace llvm