1//===--- OrcRPCTargetProcessControl.h - Remote target control ---*- 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// Utilities for interacting with target processes.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_EXECUTIONENGINE_ORC_ORCRPCTARGETPROCESSCONTROL_H
14#define LLVM_EXECUTIONENGINE_ORC_ORCRPCTARGETPROCESSCONTROL_H
15
16#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
17#include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h"
18#include "llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h"
19#include "llvm/ExecutionEngine/Orc/TargetProcessControl.h"
20#include "llvm/Support/MSVCErrorWorkarounds.h"
21
22namespace llvm {
23namespace orc {
24
25/// JITLinkMemoryManager implementation for a process connected via an ORC RPC
26/// endpoint.
27template <typename OrcRPCTPCImplT>
28class OrcRPCTPCJITLinkMemoryManager : public jitlink::JITLinkMemoryManager {
29private:
30 struct HostAlloc {
31 std::unique_ptr<char[]> Mem;
32 uint64_t Size;
33 };
34
35 struct TargetAlloc {
36 JITTargetAddress Address = 0;
37 uint64_t AllocatedSize = 0;
38 };
39
40 using HostAllocMap = DenseMap<int, HostAlloc>;
41 using TargetAllocMap = DenseMap<int, TargetAlloc>;
42
43public:
44 class OrcRPCAllocation : public Allocation {
45 public:
46 OrcRPCAllocation(OrcRPCTPCJITLinkMemoryManager<OrcRPCTPCImplT> &Parent,
47 HostAllocMap HostAllocs, TargetAllocMap TargetAllocs)
48 : Parent(Parent), HostAllocs(std::move(HostAllocs)),
49 TargetAllocs(std::move(TargetAllocs)) {
50 assert(HostAllocs.size() == TargetAllocs.size() &&
51 "HostAllocs size should match TargetAllocs");
52 }
53
54 ~OrcRPCAllocation() override {
55 assert(TargetAllocs.empty() && "failed to deallocate");
56 }
57
58 MutableArrayRef<char> getWorkingMemory(ProtectionFlags Seg) override {
59 auto I = HostAllocs.find(Seg);
60 assert(I != HostAllocs.end() && "No host allocation for segment");
61 auto &HA = I->second;
62 return {HA.Mem.get(), static_cast<size_t>(HA.Size)};
63 }
64
65 JITTargetAddress getTargetMemory(ProtectionFlags Seg) override {
66 auto I = TargetAllocs.find(Seg);
67 assert(I != TargetAllocs.end() && "No target allocation for segment");
68 return I->second.Address;
69 }
70
71 void finalizeAsync(FinalizeContinuation OnFinalize) override {
72
73 std::vector<tpctypes::BufferWrite> BufferWrites;
74 orcrpctpc::ReleaseOrFinalizeMemRequest FMR;
75
76 for (auto &KV : HostAllocs) {
77 assert(TargetAllocs.count(KV.first) &&
78 "No target allocation for buffer");
79 auto &HA = KV.second;
80 auto &TA = TargetAllocs[KV.first];
81 BufferWrites.push_back({TA.Address, StringRef(HA.Mem.get(), HA.Size)});
82 FMR.push_back({orcrpctpc::toWireProtectionFlags(
83 static_cast<sys::Memory::ProtectionFlags>(KV.first)),
84 TA.Address, TA.AllocatedSize});
85 }
86
87 DEBUG_WITH_TYPE("orc", {
88 dbgs() << "finalizeAsync " << (void *)this << ":\n";
89 auto FMRI = FMR.begin();
90 for (auto &B : BufferWrites) {
91 auto Prot = FMRI->Prot;
92 ++FMRI;
93 dbgs() << " Writing " << formatv("{0:x16}", B.Buffer.size())
94 << " bytes to " << ((Prot & orcrpctpc::WPF_Read) ? 'R' : '-')
95 << ((Prot & orcrpctpc::WPF_Write) ? 'W' : '-')
96 << ((Prot & orcrpctpc::WPF_Exec) ? 'X' : '-')
97 << " segment: local " << (const void *)B.Buffer.data()
98 << " -> target " << formatv("{0:x16}", B.Address) << "\n";
99 }
100 });
101 if (auto Err =
102 Parent.Parent.getMemoryAccess().writeBuffers(BufferWrites)) {
103 OnFinalize(std::move(Err));
104 return;
105 }
106
107 DEBUG_WITH_TYPE("orc", dbgs() << " Applying permissions...\n");
108 if (auto Err =
109 Parent.getEndpoint().template callAsync<orcrpctpc::FinalizeMem>(
110 [OF = std::move(OnFinalize)](Error Err2) {
111 // FIXME: Dispatch to work queue.
112 std::thread([OF = std::move(OF),
113 Err3 = std::move(Err2)]() mutable {
114 DEBUG_WITH_TYPE(
115 "orc", { dbgs() << " finalizeAsync complete\n"; });
116 OF(std::move(Err3));
117 }).detach();
118 return Error::success();
119 },
120 FMR)) {
121 DEBUG_WITH_TYPE("orc", dbgs() << " failed.\n");
122 Parent.getEndpoint().abandonPendingResponses();
123 Parent.reportError(std::move(Err));
124 }
125 DEBUG_WITH_TYPE("orc", {
126 dbgs() << "Leaving finalizeAsync (finalization may continue in "
127 "background)\n";
128 });
129 }
130
131 Error deallocate() override {
132 orcrpctpc::ReleaseOrFinalizeMemRequest RMR;
133 for (auto &KV : TargetAllocs)
134 RMR.push_back({orcrpctpc::toWireProtectionFlags(
135 static_cast<sys::Memory::ProtectionFlags>(KV.first)),
136 KV.second.Address, KV.second.AllocatedSize});
137 TargetAllocs.clear();
138
139 return Parent.getEndpoint().template callB<orcrpctpc::ReleaseMem>(RMR);
140 }
141
142 private:
143 OrcRPCTPCJITLinkMemoryManager<OrcRPCTPCImplT> &Parent;
144 HostAllocMap HostAllocs;
145 TargetAllocMap TargetAllocs;
146 };
147
148 OrcRPCTPCJITLinkMemoryManager(OrcRPCTPCImplT &Parent) : Parent(Parent) {}
149
150 Expected<std::unique_ptr<Allocation>>
151 allocate(const jitlink::JITLinkDylib *JD,
152 const SegmentsRequestMap &Request) override {
153 orcrpctpc::ReserveMemRequest RMR;
154 HostAllocMap HostAllocs;
155
156 for (auto &KV : Request) {
157 assert(KV.second.getContentSize() <= std::numeric_limits<size_t>::max() &&
158 "Content size is out-of-range for host");
159
160 RMR.push_back({orcrpctpc::toWireProtectionFlags(
161 static_cast<sys::Memory::ProtectionFlags>(KV.first)),
162 KV.second.getContentSize() + KV.second.getZeroFillSize(),
163 KV.second.getAlignment()});
164 HostAllocs[KV.first] = {
165 std::make_unique<char[]>(KV.second.getContentSize()),
166 KV.second.getContentSize()};
167 }
168
169 DEBUG_WITH_TYPE("orc", {
170 dbgs() << "Orc remote memmgr got request:\n";
171 for (auto &KV : Request)
172 dbgs() << " permissions: "
173 << ((KV.first & sys::Memory::MF_READ) ? 'R' : '-')
174 << ((KV.first & sys::Memory::MF_WRITE) ? 'W' : '-')
175 << ((KV.first & sys::Memory::MF_EXEC) ? 'X' : '-')
176 << ", content size: "
177 << formatv("{0:x16}", KV.second.getContentSize())
178 << " + zero-fill-size: "
179 << formatv("{0:x16}", KV.second.getZeroFillSize())
180 << ", align: " << KV.second.getAlignment() << "\n";
181 });
182
183 // FIXME: LLVM RPC needs to be fixed to support alt
184 // serialization/deserialization on return types. For now just
185 // translate from std::map to DenseMap manually.
186 auto TmpTargetAllocs =
187 Parent.getEndpoint().template callB<orcrpctpc::ReserveMem>(RMR);
188 if (!TmpTargetAllocs)
189 return TmpTargetAllocs.takeError();
190
191 if (TmpTargetAllocs->size() != RMR.size())
192 return make_error<StringError>(
193 "Number of target allocations does not match request",
194 inconvertibleErrorCode());
195
196 TargetAllocMap TargetAllocs;
197 for (auto &E : *TmpTargetAllocs)
198 TargetAllocs[orcrpctpc::fromWireProtectionFlags(E.Prot)] = {
199 E.Address, E.AllocatedSize};
200
201 DEBUG_WITH_TYPE("orc", {
202 auto HAI = HostAllocs.begin();
203 for (auto &KV : TargetAllocs)
204 dbgs() << " permissions: "
205 << ((KV.first & sys::Memory::MF_READ) ? 'R' : '-')
206 << ((KV.first & sys::Memory::MF_WRITE) ? 'W' : '-')
207 << ((KV.first & sys::Memory::MF_EXEC) ? 'X' : '-')
208 << " assigned local " << (void *)HAI->second.Mem.get()
209 << ", target " << formatv("{0:x16}", KV.second.Address) << "\n";
210 });
211
212 return std::make_unique<OrcRPCAllocation>(*this, std::move(HostAllocs),
213 std::move(TargetAllocs));
214 }
215
216private:
217 void reportError(Error Err) { Parent.reportError(std::move(Err)); }
218
219 decltype(std::declval<OrcRPCTPCImplT>().getEndpoint()) getEndpoint() {
220 return Parent.getEndpoint();
221 }
222
223 OrcRPCTPCImplT &Parent;
224};
225
226/// TargetProcessControl::MemoryAccess implementation for a process connected
227/// via an ORC RPC endpoint.
228template <typename OrcRPCTPCImplT>
229class OrcRPCTPCMemoryAccess : public TargetProcessControl::MemoryAccess {
230public:
231 OrcRPCTPCMemoryAccess(OrcRPCTPCImplT &Parent) : Parent(Parent) {}
232
233 void writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws,
234 WriteResultFn OnWriteComplete) override {
235 writeViaRPC<orcrpctpc::WriteUInt8s>(Ws, std::move(OnWriteComplete));
236 }
237
238 void writeUInt16s(ArrayRef<tpctypes::UInt16Write> Ws,
239 WriteResultFn OnWriteComplete) override {
240 writeViaRPC<orcrpctpc::WriteUInt16s>(Ws, std::move(OnWriteComplete));
241 }
242
243 void writeUInt32s(ArrayRef<tpctypes::UInt32Write> Ws,
244 WriteResultFn OnWriteComplete) override {
245 writeViaRPC<orcrpctpc::WriteUInt32s>(Ws, std::move(OnWriteComplete));
246 }
247
248 void writeUInt64s(ArrayRef<tpctypes::UInt64Write> Ws,
249 WriteResultFn OnWriteComplete) override {
250 writeViaRPC<orcrpctpc::WriteUInt64s>(Ws, std::move(OnWriteComplete));
251 }
252
253 void writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws,
254 WriteResultFn OnWriteComplete) override {
255 writeViaRPC<orcrpctpc::WriteBuffers>(Ws, std::move(OnWriteComplete));
256 }
257
258private:
259 template <typename WriteRPCFunction, typename WriteElementT>
260 void writeViaRPC(ArrayRef<WriteElementT> Ws, WriteResultFn OnWriteComplete) {
261 if (auto Err = Parent.getEndpoint().template callAsync<WriteRPCFunction>(
262 [OWC = std::move(OnWriteComplete)](Error Err2) mutable -> Error {
263 OWC(std::move(Err2));
264 return Error::success();
265 },
266 Ws)) {
267 Parent.reportError(std::move(Err));
268 Parent.getEndpoint().abandonPendingResponses();
269 }
270 }
271
272 OrcRPCTPCImplT &Parent;
273};
274
275// TargetProcessControl for a process connected via an ORC RPC Endpoint.
276template <typename RPCEndpointT>
277class OrcRPCTargetProcessControlBase : public TargetProcessControl {
278public:
279 using ErrorReporter = unique_function<void(Error)>;
280
281 using OnCloseConnectionFunction = unique_function<Error(Error)>;
282
283 OrcRPCTargetProcessControlBase(std::shared_ptr<SymbolStringPool> SSP,
284 RPCEndpointT &EP, ErrorReporter ReportError)
285 : TargetProcessControl(std::move(SSP)),
286 ReportError(std::move(ReportError)), EP(EP) {}
287
288 void reportError(Error Err) { ReportError(std::move(Err)); }
289
290 RPCEndpointT &getEndpoint() { return EP; }
291
292 Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override {
293 DEBUG_WITH_TYPE("orc", {
294 dbgs() << "Loading dylib \"" << (DylibPath ? DylibPath : "") << "\" ";
295 if (!DylibPath)
296 dbgs() << "(process symbols)";
297 dbgs() << "\n";
298 });
299 if (!DylibPath)
300 DylibPath = "";
301 auto H = EP.template callB<orcrpctpc::LoadDylib>(DylibPath);
302 DEBUG_WITH_TYPE("orc", {
303 if (H)
304 dbgs() << " got handle " << formatv("{0:x16}", *H) << "\n";
305 else
306 dbgs() << " error, unable to load\n";
307 });
308 return H;
309 }
310
311 Expected<std::vector<tpctypes::LookupResult>>
312 lookupSymbols(ArrayRef<LookupRequest> Request) override {
313 std::vector<orcrpctpc::RemoteLookupRequest> RR;
314 for (auto &E : Request) {
315 RR.push_back({});
316 RR.back().first = E.Handle;
317 for (auto &KV : E.Symbols)
318 RR.back().second.push_back(
319 {(*KV.first).str(),
320 KV.second == SymbolLookupFlags::WeaklyReferencedSymbol});
321 }
322 DEBUG_WITH_TYPE("orc", {
323 dbgs() << "Compound lookup:\n";
324 for (auto &R : Request) {
325 dbgs() << " In " << formatv("{0:x16}", R.Handle) << ": {";
326 bool First = true;
327 for (auto &KV : R.Symbols) {
328 dbgs() << (First ? "" : ",") << " " << *KV.first;
329 First = false;
330 }
331 dbgs() << " }\n";
332 }
333 });
334 return EP.template callB<orcrpctpc::LookupSymbols>(RR);
335 }
336
337 Expected<int32_t> runAsMain(JITTargetAddress MainFnAddr,
338 ArrayRef<std::string> Args) override {
339 DEBUG_WITH_TYPE("orc", {
340 dbgs() << "Running as main: " << formatv("{0:x16}", MainFnAddr)
341 << ", args = [";
342 for (unsigned I = 0; I != Args.size(); ++I)
343 dbgs() << (I ? "," : "") << " \"" << Args[I] << "\"";
344 dbgs() << "]\n";
345 });
346 auto Result = EP.template callB<orcrpctpc::RunMain>(MainFnAddr, Args);
347 DEBUG_WITH_TYPE("orc", {
348 dbgs() << " call to " << formatv("{0:x16}", MainFnAddr);
349 if (Result)
350 dbgs() << " returned result " << *Result << "\n";
351 else
352 dbgs() << " failed\n";
353 });
354 return Result;
355 }
356
357 Expected<tpctypes::WrapperFunctionResult>
358 runWrapper(JITTargetAddress WrapperFnAddr,
359 ArrayRef<uint8_t> ArgBuffer) override {
360 DEBUG_WITH_TYPE("orc", {
361 dbgs() << "Running as wrapper function "
362 << formatv("{0:x16}", WrapperFnAddr) << " with "
363 << formatv("{0:x16}", ArgBuffer.size()) << " argument buffer\n";
364 });
365 auto Result =
366 EP.template callB<orcrpctpc::RunWrapper>(WrapperFnAddr, ArgBuffer);
367 // dbgs() << "Returned from runWrapper...\n";
368 return Result;
369 }
370
371 Error closeConnection(OnCloseConnectionFunction OnCloseConnection) {
372 DEBUG_WITH_TYPE("orc", dbgs() << "Closing connection to remote\n");
373 return EP.template callAsync<orcrpctpc::CloseConnection>(
374 std::move(OnCloseConnection));
375 }
376
377 Error closeConnectionAndWait() {
378 std::promise<MSVCPError> P;
379 auto F = P.get_future();
380 if (auto Err = closeConnection([&](Error Err2) -> Error {
381 P.set_value(std::move(Err2));
382 return Error::success();
383 })) {
384 EP.abandonAllPendingResponses();
385 return joinErrors(std::move(Err), F.get());
386 }
387 return F.get();
388 }
389
390protected:
391 /// Subclasses must call this during construction to initialize the
392 /// TargetTriple and PageSize members.
393 Error initializeORCRPCTPCBase() {
394 if (auto TripleOrErr = EP.template callB<orcrpctpc::GetTargetTriple>())
395 TargetTriple = Triple(*TripleOrErr);
396 else
397 return TripleOrErr.takeError();
398
399 if (auto PageSizeOrErr = EP.template callB<orcrpctpc::GetPageSize>())
400 PageSize = *PageSizeOrErr;
401 else
402 return PageSizeOrErr.takeError();
403
404 return Error::success();
405 }
406
407private:
408 ErrorReporter ReportError;
409 RPCEndpointT &EP;
410};
411
412} // end namespace orc
413} // end namespace llvm
414
415#endif // LLVM_EXECUTIONENGINE_ORC_ORCRPCTARGETPROCESSCONTROL_H
416