1 //===- OrcRemoteTargetServer.h - Orc Remote-target Server -------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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.
12 //===----------------------------------------------------------------------===//
14 #ifndef LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
15 #define LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H
17 #include "llvm/ExecutionEngine/JITSymbol.h"
18 #include "llvm/ExecutionEngine/Orc/OrcError.h"
19 #include "llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h"
20 #include "llvm/Support/Debug.h"
21 #include "llvm/Support/Error.h"
22 #include "llvm/Support/Format.h"
23 #include "llvm/Support/Host.h"
24 #include "llvm/Support/Memory.h"
25 #include "llvm/Support/Process.h"
26 #include "llvm/Support/raw_ostream.h"
35 #include <system_error>
37 #include <type_traits>
40 #define DEBUG_TYPE "orc-remote"
46 template <typename ChannelT
, typename TargetT
>
47 class OrcRemoteTargetServer
48 : public rpc::SingleThreadedRPCEndpoint
<rpc::RawByteChannel
> {
50 using SymbolLookupFtor
=
51 std::function
<JITTargetAddress(const std::string
&Name
)>;
53 using EHFrameRegistrationFtor
=
54 std::function
<void(uint8_t *Addr
, uint32_t Size
)>;
56 OrcRemoteTargetServer(ChannelT
&Channel
, SymbolLookupFtor SymbolLookup
,
57 EHFrameRegistrationFtor EHFramesRegister
,
58 EHFrameRegistrationFtor EHFramesDeregister
)
59 : rpc::SingleThreadedRPCEndpoint
<rpc::RawByteChannel
>(Channel
, true),
60 SymbolLookup(std::move(SymbolLookup
)),
61 EHFramesRegister(std::move(EHFramesRegister
)),
62 EHFramesDeregister(std::move(EHFramesDeregister
)) {
63 using ThisT
= typename
std::remove_reference
<decltype(*this)>::type
;
64 addHandler
<exec::CallIntVoid
>(*this, &ThisT::handleCallIntVoid
);
65 addHandler
<exec::CallMain
>(*this, &ThisT::handleCallMain
);
66 addHandler
<exec::CallVoidVoid
>(*this, &ThisT::handleCallVoidVoid
);
67 addHandler
<mem::CreateRemoteAllocator
>(*this,
68 &ThisT::handleCreateRemoteAllocator
);
69 addHandler
<mem::DestroyRemoteAllocator
>(
70 *this, &ThisT::handleDestroyRemoteAllocator
);
71 addHandler
<mem::ReadMem
>(*this, &ThisT::handleReadMem
);
72 addHandler
<mem::ReserveMem
>(*this, &ThisT::handleReserveMem
);
73 addHandler
<mem::SetProtections
>(*this, &ThisT::handleSetProtections
);
74 addHandler
<mem::WriteMem
>(*this, &ThisT::handleWriteMem
);
75 addHandler
<mem::WritePtr
>(*this, &ThisT::handleWritePtr
);
76 addHandler
<eh::RegisterEHFrames
>(*this, &ThisT::handleRegisterEHFrames
);
77 addHandler
<eh::DeregisterEHFrames
>(*this, &ThisT::handleDeregisterEHFrames
);
78 addHandler
<stubs::CreateIndirectStubsOwner
>(
79 *this, &ThisT::handleCreateIndirectStubsOwner
);
80 addHandler
<stubs::DestroyIndirectStubsOwner
>(
81 *this, &ThisT::handleDestroyIndirectStubsOwner
);
82 addHandler
<stubs::EmitIndirectStubs
>(*this,
83 &ThisT::handleEmitIndirectStubs
);
84 addHandler
<stubs::EmitResolverBlock
>(*this,
85 &ThisT::handleEmitResolverBlock
);
86 addHandler
<stubs::EmitTrampolineBlock
>(*this,
87 &ThisT::handleEmitTrampolineBlock
);
88 addHandler
<utils::GetSymbolAddress
>(*this, &ThisT::handleGetSymbolAddress
);
89 addHandler
<utils::GetRemoteInfo
>(*this, &ThisT::handleGetRemoteInfo
);
90 addHandler
<utils::TerminateSession
>(*this, &ThisT::handleTerminateSession
);
93 // FIXME: Remove move/copy ops once MSVC supports synthesizing move ops.
94 OrcRemoteTargetServer(const OrcRemoteTargetServer
&) = delete;
95 OrcRemoteTargetServer
&operator=(const OrcRemoteTargetServer
&) = delete;
97 OrcRemoteTargetServer(OrcRemoteTargetServer
&&Other
) = default;
98 OrcRemoteTargetServer
&operator=(OrcRemoteTargetServer
&&) = delete;
100 Expected
<JITTargetAddress
> requestCompile(JITTargetAddress TrampolineAddr
) {
101 return callB
<utils::RequestCompile
>(TrampolineAddr
);
104 bool receivedTerminate() const { return TerminateFlag
; }
108 Allocator() = default;
109 Allocator(Allocator
&&Other
) : Allocs(std::move(Other
.Allocs
)) {}
111 Allocator
&operator=(Allocator
&&Other
) {
112 Allocs
= std::move(Other
.Allocs
);
117 for (auto &Alloc
: Allocs
)
118 sys::Memory::releaseMappedMemory(Alloc
.second
);
121 Error
allocate(void *&Addr
, size_t Size
, uint32_t Align
) {
123 sys::MemoryBlock MB
= sys::Memory::allocateMappedMemory(
124 Size
, nullptr, sys::Memory::MF_READ
| sys::Memory::MF_WRITE
, EC
);
126 return errorCodeToError(EC
);
129 assert(Allocs
.find(MB
.base()) == Allocs
.end() && "Duplicate alloc");
130 Allocs
[MB
.base()] = std::move(MB
);
131 return Error::success();
134 Error
setProtections(void *block
, unsigned Flags
) {
135 auto I
= Allocs
.find(block
);
136 if (I
== Allocs
.end())
137 return errorCodeToError(orcError(OrcErrorCode::RemoteMProtectAddrUnrecognized
));
138 return errorCodeToError(
139 sys::Memory::protectMappedMemory(I
->second
, Flags
));
143 std::map
<void *, sys::MemoryBlock
> Allocs
;
146 static Error
doNothing() { return Error::success(); }
148 static JITTargetAddress
reenter(void *JITTargetAddr
, void *TrampolineAddr
) {
149 auto T
= static_cast<OrcRemoteTargetServer
*>(JITTargetAddr
);
150 auto AddrOrErr
= T
->requestCompile(static_cast<JITTargetAddress
>(
151 reinterpret_cast<uintptr_t>(TrampolineAddr
)));
152 // FIXME: Allow customizable failure substitution functions.
153 assert(AddrOrErr
&& "Compile request failed");
157 Expected
<int32_t> handleCallIntVoid(JITTargetAddress Addr
) {
158 using IntVoidFnTy
= int (*)();
161 reinterpret_cast<IntVoidFnTy
>(static_cast<uintptr_t>(Addr
));
163 LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr
) << "\n");
165 LLVM_DEBUG(dbgs() << " Result = " << Result
<< "\n");
170 Expected
<int32_t> handleCallMain(JITTargetAddress Addr
,
171 std::vector
<std::string
> Args
) {
172 using MainFnTy
= int (*)(int, const char *[]);
174 MainFnTy Fn
= reinterpret_cast<MainFnTy
>(static_cast<uintptr_t>(Addr
));
175 int ArgC
= Args
.size() + 1;
177 std::unique_ptr
<const char *[]> ArgV(new const char *[ArgC
+ 1]);
178 ArgV
[0] = "<jit process>";
179 for (auto &Arg
: Args
)
180 ArgV
[Idx
++] = Arg
.c_str();
182 LLVM_DEBUG(for (int Idx
= 0; Idx
< ArgC
; ++Idx
) {
183 llvm::dbgs() << "Arg " << Idx
<< ": " << ArgV
[Idx
] << "\n";
186 LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr
) << "\n");
187 int Result
= Fn(ArgC
, ArgV
.get());
188 LLVM_DEBUG(dbgs() << " Result = " << Result
<< "\n");
193 Error
handleCallVoidVoid(JITTargetAddress Addr
) {
194 using VoidVoidFnTy
= void (*)();
197 reinterpret_cast<VoidVoidFnTy
>(static_cast<uintptr_t>(Addr
));
199 LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr
) << "\n");
201 LLVM_DEBUG(dbgs() << " Complete.\n");
203 return Error::success();
206 Error
handleCreateRemoteAllocator(ResourceIdMgr::ResourceId Id
) {
207 auto I
= Allocators
.find(Id
);
208 if (I
!= Allocators
.end())
209 return errorCodeToError(
210 orcError(OrcErrorCode::RemoteAllocatorIdAlreadyInUse
));
211 LLVM_DEBUG(dbgs() << " Created allocator " << Id
<< "\n");
212 Allocators
[Id
] = Allocator();
213 return Error::success();
216 Error
handleCreateIndirectStubsOwner(ResourceIdMgr::ResourceId Id
) {
217 auto I
= IndirectStubsOwners
.find(Id
);
218 if (I
!= IndirectStubsOwners
.end())
219 return errorCodeToError(
220 orcError(OrcErrorCode::RemoteIndirectStubsOwnerIdAlreadyInUse
));
221 LLVM_DEBUG(dbgs() << " Create indirect stubs owner " << Id
<< "\n");
222 IndirectStubsOwners
[Id
] = ISBlockOwnerList();
223 return Error::success();
226 Error
handleDeregisterEHFrames(JITTargetAddress TAddr
, uint32_t Size
) {
227 uint8_t *Addr
= reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(TAddr
));
228 LLVM_DEBUG(dbgs() << " Registering EH frames at "
229 << format("0x%016x", TAddr
) << ", Size = " << Size
231 EHFramesDeregister(Addr
, Size
);
232 return Error::success();
235 Error
handleDestroyRemoteAllocator(ResourceIdMgr::ResourceId Id
) {
236 auto I
= Allocators
.find(Id
);
237 if (I
== Allocators
.end())
238 return errorCodeToError(
239 orcError(OrcErrorCode::RemoteAllocatorDoesNotExist
));
241 LLVM_DEBUG(dbgs() << " Destroyed allocator " << Id
<< "\n");
242 return Error::success();
245 Error
handleDestroyIndirectStubsOwner(ResourceIdMgr::ResourceId Id
) {
246 auto I
= IndirectStubsOwners
.find(Id
);
247 if (I
== IndirectStubsOwners
.end())
248 return errorCodeToError(
249 orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist
));
250 IndirectStubsOwners
.erase(I
);
251 return Error::success();
254 Expected
<std::tuple
<JITTargetAddress
, JITTargetAddress
, uint32_t>>
255 handleEmitIndirectStubs(ResourceIdMgr::ResourceId Id
,
256 uint32_t NumStubsRequired
) {
257 LLVM_DEBUG(dbgs() << " ISMgr " << Id
<< " request " << NumStubsRequired
260 auto StubOwnerItr
= IndirectStubsOwners
.find(Id
);
261 if (StubOwnerItr
== IndirectStubsOwners
.end())
262 return errorCodeToError(
263 orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist
));
265 typename
TargetT::IndirectStubsInfo IS
;
267 TargetT::emitIndirectStubsBlock(IS
, NumStubsRequired
, nullptr))
268 return std::move(Err
);
270 JITTargetAddress StubsBase
= static_cast<JITTargetAddress
>(
271 reinterpret_cast<uintptr_t>(IS
.getStub(0)));
272 JITTargetAddress PtrsBase
= static_cast<JITTargetAddress
>(
273 reinterpret_cast<uintptr_t>(IS
.getPtr(0)));
274 uint32_t NumStubsEmitted
= IS
.getNumStubs();
276 auto &BlockList
= StubOwnerItr
->second
;
277 BlockList
.push_back(std::move(IS
));
279 return std::make_tuple(StubsBase
, PtrsBase
, NumStubsEmitted
);
282 Error
handleEmitResolverBlock() {
284 ResolverBlock
= sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
285 TargetT::ResolverCodeSize
, nullptr,
286 sys::Memory::MF_READ
| sys::Memory::MF_WRITE
, EC
));
288 return errorCodeToError(EC
);
290 TargetT::writeResolverCode(static_cast<uint8_t *>(ResolverBlock
.base()),
293 return errorCodeToError(sys::Memory::protectMappedMemory(
294 ResolverBlock
.getMemoryBlock(),
295 sys::Memory::MF_READ
| sys::Memory::MF_EXEC
));
298 Expected
<std::tuple
<JITTargetAddress
, uint32_t>> handleEmitTrampolineBlock() {
300 auto TrampolineBlock
=
301 sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
302 sys::Process::getPageSizeEstimate(), nullptr,
303 sys::Memory::MF_READ
| sys::Memory::MF_WRITE
, EC
));
305 return errorCodeToError(EC
);
307 uint32_t NumTrampolines
=
308 (sys::Process::getPageSizeEstimate() - TargetT::PointerSize
) /
309 TargetT::TrampolineSize
;
311 uint8_t *TrampolineMem
= static_cast<uint8_t *>(TrampolineBlock
.base());
312 TargetT::writeTrampolines(TrampolineMem
, ResolverBlock
.base(),
315 EC
= sys::Memory::protectMappedMemory(TrampolineBlock
.getMemoryBlock(),
316 sys::Memory::MF_READ
|
317 sys::Memory::MF_EXEC
);
319 TrampolineBlocks
.push_back(std::move(TrampolineBlock
));
321 auto TrampolineBaseAddr
= static_cast<JITTargetAddress
>(
322 reinterpret_cast<uintptr_t>(TrampolineMem
));
324 return std::make_tuple(TrampolineBaseAddr
, NumTrampolines
);
327 Expected
<JITTargetAddress
> handleGetSymbolAddress(const std::string
&Name
) {
328 JITTargetAddress Addr
= SymbolLookup(Name
);
329 LLVM_DEBUG(dbgs() << " Symbol '" << Name
330 << "' = " << format("0x%016x", Addr
) << "\n");
334 Expected
<std::tuple
<std::string
, uint32_t, uint32_t, uint32_t, uint32_t>>
335 handleGetRemoteInfo() {
336 std::string ProcessTriple
= sys::getProcessTriple();
337 uint32_t PointerSize
= TargetT::PointerSize
;
338 uint32_t PageSize
= sys::Process::getPageSizeEstimate();
339 uint32_t TrampolineSize
= TargetT::TrampolineSize
;
340 uint32_t IndirectStubSize
= TargetT::IndirectStubsInfo::StubSize
;
341 LLVM_DEBUG(dbgs() << " Remote info:\n"
342 << " triple = '" << ProcessTriple
<< "'\n"
343 << " pointer size = " << PointerSize
<< "\n"
344 << " page size = " << PageSize
<< "\n"
345 << " trampoline size = " << TrampolineSize
<< "\n"
346 << " indirect stub size = " << IndirectStubSize
348 return std::make_tuple(ProcessTriple
, PointerSize
, PageSize
, TrampolineSize
,
352 Expected
<std::vector
<uint8_t>> handleReadMem(JITTargetAddress RSrc
,
354 uint8_t *Src
= reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(RSrc
));
356 LLVM_DEBUG(dbgs() << " Reading " << Size
<< " bytes from "
357 << format("0x%016x", RSrc
) << "\n");
359 std::vector
<uint8_t> Buffer
;
361 for (uint8_t *P
= Src
; Size
!= 0; --Size
)
362 Buffer
.push_back(*P
++);
367 Error
handleRegisterEHFrames(JITTargetAddress TAddr
, uint32_t Size
) {
368 uint8_t *Addr
= reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(TAddr
));
369 LLVM_DEBUG(dbgs() << " Registering EH frames at "
370 << format("0x%016x", TAddr
) << ", Size = " << Size
372 EHFramesRegister(Addr
, Size
);
373 return Error::success();
376 Expected
<JITTargetAddress
> handleReserveMem(ResourceIdMgr::ResourceId Id
,
377 uint64_t Size
, uint32_t Align
) {
378 auto I
= Allocators
.find(Id
);
379 if (I
== Allocators
.end())
380 return errorCodeToError(
381 orcError(OrcErrorCode::RemoteAllocatorDoesNotExist
));
382 auto &Allocator
= I
->second
;
383 void *LocalAllocAddr
= nullptr;
384 if (auto Err
= Allocator
.allocate(LocalAllocAddr
, Size
, Align
))
385 return std::move(Err
);
387 LLVM_DEBUG(dbgs() << " Allocator " << Id
<< " reserved " << LocalAllocAddr
388 << " (" << Size
<< " bytes, alignment " << Align
391 JITTargetAddress AllocAddr
= static_cast<JITTargetAddress
>(
392 reinterpret_cast<uintptr_t>(LocalAllocAddr
));
397 Error
handleSetProtections(ResourceIdMgr::ResourceId Id
,
398 JITTargetAddress Addr
, uint32_t Flags
) {
399 auto I
= Allocators
.find(Id
);
400 if (I
== Allocators
.end())
401 return errorCodeToError(
402 orcError(OrcErrorCode::RemoteAllocatorDoesNotExist
));
403 auto &Allocator
= I
->second
;
404 void *LocalAddr
= reinterpret_cast<void *>(static_cast<uintptr_t>(Addr
));
405 LLVM_DEBUG(dbgs() << " Allocator " << Id
<< " set permissions on "
406 << LocalAddr
<< " to "
407 << (Flags
& sys::Memory::MF_READ
? 'R' : '-')
408 << (Flags
& sys::Memory::MF_WRITE
? 'W' : '-')
409 << (Flags
& sys::Memory::MF_EXEC
? 'X' : '-') << "\n");
410 return Allocator
.setProtections(LocalAddr
, Flags
);
413 Error
handleTerminateSession() {
414 TerminateFlag
= true;
415 return Error::success();
418 Error
handleWriteMem(DirectBufferWriter DBW
) {
419 LLVM_DEBUG(dbgs() << " Writing " << DBW
.getSize() << " bytes to "
420 << format("0x%016x", DBW
.getDst()) << "\n");
421 return Error::success();
424 Error
handleWritePtr(JITTargetAddress Addr
, JITTargetAddress PtrVal
) {
425 LLVM_DEBUG(dbgs() << " Writing pointer *" << format("0x%016x", Addr
)
426 << " = " << format("0x%016x", PtrVal
) << "\n");
428 reinterpret_cast<uintptr_t *>(static_cast<uintptr_t>(Addr
));
429 *Ptr
= static_cast<uintptr_t>(PtrVal
);
430 return Error::success();
433 SymbolLookupFtor SymbolLookup
;
434 EHFrameRegistrationFtor EHFramesRegister
, EHFramesDeregister
;
435 std::map
<ResourceIdMgr::ResourceId
, Allocator
> Allocators
;
436 using ISBlockOwnerList
= std::vector
<typename
TargetT::IndirectStubsInfo
>;
437 std::map
<ResourceIdMgr::ResourceId
, ISBlockOwnerList
> IndirectStubsOwners
;
438 sys::OwningMemoryBlock ResolverBlock
;
439 std::vector
<sys::OwningMemoryBlock
> TrampolineBlocks
;
440 bool TerminateFlag
= false;
443 } // end namespace remote
444 } // end namespace orc
445 } // end namespace llvm
449 #endif // LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H