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