xref: /netbsd-src/external/apache2/llvm/dist/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCTargetProcessControl.h (revision 82d56013d7b633d116a93943de88e08335357a7c)
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 
22 namespace llvm {
23 namespace orc {
24 
25 /// JITLinkMemoryManager implementation for a process connected via an ORC RPC
26 /// endpoint.
27 template <typename OrcRPCTPCImplT>
28 class OrcRPCTPCJITLinkMemoryManager : public jitlink::JITLinkMemoryManager {
29 private:
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 
43 public:
44   class OrcRPCAllocation : public Allocation {
45   public:
OrcRPCAllocation(OrcRPCTPCJITLinkMemoryManager<OrcRPCTPCImplT> & Parent,HostAllocMap HostAllocs,TargetAllocMap TargetAllocs)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 
~OrcRPCAllocation()54     ~OrcRPCAllocation() override {
55       assert(TargetAllocs.empty() && "failed to deallocate");
56     }
57 
getWorkingMemory(ProtectionFlags Seg)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 
getTargetMemory(ProtectionFlags Seg)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 
finalizeAsync(FinalizeContinuation OnFinalize)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 
deallocate()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 
OrcRPCTPCJITLinkMemoryManager(OrcRPCTPCImplT & Parent)148   OrcRPCTPCJITLinkMemoryManager(OrcRPCTPCImplT &Parent) : Parent(Parent) {}
149 
150   Expected<std::unique_ptr<Allocation>>
allocate(const jitlink::JITLinkDylib * JD,const SegmentsRequestMap & Request)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 
216 private:
reportError(Error Err)217   void reportError(Error Err) { Parent.reportError(std::move(Err)); }
218 
getEndpoint()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.
228 template <typename OrcRPCTPCImplT>
229 class OrcRPCTPCMemoryAccess : public TargetProcessControl::MemoryAccess {
230 public:
OrcRPCTPCMemoryAccess(OrcRPCTPCImplT & Parent)231   OrcRPCTPCMemoryAccess(OrcRPCTPCImplT &Parent) : Parent(Parent) {}
232 
writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws,WriteResultFn OnWriteComplete)233   void writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws,
234                    WriteResultFn OnWriteComplete) override {
235     writeViaRPC<orcrpctpc::WriteUInt8s>(Ws, std::move(OnWriteComplete));
236   }
237 
writeUInt16s(ArrayRef<tpctypes::UInt16Write> Ws,WriteResultFn OnWriteComplete)238   void writeUInt16s(ArrayRef<tpctypes::UInt16Write> Ws,
239                     WriteResultFn OnWriteComplete) override {
240     writeViaRPC<orcrpctpc::WriteUInt16s>(Ws, std::move(OnWriteComplete));
241   }
242 
writeUInt32s(ArrayRef<tpctypes::UInt32Write> Ws,WriteResultFn OnWriteComplete)243   void writeUInt32s(ArrayRef<tpctypes::UInt32Write> Ws,
244                     WriteResultFn OnWriteComplete) override {
245     writeViaRPC<orcrpctpc::WriteUInt32s>(Ws, std::move(OnWriteComplete));
246   }
247 
writeUInt64s(ArrayRef<tpctypes::UInt64Write> Ws,WriteResultFn OnWriteComplete)248   void writeUInt64s(ArrayRef<tpctypes::UInt64Write> Ws,
249                     WriteResultFn OnWriteComplete) override {
250     writeViaRPC<orcrpctpc::WriteUInt64s>(Ws, std::move(OnWriteComplete));
251   }
252 
writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws,WriteResultFn OnWriteComplete)253   void writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws,
254                     WriteResultFn OnWriteComplete) override {
255     writeViaRPC<orcrpctpc::WriteBuffers>(Ws, std::move(OnWriteComplete));
256   }
257 
258 private:
259   template <typename WriteRPCFunction, typename WriteElementT>
writeViaRPC(ArrayRef<WriteElementT> Ws,WriteResultFn OnWriteComplete)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.
276 template <typename RPCEndpointT>
277 class OrcRPCTargetProcessControlBase : public TargetProcessControl {
278 public:
279   using ErrorReporter = unique_function<void(Error)>;
280 
281   using OnCloseConnectionFunction = unique_function<Error(Error)>;
282 
OrcRPCTargetProcessControlBase(std::shared_ptr<SymbolStringPool> SSP,RPCEndpointT & EP,ErrorReporter ReportError)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 
reportError(Error Err)288   void reportError(Error Err) { ReportError(std::move(Err)); }
289 
getEndpoint()290   RPCEndpointT &getEndpoint() { return EP; }
291 
loadDylib(const char * DylibPath)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>>
lookupSymbols(ArrayRef<LookupRequest> Request)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 
runAsMain(JITTargetAddress MainFnAddr,ArrayRef<std::string> Args)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>
runWrapper(JITTargetAddress WrapperFnAddr,ArrayRef<uint8_t> ArgBuffer)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 
closeConnection(OnCloseConnectionFunction OnCloseConnection)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 
closeConnectionAndWait()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 
390 protected:
391   /// Subclasses must call this during construction to initialize the
392   /// TargetTriple and PageSize members.
initializeORCRPCTPCBase()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 
407 private:
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