1 //===- OrcRemoteTargetServer.h - Orc Remote-target Server -------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
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.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
16 #define LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
17 
18 #include "llvm/ExecutionEngine/JITSymbol.h"
19 #include "llvm/ExecutionEngine/Orc/OrcError.h"
20 #include "llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h"
21 #include "llvm/Support/Debug.h"
22 #include "llvm/Support/Error.h"
23 #include "llvm/Support/Format.h"
24 #include "llvm/Support/Host.h"
25 #include "llvm/Support/Memory.h"
26 #include "llvm/Support/Process.h"
27 #include "llvm/Support/raw_ostream.h"
28 #include <algorithm>
29 #include <cassert>
30 #include <cstddef>
31 #include <cstdint>
32 #include <functional>
33 #include <map>
34 #include <memory>
35 #include <string>
36 #include <system_error>
37 #include <tuple>
38 #include <type_traits>
39 #include <vector>
40 
41 #define DEBUG_TYPE "orc-remote"
42 
43 namespace llvm {
44 namespace orc {
45 namespace remote {
46 
47 template <typename ChannelT, typename TargetT>
48 class OrcRemoteTargetServer
49     : public rpc::SingleThreadedRPCEndpoint<rpc::RawByteChannel> {
50 public:
51   using SymbolLookupFtor =
52       std::function<JITTargetAddress(const std::string &Name)>;
53 
54   using EHFrameRegistrationFtor =
55       std::function<void(uint8_t *Addr, uint32_t Size)>;
56 
OrcRemoteTargetServer(ChannelT & Channel,SymbolLookupFtor SymbolLookup,EHFrameRegistrationFtor EHFramesRegister,EHFrameRegistrationFtor EHFramesDeregister)57   OrcRemoteTargetServer(ChannelT &Channel, SymbolLookupFtor SymbolLookup,
58                         EHFrameRegistrationFtor EHFramesRegister,
59                         EHFrameRegistrationFtor EHFramesDeregister)
60       : rpc::SingleThreadedRPCEndpoint<rpc::RawByteChannel>(Channel, true),
61         SymbolLookup(std::move(SymbolLookup)),
62         EHFramesRegister(std::move(EHFramesRegister)),
63         EHFramesDeregister(std::move(EHFramesDeregister)) {
64     using ThisT = typename std::remove_reference<decltype(*this)>::type;
65     addHandler<exec::CallIntVoid>(*this, &ThisT::handleCallIntVoid);
66     addHandler<exec::CallMain>(*this, &ThisT::handleCallMain);
67     addHandler<exec::CallVoidVoid>(*this, &ThisT::handleCallVoidVoid);
68     addHandler<mem::CreateRemoteAllocator>(*this,
69                                            &ThisT::handleCreateRemoteAllocator);
70     addHandler<mem::DestroyRemoteAllocator>(
71         *this, &ThisT::handleDestroyRemoteAllocator);
72     addHandler<mem::ReadMem>(*this, &ThisT::handleReadMem);
73     addHandler<mem::ReserveMem>(*this, &ThisT::handleReserveMem);
74     addHandler<mem::SetProtections>(*this, &ThisT::handleSetProtections);
75     addHandler<mem::WriteMem>(*this, &ThisT::handleWriteMem);
76     addHandler<mem::WritePtr>(*this, &ThisT::handleWritePtr);
77     addHandler<eh::RegisterEHFrames>(*this, &ThisT::handleRegisterEHFrames);
78     addHandler<eh::DeregisterEHFrames>(*this, &ThisT::handleDeregisterEHFrames);
79     addHandler<stubs::CreateIndirectStubsOwner>(
80         *this, &ThisT::handleCreateIndirectStubsOwner);
81     addHandler<stubs::DestroyIndirectStubsOwner>(
82         *this, &ThisT::handleDestroyIndirectStubsOwner);
83     addHandler<stubs::EmitIndirectStubs>(*this,
84                                          &ThisT::handleEmitIndirectStubs);
85     addHandler<stubs::EmitResolverBlock>(*this,
86                                          &ThisT::handleEmitResolverBlock);
87     addHandler<stubs::EmitTrampolineBlock>(*this,
88                                            &ThisT::handleEmitTrampolineBlock);
89     addHandler<utils::GetSymbolAddress>(*this, &ThisT::handleGetSymbolAddress);
90     addHandler<utils::GetRemoteInfo>(*this, &ThisT::handleGetRemoteInfo);
91     addHandler<utils::TerminateSession>(*this, &ThisT::handleTerminateSession);
92   }
93 
94   // FIXME: Remove move/copy ops once MSVC supports synthesizing move ops.
95   OrcRemoteTargetServer(const OrcRemoteTargetServer &) = delete;
96   OrcRemoteTargetServer &operator=(const OrcRemoteTargetServer &) = delete;
97 
98   OrcRemoteTargetServer(OrcRemoteTargetServer &&Other) = default;
99   OrcRemoteTargetServer &operator=(OrcRemoteTargetServer &&) = delete;
100 
requestCompile(JITTargetAddress TrampolineAddr)101   Expected<JITTargetAddress> requestCompile(JITTargetAddress TrampolineAddr) {
102     return callB<utils::RequestCompile>(TrampolineAddr);
103   }
104 
receivedTerminate()105   bool receivedTerminate() const { return TerminateFlag; }
106 
107 private:
108   struct Allocator {
109     Allocator() = default;
AllocatorAllocator110     Allocator(Allocator &&Other) : Allocs(std::move(Other.Allocs)) {}
111 
112     Allocator &operator=(Allocator &&Other) {
113       Allocs = std::move(Other.Allocs);
114       return *this;
115     }
116 
~AllocatorAllocator117     ~Allocator() {
118       for (auto &Alloc : Allocs)
119         sys::Memory::releaseMappedMemory(Alloc.second);
120     }
121 
allocateAllocator122     Error allocate(void *&Addr, size_t Size, uint32_t Align) {
123       std::error_code EC;
124       sys::MemoryBlock MB = sys::Memory::allocateMappedMemory(
125           Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
126       if (EC)
127         return errorCodeToError(EC);
128 
129       Addr = MB.base();
130       assert(Allocs.find(MB.base()) == Allocs.end() && "Duplicate alloc");
131       Allocs[MB.base()] = std::move(MB);
132       return Error::success();
133     }
134 
setProtectionsAllocator135     Error setProtections(void *block, unsigned Flags) {
136       auto I = Allocs.find(block);
137       if (I == Allocs.end())
138         return errorCodeToError(orcError(OrcErrorCode::RemoteMProtectAddrUnrecognized));
139       return errorCodeToError(
140           sys::Memory::protectMappedMemory(I->second, Flags));
141     }
142 
143   private:
144     std::map<void *, sys::MemoryBlock> Allocs;
145   };
146 
doNothing()147   static Error doNothing() { return Error::success(); }
148 
reenter(void * JITTargetAddr,void * TrampolineAddr)149   static JITTargetAddress reenter(void *JITTargetAddr, void *TrampolineAddr) {
150     auto T = static_cast<OrcRemoteTargetServer *>(JITTargetAddr);
151     auto AddrOrErr = T->requestCompile(static_cast<JITTargetAddress>(
152         reinterpret_cast<uintptr_t>(TrampolineAddr)));
153     // FIXME: Allow customizable failure substitution functions.
154     assert(AddrOrErr && "Compile request failed");
155     return *AddrOrErr;
156   }
157 
handleCallIntVoid(JITTargetAddress Addr)158   Expected<int32_t> handleCallIntVoid(JITTargetAddress Addr) {
159     using IntVoidFnTy = int (*)();
160 
161     IntVoidFnTy Fn =
162         reinterpret_cast<IntVoidFnTy>(static_cast<uintptr_t>(Addr));
163 
164     LLVM_DEBUG(dbgs() << "  Calling " << format("0x%016x", Addr) << "\n");
165     int Result = Fn();
166     LLVM_DEBUG(dbgs() << "  Result = " << Result << "\n");
167 
168     return Result;
169   }
170 
handleCallMain(JITTargetAddress Addr,std::vector<std::string> Args)171   Expected<int32_t> handleCallMain(JITTargetAddress Addr,
172                                    std::vector<std::string> Args) {
173     using MainFnTy = int (*)(int, const char *[]);
174 
175     MainFnTy Fn = reinterpret_cast<MainFnTy>(static_cast<uintptr_t>(Addr));
176     int ArgC = Args.size() + 1;
177     int Idx = 1;
178     std::unique_ptr<const char *[]> ArgV(new const char *[ArgC + 1]);
179     ArgV[0] = "<jit process>";
180     for (auto &Arg : Args)
181       ArgV[Idx++] = Arg.c_str();
182     ArgV[ArgC] = 0;
183     LLVM_DEBUG(for (int Idx = 0; Idx < ArgC; ++Idx) {
184       llvm::dbgs() << "Arg " << Idx << ": " << ArgV[Idx] << "\n";
185     });
186 
187     LLVM_DEBUG(dbgs() << "  Calling " << format("0x%016x", Addr) << "\n");
188     int Result = Fn(ArgC, ArgV.get());
189     LLVM_DEBUG(dbgs() << "  Result = " << Result << "\n");
190 
191     return Result;
192   }
193 
handleCallVoidVoid(JITTargetAddress Addr)194   Error handleCallVoidVoid(JITTargetAddress Addr) {
195     using VoidVoidFnTy = void (*)();
196 
197     VoidVoidFnTy Fn =
198         reinterpret_cast<VoidVoidFnTy>(static_cast<uintptr_t>(Addr));
199 
200     LLVM_DEBUG(dbgs() << "  Calling " << format("0x%016x", Addr) << "\n");
201     Fn();
202     LLVM_DEBUG(dbgs() << "  Complete.\n");
203 
204     return Error::success();
205   }
206 
handleCreateRemoteAllocator(ResourceIdMgr::ResourceId Id)207   Error handleCreateRemoteAllocator(ResourceIdMgr::ResourceId Id) {
208     auto I = Allocators.find(Id);
209     if (I != Allocators.end())
210       return errorCodeToError(
211                orcError(OrcErrorCode::RemoteAllocatorIdAlreadyInUse));
212     LLVM_DEBUG(dbgs() << "  Created allocator " << Id << "\n");
213     Allocators[Id] = Allocator();
214     return Error::success();
215   }
216 
handleCreateIndirectStubsOwner(ResourceIdMgr::ResourceId Id)217   Error handleCreateIndirectStubsOwner(ResourceIdMgr::ResourceId Id) {
218     auto I = IndirectStubsOwners.find(Id);
219     if (I != IndirectStubsOwners.end())
220       return errorCodeToError(
221                orcError(OrcErrorCode::RemoteIndirectStubsOwnerIdAlreadyInUse));
222     LLVM_DEBUG(dbgs() << "  Create indirect stubs owner " << Id << "\n");
223     IndirectStubsOwners[Id] = ISBlockOwnerList();
224     return Error::success();
225   }
226 
handleDeregisterEHFrames(JITTargetAddress TAddr,uint32_t Size)227   Error handleDeregisterEHFrames(JITTargetAddress TAddr, uint32_t Size) {
228     uint8_t *Addr = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(TAddr));
229     LLVM_DEBUG(dbgs() << "  Registering EH frames at "
230                       << format("0x%016x", TAddr) << ", Size = " << Size
231                       << " bytes\n");
232     EHFramesDeregister(Addr, Size);
233     return Error::success();
234   }
235 
handleDestroyRemoteAllocator(ResourceIdMgr::ResourceId Id)236   Error handleDestroyRemoteAllocator(ResourceIdMgr::ResourceId Id) {
237     auto I = Allocators.find(Id);
238     if (I == Allocators.end())
239       return errorCodeToError(
240                orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
241     Allocators.erase(I);
242     LLVM_DEBUG(dbgs() << "  Destroyed allocator " << Id << "\n");
243     return Error::success();
244   }
245 
handleDestroyIndirectStubsOwner(ResourceIdMgr::ResourceId Id)246   Error handleDestroyIndirectStubsOwner(ResourceIdMgr::ResourceId Id) {
247     auto I = IndirectStubsOwners.find(Id);
248     if (I == IndirectStubsOwners.end())
249       return errorCodeToError(
250                orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist));
251     IndirectStubsOwners.erase(I);
252     return Error::success();
253   }
254 
255   Expected<std::tuple<JITTargetAddress, JITTargetAddress, uint32_t>>
handleEmitIndirectStubs(ResourceIdMgr::ResourceId Id,uint32_t NumStubsRequired)256   handleEmitIndirectStubs(ResourceIdMgr::ResourceId Id,
257                           uint32_t NumStubsRequired) {
258     LLVM_DEBUG(dbgs() << "  ISMgr " << Id << " request " << NumStubsRequired
259                       << " stubs.\n");
260 
261     auto StubOwnerItr = IndirectStubsOwners.find(Id);
262     if (StubOwnerItr == IndirectStubsOwners.end())
263       return errorCodeToError(
264                orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist));
265 
266     typename TargetT::IndirectStubsInfo IS;
267     if (auto Err =
268             TargetT::emitIndirectStubsBlock(IS, NumStubsRequired, nullptr))
269       return std::move(Err);
270 
271     JITTargetAddress StubsBase = static_cast<JITTargetAddress>(
272         reinterpret_cast<uintptr_t>(IS.getStub(0)));
273     JITTargetAddress PtrsBase = static_cast<JITTargetAddress>(
274         reinterpret_cast<uintptr_t>(IS.getPtr(0)));
275     uint32_t NumStubsEmitted = IS.getNumStubs();
276 
277     auto &BlockList = StubOwnerItr->second;
278     BlockList.push_back(std::move(IS));
279 
280     return std::make_tuple(StubsBase, PtrsBase, NumStubsEmitted);
281   }
282 
handleEmitResolverBlock()283   Error handleEmitResolverBlock() {
284     std::error_code EC;
285     ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
286         TargetT::ResolverCodeSize, nullptr,
287         sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
288     if (EC)
289       return errorCodeToError(EC);
290 
291     TargetT::writeResolverCode(static_cast<uint8_t *>(ResolverBlock.base()),
292                                &reenter, this);
293 
294     return errorCodeToError(sys::Memory::protectMappedMemory(
295         ResolverBlock.getMemoryBlock(),
296         sys::Memory::MF_READ | sys::Memory::MF_EXEC));
297   }
298 
handleEmitTrampolineBlock()299   Expected<std::tuple<JITTargetAddress, uint32_t>> handleEmitTrampolineBlock() {
300     std::error_code EC;
301     auto TrampolineBlock =
302         sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
303             sys::Process::getPageSize(), nullptr,
304             sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
305     if (EC)
306       return errorCodeToError(EC);
307 
308     uint32_t NumTrampolines =
309         (sys::Process::getPageSize() - TargetT::PointerSize) /
310         TargetT::TrampolineSize;
311 
312     uint8_t *TrampolineMem = static_cast<uint8_t *>(TrampolineBlock.base());
313     TargetT::writeTrampolines(TrampolineMem, ResolverBlock.base(),
314                               NumTrampolines);
315 
316     EC = sys::Memory::protectMappedMemory(TrampolineBlock.getMemoryBlock(),
317                                           sys::Memory::MF_READ |
318                                               sys::Memory::MF_EXEC);
319 
320     TrampolineBlocks.push_back(std::move(TrampolineBlock));
321 
322     auto TrampolineBaseAddr = static_cast<JITTargetAddress>(
323         reinterpret_cast<uintptr_t>(TrampolineMem));
324 
325     return std::make_tuple(TrampolineBaseAddr, NumTrampolines);
326   }
327 
handleGetSymbolAddress(const std::string & Name)328   Expected<JITTargetAddress> handleGetSymbolAddress(const std::string &Name) {
329     JITTargetAddress Addr = SymbolLookup(Name);
330     LLVM_DEBUG(dbgs() << "  Symbol '" << Name
331                       << "' =  " << format("0x%016x", Addr) << "\n");
332     return Addr;
333   }
334 
335   Expected<std::tuple<std::string, uint32_t, uint32_t, uint32_t, uint32_t>>
handleGetRemoteInfo()336   handleGetRemoteInfo() {
337     std::string ProcessTriple = sys::getProcessTriple();
338     uint32_t PointerSize = TargetT::PointerSize;
339     uint32_t PageSize = sys::Process::getPageSize();
340     uint32_t TrampolineSize = TargetT::TrampolineSize;
341     uint32_t IndirectStubSize = TargetT::IndirectStubsInfo::StubSize;
342     LLVM_DEBUG(dbgs() << "  Remote info:\n"
343                       << "    triple             = '" << ProcessTriple << "'\n"
344                       << "    pointer size       = " << PointerSize << "\n"
345                       << "    page size          = " << PageSize << "\n"
346                       << "    trampoline size    = " << TrampolineSize << "\n"
347                       << "    indirect stub size = " << IndirectStubSize
348                       << "\n");
349     return std::make_tuple(ProcessTriple, PointerSize, PageSize, TrampolineSize,
350                            IndirectStubSize);
351   }
352 
handleReadMem(JITTargetAddress RSrc,uint64_t Size)353   Expected<std::vector<uint8_t>> handleReadMem(JITTargetAddress RSrc,
354                                                uint64_t Size) {
355     uint8_t *Src = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(RSrc));
356 
357     LLVM_DEBUG(dbgs() << "  Reading " << Size << " bytes from "
358                       << format("0x%016x", RSrc) << "\n");
359 
360     std::vector<uint8_t> Buffer;
361     Buffer.resize(Size);
362     for (uint8_t *P = Src; Size != 0; --Size)
363       Buffer.push_back(*P++);
364 
365     return Buffer;
366   }
367 
handleRegisterEHFrames(JITTargetAddress TAddr,uint32_t Size)368   Error handleRegisterEHFrames(JITTargetAddress TAddr, uint32_t Size) {
369     uint8_t *Addr = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(TAddr));
370     LLVM_DEBUG(dbgs() << "  Registering EH frames at "
371                       << format("0x%016x", TAddr) << ", Size = " << Size
372                       << " bytes\n");
373     EHFramesRegister(Addr, Size);
374     return Error::success();
375   }
376 
handleReserveMem(ResourceIdMgr::ResourceId Id,uint64_t Size,uint32_t Align)377   Expected<JITTargetAddress> handleReserveMem(ResourceIdMgr::ResourceId Id,
378                                               uint64_t Size, uint32_t Align) {
379     auto I = Allocators.find(Id);
380     if (I == Allocators.end())
381       return errorCodeToError(
382                orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
383     auto &Allocator = I->second;
384     void *LocalAllocAddr = nullptr;
385     if (auto Err = Allocator.allocate(LocalAllocAddr, Size, Align))
386       return std::move(Err);
387 
388     LLVM_DEBUG(dbgs() << "  Allocator " << Id << " reserved " << LocalAllocAddr
389                       << " (" << Size << " bytes, alignment " << Align
390                       << ")\n");
391 
392     JITTargetAddress AllocAddr = static_cast<JITTargetAddress>(
393         reinterpret_cast<uintptr_t>(LocalAllocAddr));
394 
395     return AllocAddr;
396   }
397 
handleSetProtections(ResourceIdMgr::ResourceId Id,JITTargetAddress Addr,uint32_t Flags)398   Error handleSetProtections(ResourceIdMgr::ResourceId Id,
399                              JITTargetAddress Addr, uint32_t Flags) {
400     auto I = Allocators.find(Id);
401     if (I == Allocators.end())
402       return errorCodeToError(
403                orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
404     auto &Allocator = I->second;
405     void *LocalAddr = reinterpret_cast<void *>(static_cast<uintptr_t>(Addr));
406     LLVM_DEBUG(dbgs() << "  Allocator " << Id << " set permissions on "
407                       << LocalAddr << " to "
408                       << (Flags & sys::Memory::MF_READ ? 'R' : '-')
409                       << (Flags & sys::Memory::MF_WRITE ? 'W' : '-')
410                       << (Flags & sys::Memory::MF_EXEC ? 'X' : '-') << "\n");
411     return Allocator.setProtections(LocalAddr, Flags);
412   }
413 
handleTerminateSession()414   Error handleTerminateSession() {
415     TerminateFlag = true;
416     return Error::success();
417   }
418 
handleWriteMem(DirectBufferWriter DBW)419   Error handleWriteMem(DirectBufferWriter DBW) {
420     LLVM_DEBUG(dbgs() << "  Writing " << DBW.getSize() << " bytes to "
421                       << format("0x%016x", DBW.getDst()) << "\n");
422     return Error::success();
423   }
424 
handleWritePtr(JITTargetAddress Addr,JITTargetAddress PtrVal)425   Error handleWritePtr(JITTargetAddress Addr, JITTargetAddress PtrVal) {
426     LLVM_DEBUG(dbgs() << "  Writing pointer *" << format("0x%016x", Addr)
427                       << " = " << format("0x%016x", PtrVal) << "\n");
428     uintptr_t *Ptr =
429         reinterpret_cast<uintptr_t *>(static_cast<uintptr_t>(Addr));
430     *Ptr = static_cast<uintptr_t>(PtrVal);
431     return Error::success();
432   }
433 
434   SymbolLookupFtor SymbolLookup;
435   EHFrameRegistrationFtor EHFramesRegister, EHFramesDeregister;
436   std::map<ResourceIdMgr::ResourceId, Allocator> Allocators;
437   using ISBlockOwnerList = std::vector<typename TargetT::IndirectStubsInfo>;
438   std::map<ResourceIdMgr::ResourceId, ISBlockOwnerList> IndirectStubsOwners;
439   sys::OwningMemoryBlock ResolverBlock;
440   std::vector<sys::OwningMemoryBlock> TrampolineBlocks;
441   bool TerminateFlag = false;
442 };
443 
444 } // end namespace remote
445 } // end namespace orc
446 } // end namespace llvm
447 
448 #undef DEBUG_TYPE
449 
450 #endif // LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
451