Line data Source code
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 :
57 24 : 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 24 : 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 24 : }
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 0 : return callB<utils::RequestCompile>(TrampolineAddr);
103 : }
104 :
105 0 : bool receivedTerminate() const { return TerminateFlag; }
106 :
107 : private:
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 48 : ~Allocator() {
118 106 : for (auto &Alloc : Allocs)
119 58 : sys::Memory::releaseMappedMemory(Alloc.second);
120 48 : }
121 :
122 58 : Error allocate(void *&Addr, size_t Size, uint32_t Align) {
123 : std::error_code EC;
124 58 : sys::MemoryBlock MB = sys::Memory::allocateMappedMemory(
125 : Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
126 58 : if (EC)
127 0 : return errorCodeToError(EC);
128 :
129 58 : Addr = MB.base();
130 : assert(Allocs.find(MB.base()) == Allocs.end() && "Duplicate alloc");
131 58 : Allocs[MB.base()] = std::move(MB);
132 : return Error::success();
133 : }
134 :
135 58 : Error setProtections(void *block, unsigned Flags) {
136 : auto I = Allocs.find(block);
137 58 : if (I == Allocs.end())
138 0 : return errorCodeToError(orcError(OrcErrorCode::RemoteMProtectAddrUnrecognized));
139 : return errorCodeToError(
140 58 : 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 0 : static JITTargetAddress reenter(void *JITTargetAddr, void *TrampolineAddr) {
150 : auto T = static_cast<OrcRemoteTargetServer *>(JITTargetAddr);
151 0 : 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 0 : return *AddrOrErr;
156 : }
157 :
158 24 : Expected<int32_t> handleCallIntVoid(JITTargetAddress Addr) {
159 : using IntVoidFnTy = int (*)();
160 :
161 24 : IntVoidFnTy Fn =
162 : reinterpret_cast<IntVoidFnTy>(static_cast<uintptr_t>(Addr));
163 :
164 : LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n");
165 24 : int Result = Fn();
166 : LLVM_DEBUG(dbgs() << " Result = " << Result << "\n");
167 :
168 24 : return Result;
169 : }
170 :
171 0 : Expected<int32_t> handleCallMain(JITTargetAddress Addr,
172 : std::vector<std::string> Args) {
173 : using MainFnTy = int (*)(int, const char *[]);
174 :
175 0 : MainFnTy Fn = reinterpret_cast<MainFnTy>(static_cast<uintptr_t>(Addr));
176 0 : int ArgC = Args.size() + 1;
177 : int Idx = 1;
178 0 : std::unique_ptr<const char *[]> ArgV(new const char *[ArgC + 1]);
179 0 : ArgV[0] = "<jit process>";
180 0 : for (auto &Arg : Args)
181 0 : ArgV[Idx++] = Arg.c_str();
182 0 : 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 0 : int Result = Fn(ArgC, ArgV.get());
189 : LLVM_DEBUG(dbgs() << " Result = " << Result << "\n");
190 :
191 0 : return Result;
192 : }
193 :
194 0 : Error handleCallVoidVoid(JITTargetAddress Addr) {
195 : using VoidVoidFnTy = void (*)();
196 :
197 0 : VoidVoidFnTy Fn =
198 : reinterpret_cast<VoidVoidFnTy>(static_cast<uintptr_t>(Addr));
199 :
200 : LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n");
201 0 : Fn();
202 : LLVM_DEBUG(dbgs() << " Complete.\n");
203 :
204 0 : return Error::success();
205 : }
206 :
207 24 : Error handleCreateRemoteAllocator(ResourceIdMgr::ResourceId Id) {
208 : auto I = Allocators.find(Id);
209 24 : if (I != Allocators.end())
210 : return errorCodeToError(
211 0 : orcError(OrcErrorCode::RemoteAllocatorIdAlreadyInUse));
212 : LLVM_DEBUG(dbgs() << " Created allocator " << Id << "\n");
213 48 : Allocators[Id] = Allocator();
214 : return Error::success();
215 : }
216 :
217 0 : Error handleCreateIndirectStubsOwner(ResourceIdMgr::ResourceId Id) {
218 : auto I = IndirectStubsOwners.find(Id);
219 0 : if (I != IndirectStubsOwners.end())
220 : return errorCodeToError(
221 0 : orcError(OrcErrorCode::RemoteIndirectStubsOwnerIdAlreadyInUse));
222 : LLVM_DEBUG(dbgs() << " Create indirect stubs owner " << Id << "\n");
223 0 : IndirectStubsOwners[Id] = ISBlockOwnerList();
224 : return Error::success();
225 : }
226 :
227 0 : Error handleDeregisterEHFrames(JITTargetAddress TAddr, uint32_t Size) {
228 0 : 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 0 : EHFramesDeregister(Addr, Size);
233 0 : return Error::success();
234 : }
235 :
236 24 : Error handleDestroyRemoteAllocator(ResourceIdMgr::ResourceId Id) {
237 : auto I = Allocators.find(Id);
238 24 : if (I == Allocators.end())
239 : return errorCodeToError(
240 0 : orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
241 : Allocators.erase(I);
242 : LLVM_DEBUG(dbgs() << " Destroyed allocator " << Id << "\n");
243 : return Error::success();
244 : }
245 :
246 0 : Error handleDestroyIndirectStubsOwner(ResourceIdMgr::ResourceId Id) {
247 : auto I = IndirectStubsOwners.find(Id);
248 0 : if (I == IndirectStubsOwners.end())
249 : return errorCodeToError(
250 0 : orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist));
251 : IndirectStubsOwners.erase(I);
252 : return Error::success();
253 : }
254 :
255 : Expected<std::tuple<JITTargetAddress, JITTargetAddress, uint32_t>>
256 0 : 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 0 : if (StubOwnerItr == IndirectStubsOwners.end())
263 : return errorCodeToError(
264 0 : orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist));
265 :
266 : typename TargetT::IndirectStubsInfo IS;
267 0 : if (auto Err =
268 : TargetT::emitIndirectStubsBlock(IS, NumStubsRequired, nullptr))
269 : return std::move(Err);
270 :
271 0 : JITTargetAddress StubsBase = static_cast<JITTargetAddress>(
272 : reinterpret_cast<uintptr_t>(IS.getStub(0)));
273 0 : JITTargetAddress PtrsBase = static_cast<JITTargetAddress>(
274 : reinterpret_cast<uintptr_t>(IS.getPtr(0)));
275 : uint32_t NumStubsEmitted = IS.getNumStubs();
276 :
277 0 : auto &BlockList = StubOwnerItr->second;
278 : BlockList.push_back(std::move(IS));
279 :
280 : return std::make_tuple(StubsBase, PtrsBase, NumStubsEmitted);
281 : }
282 :
283 0 : Error handleEmitResolverBlock() {
284 : std::error_code EC;
285 0 : ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
286 : TargetT::ResolverCodeSize, nullptr,
287 : sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
288 0 : if (EC)
289 0 : return errorCodeToError(EC);
290 :
291 0 : TargetT::writeResolverCode(static_cast<uint8_t *>(ResolverBlock.base()),
292 : &reenter, this);
293 :
294 : return errorCodeToError(sys::Memory::protectMappedMemory(
295 0 : ResolverBlock.getMemoryBlock(),
296 0 : sys::Memory::MF_READ | sys::Memory::MF_EXEC));
297 : }
298 :
299 0 : Expected<std::tuple<JITTargetAddress, uint32_t>> handleEmitTrampolineBlock() {
300 : std::error_code EC;
301 0 : auto TrampolineBlock =
302 : sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
303 0 : sys::Process::getPageSize(), nullptr,
304 : sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
305 0 : if (EC)
306 0 : return errorCodeToError(EC);
307 :
308 : uint32_t NumTrampolines =
309 0 : (sys::Process::getPageSize() - TargetT::PointerSize) /
310 : TargetT::TrampolineSize;
311 :
312 : uint8_t *TrampolineMem = static_cast<uint8_t *>(TrampolineBlock.base());
313 0 : TargetT::writeTrampolines(TrampolineMem, ResolverBlock.base(),
314 : NumTrampolines);
315 :
316 0 : EC = sys::Memory::protectMappedMemory(TrampolineBlock.getMemoryBlock(),
317 : sys::Memory::MF_READ |
318 : sys::Memory::MF_EXEC);
319 :
320 0 : TrampolineBlocks.push_back(std::move(TrampolineBlock));
321 :
322 0 : auto TrampolineBaseAddr = static_cast<JITTargetAddress>(
323 : reinterpret_cast<uintptr_t>(TrampolineMem));
324 :
325 : return std::make_tuple(TrampolineBaseAddr, NumTrampolines);
326 : }
327 :
328 20 : 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 20 : return Addr;
333 : }
334 :
335 : Expected<std::tuple<std::string, uint32_t, uint32_t, uint32_t, uint32_t>>
336 24 : handleGetRemoteInfo() {
337 24 : std::string ProcessTriple = sys::getProcessTriple();
338 : uint32_t PointerSize = TargetT::PointerSize;
339 24 : 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 24 : IndirectStubSize);
351 : }
352 :
353 0 : Expected<std::vector<uint8_t>> handleReadMem(JITTargetAddress RSrc,
354 : uint64_t Size) {
355 0 : 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 0 : Buffer.resize(Size);
362 0 : for (uint8_t *P = Src; Size != 0; --Size)
363 0 : Buffer.push_back(*P++);
364 :
365 0 : return Buffer;
366 : }
367 :
368 5 : Error handleRegisterEHFrames(JITTargetAddress TAddr, uint32_t Size) {
369 5 : 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 5 : EHFramesRegister(Addr, Size);
374 5 : return Error::success();
375 : }
376 :
377 58 : Expected<JITTargetAddress> handleReserveMem(ResourceIdMgr::ResourceId Id,
378 : uint64_t Size, uint32_t Align) {
379 : auto I = Allocators.find(Id);
380 58 : if (I == Allocators.end())
381 : return errorCodeToError(
382 0 : orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
383 58 : auto &Allocator = I->second;
384 58 : void *LocalAllocAddr = nullptr;
385 116 : 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 58 : JITTargetAddress AllocAddr = static_cast<JITTargetAddress>(
393 : reinterpret_cast<uintptr_t>(LocalAllocAddr));
394 :
395 : return AllocAddr;
396 : }
397 :
398 58 : Error handleSetProtections(ResourceIdMgr::ResourceId Id,
399 : JITTargetAddress Addr, uint32_t Flags) {
400 : auto I = Allocators.find(Id);
401 58 : if (I == Allocators.end())
402 : return errorCodeToError(
403 0 : orcError(OrcErrorCode::RemoteAllocatorDoesNotExist));
404 58 : auto &Allocator = I->second;
405 58 : 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 58 : return Allocator.setProtections(LocalAddr, Flags);
412 : }
413 :
414 24 : Error handleTerminateSession() {
415 24 : TerminateFlag = true;
416 24 : return Error::success();
417 : }
418 :
419 64 : Error handleWriteMem(DirectBufferWriter DBW) {
420 : LLVM_DEBUG(dbgs() << " Writing " << DBW.getSize() << " bytes to "
421 : << format("0x%016x", DBW.getDst()) << "\n");
422 64 : return Error::success();
423 : }
424 :
425 0 : Error handleWritePtr(JITTargetAddress Addr, JITTargetAddress PtrVal) {
426 : LLVM_DEBUG(dbgs() << " Writing pointer *" << format("0x%016x", Addr)
427 : << " = " << format("0x%016x", PtrVal) << "\n");
428 0 : uintptr_t *Ptr =
429 : reinterpret_cast<uintptr_t *>(static_cast<uintptr_t>(Addr));
430 0 : *Ptr = static_cast<uintptr_t>(PtrVal);
431 0 : 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
|