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
43namespace llvm {
44namespace orc {
45namespace remote {
46
47template <typename ChannelT, typename TargetT>
48class OrcRemoteTargetServer
49 : public shared::SingleThreadedRPCEndpoint<shared::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 : 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
103 Expected<JITTargetAddress> requestCompile(JITTargetAddress TrampolineAddr) {
104 return callB<utils::RequestCompile>(TrampolineAddr);
105 }
106
107 bool receivedTerminate() const { return TerminateFlag; }
108
109private:
110 struct Allocator {
111 Allocator() = default;
112 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
119 ~Allocator() {
120 for (auto &Alloc : Allocs)
121 sys::Memory::releaseMappedMemory(Alloc.second);
122 }
123
124 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
137 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
149 static Error doNothing() { return Error::success(); }
150
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
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
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
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
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
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
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
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
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
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>>
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
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
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
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>>
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
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
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
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
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
428 Error handleTerminateSession() {
429 TerminateFlag = true;
430 return Error::success();
431 }
432
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
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