1 //===- ExecutorProcessControl.h - Executor process control APIs -*- 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 the executor processes.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H
14 #define LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H
15 
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/ADT/Triple.h"
18 #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
19 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
20 #include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
21 #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
22 #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
23 #include "llvm/ExecutionEngine/Orc/TaskDispatch.h"
24 #include "llvm/Support/DynamicLibrary.h"
25 #include "llvm/Support/MSVCErrorWorkarounds.h"
26 
27 #include <future>
28 #include <mutex>
29 #include <vector>
30 
31 namespace llvm {
32 namespace orc {
33 
34 class ExecutionSession;
35 class SymbolLookupSet;
36 
37 /// ExecutorProcessControl supports interaction with a JIT target process.
38 class ExecutorProcessControl {
39   friend class ExecutionSession;
40 public:
41 
42   /// A handler or incoming WrapperFunctionResults -- either return values from
43   /// callWrapper* calls, or incoming JIT-dispatch requests.
44   ///
45   /// IncomingWFRHandlers are constructible from
46   /// unique_function<void(shared::WrapperFunctionResult)>s using the
47   /// runInPlace function or a RunWithDispatch object.
48   class IncomingWFRHandler {
49     friend class ExecutorProcessControl;
50   public:
51     IncomingWFRHandler() = default;
52     explicit operator bool() const { return !!H; }
53     void operator()(shared::WrapperFunctionResult WFR) { H(std::move(WFR)); }
54   private:
55     template <typename FnT> IncomingWFRHandler(FnT &&Fn)
56       : H(std::forward<FnT>(Fn)) {}
57 
58     unique_function<void(shared::WrapperFunctionResult)> H;
59   };
60 
61   /// Constructs an IncomingWFRHandler from a function object that is callable
62   /// as void(shared::WrapperFunctionResult). The function object will be called
63   /// directly. This should be used with care as it may block listener threads
64   /// in remote EPCs. It is only suitable for simple tasks (e.g. setting a
65   /// future), or for performing some quick analysis before dispatching "real"
66   /// work as a Task.
67   class RunInPlace {
68   public:
69     template <typename FnT>
70     IncomingWFRHandler operator()(FnT &&Fn) {
71       return IncomingWFRHandler(std::forward<FnT>(Fn));
72     }
73   };
74 
75   /// Constructs an IncomingWFRHandler from a function object by creating a new
76   /// function object that dispatches the original using a TaskDispatcher,
77   /// wrapping the original as a GenericNamedTask.
78   ///
79   /// This is the default approach for running WFR handlers.
80   class RunAsTask {
81   public:
82     RunAsTask(TaskDispatcher &D) : D(D) {}
83 
84     template <typename FnT>
85     IncomingWFRHandler operator()(FnT &&Fn) {
86       return IncomingWFRHandler(
87           [&D = this->D, Fn = std::move(Fn)]
88           (shared::WrapperFunctionResult WFR) mutable {
89               D.dispatch(
90                 makeGenericNamedTask(
91                     [Fn = std::move(Fn), WFR = std::move(WFR)]() mutable {
92                       Fn(std::move(WFR));
93                     }, "WFR handler task"));
94           });
95     }
96   private:
97     TaskDispatcher &D;
98   };
99 
100   /// APIs for manipulating memory in the target process.
101   class MemoryAccess {
102   public:
103     /// Callback function for asynchronous writes.
104     using WriteResultFn = unique_function<void(Error)>;
105 
106     virtual ~MemoryAccess();
107 
108     virtual void writeUInt8sAsync(ArrayRef<tpctypes::UInt8Write> Ws,
109                                   WriteResultFn OnWriteComplete) = 0;
110 
111     virtual void writeUInt16sAsync(ArrayRef<tpctypes::UInt16Write> Ws,
112                                    WriteResultFn OnWriteComplete) = 0;
113 
114     virtual void writeUInt32sAsync(ArrayRef<tpctypes::UInt32Write> Ws,
115                                    WriteResultFn OnWriteComplete) = 0;
116 
117     virtual void writeUInt64sAsync(ArrayRef<tpctypes::UInt64Write> Ws,
118                                    WriteResultFn OnWriteComplete) = 0;
119 
120     virtual void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
121                                    WriteResultFn OnWriteComplete) = 0;
122 
123     Error writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws) {
124       std::promise<MSVCPError> ResultP;
125       auto ResultF = ResultP.get_future();
126       writeUInt8sAsync(Ws,
127                        [&](Error Err) { ResultP.set_value(std::move(Err)); });
128       return ResultF.get();
129     }
130 
131     Error writeUInt16s(ArrayRef<tpctypes::UInt16Write> Ws) {
132       std::promise<MSVCPError> ResultP;
133       auto ResultF = ResultP.get_future();
134       writeUInt16sAsync(Ws,
135                         [&](Error Err) { ResultP.set_value(std::move(Err)); });
136       return ResultF.get();
137     }
138 
139     Error writeUInt32s(ArrayRef<tpctypes::UInt32Write> Ws) {
140       std::promise<MSVCPError> ResultP;
141       auto ResultF = ResultP.get_future();
142       writeUInt32sAsync(Ws,
143                         [&](Error Err) { ResultP.set_value(std::move(Err)); });
144       return ResultF.get();
145     }
146 
147     Error writeUInt64s(ArrayRef<tpctypes::UInt64Write> Ws) {
148       std::promise<MSVCPError> ResultP;
149       auto ResultF = ResultP.get_future();
150       writeUInt64sAsync(Ws,
151                         [&](Error Err) { ResultP.set_value(std::move(Err)); });
152       return ResultF.get();
153     }
154 
155     Error writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws) {
156       std::promise<MSVCPError> ResultP;
157       auto ResultF = ResultP.get_future();
158       writeBuffersAsync(Ws,
159                         [&](Error Err) { ResultP.set_value(std::move(Err)); });
160       return ResultF.get();
161     }
162   };
163 
164   /// A pair of a dylib and a set of symbols to be looked up.
165   struct LookupRequest {
166     LookupRequest(tpctypes::DylibHandle Handle, const SymbolLookupSet &Symbols)
167         : Handle(Handle), Symbols(Symbols) {}
168     tpctypes::DylibHandle Handle;
169     const SymbolLookupSet &Symbols;
170   };
171 
172   /// Contains the address of the dispatch function and context that the ORC
173   /// runtime can use to call functions in the JIT.
174   struct JITDispatchInfo {
175     ExecutorAddr JITDispatchFunction;
176     ExecutorAddr JITDispatchContext;
177   };
178 
179   ExecutorProcessControl(std::shared_ptr<SymbolStringPool> SSP,
180                          std::unique_ptr<TaskDispatcher> D)
181     : SSP(std::move(SSP)), D(std::move(D)) {}
182 
183   virtual ~ExecutorProcessControl();
184 
185   /// Return the ExecutionSession associated with this instance.
186   /// Not callable until the ExecutionSession has been associated.
187   ExecutionSession &getExecutionSession() {
188     assert(ES && "No ExecutionSession associated yet");
189     return *ES;
190   }
191 
192   /// Intern a symbol name in the SymbolStringPool.
193   SymbolStringPtr intern(StringRef SymName) { return SSP->intern(SymName); }
194 
195   /// Return a shared pointer to the SymbolStringPool for this instance.
196   std::shared_ptr<SymbolStringPool> getSymbolStringPool() const { return SSP; }
197 
198   TaskDispatcher &getDispatcher() { return *D; }
199 
200   /// Return the Triple for the target process.
201   const Triple &getTargetTriple() const { return TargetTriple; }
202 
203   /// Get the page size for the target process.
204   unsigned getPageSize() const { return PageSize; }
205 
206   /// Get the JIT dispatch function and context address for the executor.
207   const JITDispatchInfo &getJITDispatchInfo() const { return JDI; }
208 
209   /// Return a MemoryAccess object for the target process.
210   MemoryAccess &getMemoryAccess() const {
211     assert(MemAccess && "No MemAccess object set.");
212     return *MemAccess;
213   }
214 
215   /// Return a JITLinkMemoryManager for the target process.
216   jitlink::JITLinkMemoryManager &getMemMgr() const {
217     assert(MemMgr && "No MemMgr object set");
218     return *MemMgr;
219   }
220 
221   /// Returns the bootstrap symbol map.
222   const StringMap<ExecutorAddr> &getBootstrapSymbolsMap() const {
223     return BootstrapSymbols;
224   }
225 
226   /// For each (ExecutorAddr&, StringRef) pair, looks up the string in the
227   /// bootstrap symbols map and writes its address to the ExecutorAddr if
228   /// found. If any symbol is not found then the function returns an error.
229   Error getBootstrapSymbols(
230       ArrayRef<std::pair<ExecutorAddr &, StringRef>> Pairs) const {
231     for (const auto &KV : Pairs) {
232       auto I = BootstrapSymbols.find(KV.second);
233       if (I == BootstrapSymbols.end())
234         return make_error<StringError>("Symbol \"" + KV.second +
235                                            "\" not found "
236                                            "in bootstrap symbols map",
237                                        inconvertibleErrorCode());
238 
239       KV.first = I->second;
240     }
241     return Error::success();
242   }
243 
244   /// Load the dynamic library at the given path and return a handle to it.
245   /// If LibraryPath is null this function will return the global handle for
246   /// the target process.
247   virtual Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) = 0;
248 
249   /// Search for symbols in the target process.
250   ///
251   /// The result of the lookup is a 2-dimentional array of target addresses
252   /// that correspond to the lookup order. If a required symbol is not
253   /// found then this method will return an error. If a weakly referenced
254   /// symbol is not found then it be assigned a '0' value.
255   virtual Expected<std::vector<tpctypes::LookupResult>>
256   lookupSymbols(ArrayRef<LookupRequest> Request) = 0;
257 
258   /// Run function with a main-like signature.
259   virtual Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
260                                       ArrayRef<std::string> Args) = 0;
261 
262   // TODO: move this to ORC runtime.
263   /// Run function with a int (*)(void) signature.
264   virtual Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) = 0;
265 
266   // TODO: move this to ORC runtime.
267   /// Run function with a int (*)(int) signature.
268   virtual Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr,
269                                              int Arg) = 0;
270 
271   /// Run a wrapper function in the executor. The given WFRHandler will be
272   /// called on the result when it is returned.
273   ///
274   /// The wrapper function should be callable as:
275   ///
276   /// \code{.cpp}
277   ///   CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size);
278   /// \endcode{.cpp}
279   virtual void callWrapperAsync(ExecutorAddr WrapperFnAddr,
280                                 IncomingWFRHandler OnComplete,
281                                 ArrayRef<char> ArgBuffer) = 0;
282 
283   /// Run a wrapper function in the executor using the given Runner to dispatch
284   /// OnComplete when the result is ready.
285   template <typename RunPolicyT, typename FnT>
286   void callWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr,
287                         FnT &&OnComplete, ArrayRef<char> ArgBuffer) {
288     callWrapperAsync(
289         WrapperFnAddr, Runner(std::forward<FnT>(OnComplete)), ArgBuffer);
290   }
291 
292   /// Run a wrapper function in the executor. OnComplete will be dispatched
293   /// as a GenericNamedTask using this instance's TaskDispatch object.
294   template <typename FnT>
295   void callWrapperAsync(ExecutorAddr WrapperFnAddr, FnT &&OnComplete,
296                         ArrayRef<char> ArgBuffer) {
297     callWrapperAsync(RunAsTask(*D), WrapperFnAddr,
298                      std::forward<FnT>(OnComplete), ArgBuffer);
299   }
300 
301   /// Run a wrapper function in the executor. The wrapper function should be
302   /// callable as:
303   ///
304   /// \code{.cpp}
305   ///   CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size);
306   /// \endcode{.cpp}
307   shared::WrapperFunctionResult callWrapper(ExecutorAddr WrapperFnAddr,
308                                             ArrayRef<char> ArgBuffer) {
309     std::promise<shared::WrapperFunctionResult> RP;
310     auto RF = RP.get_future();
311     callWrapperAsync(
312         RunInPlace(), WrapperFnAddr,
313         [&](shared::WrapperFunctionResult R) {
314           RP.set_value(std::move(R));
315         }, ArgBuffer);
316     return RF.get();
317   }
318 
319   /// Run a wrapper function using SPS to serialize the arguments and
320   /// deserialize the results.
321   template <typename SPSSignature, typename RunPolicyT, typename SendResultT,
322             typename... ArgTs>
323   void callSPSWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr,
324                            SendResultT &&SendResult, const ArgTs &...Args) {
325     shared::WrapperFunction<SPSSignature>::callAsync(
326         [this, WrapperFnAddr, Runner = std::move(Runner)]
327         (auto &&SendResult, const char *ArgData, size_t ArgSize) mutable {
328           this->callWrapperAsync(std::move(Runner), WrapperFnAddr,
329                                  std::move(SendResult),
330                                  ArrayRef<char>(ArgData, ArgSize));
331         },
332         std::forward<SendResultT>(SendResult), Args...);
333   }
334 
335   /// Run a wrapper function using SPS to serialize the arguments and
336   /// deserialize the results.
337   template <typename SPSSignature, typename SendResultT, typename... ArgTs>
338   void callSPSWrapperAsync(ExecutorAddr WrapperFnAddr, SendResultT &&SendResult,
339                            const ArgTs &...Args) {
340     callSPSWrapperAsync<SPSSignature>(RunAsTask(*D), WrapperFnAddr,
341                                       std::forward<SendResultT>(SendResult),
342                                       Args...);
343   }
344 
345   /// Run a wrapper function using SPS to serialize the arguments and
346   /// deserialize the results.
347   ///
348   /// If SPSSignature is a non-void function signature then the second argument
349   /// (the first in the Args list) should be a reference to a return value.
350   template <typename SPSSignature, typename... WrapperCallArgTs>
351   Error callSPSWrapper(ExecutorAddr WrapperFnAddr,
352                        WrapperCallArgTs &&...WrapperCallArgs) {
353     return shared::WrapperFunction<SPSSignature>::call(
354         [this, WrapperFnAddr](const char *ArgData, size_t ArgSize) {
355           return callWrapper(WrapperFnAddr, ArrayRef<char>(ArgData, ArgSize));
356         },
357         std::forward<WrapperCallArgTs>(WrapperCallArgs)...);
358   }
359 
360   /// Disconnect from the target process.
361   ///
362   /// This should be called after the JIT session is shut down.
363   virtual Error disconnect() = 0;
364 
365 protected:
366 
367   std::shared_ptr<SymbolStringPool> SSP;
368   std::unique_ptr<TaskDispatcher> D;
369   ExecutionSession *ES = nullptr;
370   Triple TargetTriple;
371   unsigned PageSize = 0;
372   JITDispatchInfo JDI;
373   MemoryAccess *MemAccess = nullptr;
374   jitlink::JITLinkMemoryManager *MemMgr = nullptr;
375   StringMap<ExecutorAddr> BootstrapSymbols;
376 };
377 
378 /// A ExecutorProcessControl instance that asserts if any of its methods are
379 /// used. Suitable for use is unit tests, and by ORC clients who haven't moved
380 /// to ExecutorProcessControl-based APIs yet.
381 class UnsupportedExecutorProcessControl : public ExecutorProcessControl {
382 public:
383   UnsupportedExecutorProcessControl(
384       std::shared_ptr<SymbolStringPool> SSP = nullptr,
385       std::unique_ptr<TaskDispatcher> D = nullptr,
386       const std::string &TT = "", unsigned PageSize = 0)
387       : ExecutorProcessControl(SSP ? std::move(SSP)
388                                : std::make_shared<SymbolStringPool>(),
389                                D ? std::move(D)
390                                : std::make_unique<InPlaceTaskDispatcher>()) {
391     this->TargetTriple = Triple(TT);
392     this->PageSize = PageSize;
393   }
394 
395   Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override {
396     llvm_unreachable("Unsupported");
397   }
398 
399   Expected<std::vector<tpctypes::LookupResult>>
400   lookupSymbols(ArrayRef<LookupRequest> Request) override {
401     llvm_unreachable("Unsupported");
402   }
403 
404   Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
405                               ArrayRef<std::string> Args) override {
406     llvm_unreachable("Unsupported");
407   }
408 
409   Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) override {
410     llvm_unreachable("Unsupported");
411   }
412 
413   Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override {
414     llvm_unreachable("Unsupported");
415   }
416 
417   void callWrapperAsync(ExecutorAddr WrapperFnAddr,
418                         IncomingWFRHandler OnComplete,
419                         ArrayRef<char> ArgBuffer) override {
420     llvm_unreachable("Unsupported");
421   }
422 
423   Error disconnect() override { return Error::success(); }
424 };
425 
426 /// A ExecutorProcessControl implementation targeting the current process.
427 class SelfExecutorProcessControl
428     : public ExecutorProcessControl,
429       private ExecutorProcessControl::MemoryAccess {
430 public:
431   SelfExecutorProcessControl(
432       std::shared_ptr<SymbolStringPool> SSP, std::unique_ptr<TaskDispatcher> D,
433       Triple TargetTriple, unsigned PageSize,
434       std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr);
435 
436   /// Create a SelfExecutorProcessControl with the given symbol string pool and
437   /// memory manager.
438   /// If no symbol string pool is given then one will be created.
439   /// If no memory manager is given a jitlink::InProcessMemoryManager will
440   /// be created and used by default.
441   static Expected<std::unique_ptr<SelfExecutorProcessControl>>
442   Create(std::shared_ptr<SymbolStringPool> SSP = nullptr,
443          std::unique_ptr<TaskDispatcher> D = nullptr,
444          std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr = nullptr);
445 
446   Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override;
447 
448   Expected<std::vector<tpctypes::LookupResult>>
449   lookupSymbols(ArrayRef<LookupRequest> Request) override;
450 
451   Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
452                               ArrayRef<std::string> Args) override;
453 
454   Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) override;
455 
456   Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override;
457 
458   void callWrapperAsync(ExecutorAddr WrapperFnAddr,
459                         IncomingWFRHandler OnComplete,
460                         ArrayRef<char> ArgBuffer) override;
461 
462   Error disconnect() override;
463 
464 private:
465   void writeUInt8sAsync(ArrayRef<tpctypes::UInt8Write> Ws,
466                         WriteResultFn OnWriteComplete) override;
467 
468   void writeUInt16sAsync(ArrayRef<tpctypes::UInt16Write> Ws,
469                          WriteResultFn OnWriteComplete) override;
470 
471   void writeUInt32sAsync(ArrayRef<tpctypes::UInt32Write> Ws,
472                          WriteResultFn OnWriteComplete) override;
473 
474   void writeUInt64sAsync(ArrayRef<tpctypes::UInt64Write> Ws,
475                          WriteResultFn OnWriteComplete) override;
476 
477   void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
478                          WriteResultFn OnWriteComplete) override;
479 
480   static shared::CWrapperFunctionResult
481   jitDispatchViaWrapperFunctionManager(void *Ctx, const void *FnTag,
482                                        const char *Data, size_t Size);
483 
484   std::unique_ptr<jitlink::JITLinkMemoryManager> OwnedMemMgr;
485   char GlobalManglingPrefix = 0;
486 };
487 
488 } // end namespace orc
489 } // end namespace llvm
490 
491 #endif // LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H
492