OrcRemoteTargetServer.h revision 341825
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
43namespace llvm {
44namespace orc {
45namespace remote {
46
47template <typename ChannelT, typename TargetT>
48class OrcRemoteTargetServer
49    : public rpc::SingleThreadedRPCEndpoint<rpc::RawByteChannel> {
50public:
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
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
101  Expected<JITTargetAddress> requestCompile(JITTargetAddress TrampolineAddr) {
102    return callB<utils::RequestCompile>(TrampolineAddr);
103  }
104
105  bool receivedTerminate() const { return TerminateFlag; }
106
107private:
108  struct Allocator {
109    Allocator() = default;
110    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
117    ~Allocator() {
118      for (auto &Alloc : Allocs)
119        sys::Memory::releaseMappedMemory(Alloc.second);
120    }
121
122    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
135    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
147  static Error doNothing() { return Error::success(); }
148
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
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
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
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
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
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
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
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
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>>
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
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
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
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>>
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
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
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
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
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
414  Error handleTerminateSession() {
415    TerminateFlag = true;
416    return Error::success();
417  }
418
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
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