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/Optional.h" 17 #include "llvm/ADT/StringRef.h" 18 #include "llvm/ADT/Triple.h" 19 #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" 20 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" 21 #include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" 22 #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h" 23 #include "llvm/ExecutionEngine/Orc/SymbolStringPool.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 41 public: 42 /// Sender to return the result of a WrapperFunction executed in the JIT. 43 using SendResultFunction = 44 unique_function<void(shared::WrapperFunctionResult)>; 45 46 /// APIs for manipulating memory in the target process. 47 class MemoryAccess { 48 public: 49 /// Callback function for asynchronous writes. 50 using WriteResultFn = unique_function<void(Error)>; 51 52 virtual ~MemoryAccess(); 53 54 virtual void writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws, 55 WriteResultFn OnWriteComplete) = 0; 56 57 virtual void writeUInt16s(ArrayRef<tpctypes::UInt16Write> Ws, 58 WriteResultFn OnWriteComplete) = 0; 59 60 virtual void writeUInt32s(ArrayRef<tpctypes::UInt32Write> Ws, 61 WriteResultFn OnWriteComplete) = 0; 62 63 virtual void writeUInt64s(ArrayRef<tpctypes::UInt64Write> Ws, 64 WriteResultFn OnWriteComplete) = 0; 65 66 virtual void writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws, 67 WriteResultFn OnWriteComplete) = 0; 68 writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws)69 Error writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws) { 70 std::promise<MSVCPError> ResultP; 71 auto ResultF = ResultP.get_future(); 72 writeUInt8s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); 73 return ResultF.get(); 74 } 75 writeUInt16s(ArrayRef<tpctypes::UInt16Write> Ws)76 Error writeUInt16s(ArrayRef<tpctypes::UInt16Write> Ws) { 77 std::promise<MSVCPError> ResultP; 78 auto ResultF = ResultP.get_future(); 79 writeUInt16s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); 80 return ResultF.get(); 81 } 82 writeUInt32s(ArrayRef<tpctypes::UInt32Write> Ws)83 Error writeUInt32s(ArrayRef<tpctypes::UInt32Write> Ws) { 84 std::promise<MSVCPError> ResultP; 85 auto ResultF = ResultP.get_future(); 86 writeUInt32s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); 87 return ResultF.get(); 88 } 89 writeUInt64s(ArrayRef<tpctypes::UInt64Write> Ws)90 Error writeUInt64s(ArrayRef<tpctypes::UInt64Write> Ws) { 91 std::promise<MSVCPError> ResultP; 92 auto ResultF = ResultP.get_future(); 93 writeUInt64s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); 94 return ResultF.get(); 95 } 96 writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws)97 Error writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws) { 98 std::promise<MSVCPError> ResultP; 99 auto ResultF = ResultP.get_future(); 100 writeBuffers(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); 101 return ResultF.get(); 102 } 103 }; 104 105 /// A pair of a dylib and a set of symbols to be looked up. 106 struct LookupRequest { LookupRequestLookupRequest107 LookupRequest(tpctypes::DylibHandle Handle, const SymbolLookupSet &Symbols) 108 : Handle(Handle), Symbols(Symbols) {} 109 tpctypes::DylibHandle Handle; 110 const SymbolLookupSet &Symbols; 111 }; 112 113 /// Contains the address of the dispatch function and context that the ORC 114 /// runtime can use to call functions in the JIT. 115 struct JITDispatchInfo { 116 ExecutorAddress JITDispatchFunctionAddress; 117 ExecutorAddress JITDispatchContextAddress; 118 }; 119 120 virtual ~ExecutorProcessControl(); 121 122 /// Return the ExecutionSession associated with this instance. 123 /// Not callable until the ExecutionSession has been associated. getExecutionSession()124 ExecutionSession &getExecutionSession() { 125 assert(ES && "No ExecutionSession associated yet"); 126 return *ES; 127 } 128 129 /// Intern a symbol name in the SymbolStringPool. intern(StringRef SymName)130 SymbolStringPtr intern(StringRef SymName) { return SSP->intern(SymName); } 131 132 /// Return a shared pointer to the SymbolStringPool for this instance. getSymbolStringPool()133 std::shared_ptr<SymbolStringPool> getSymbolStringPool() const { return SSP; } 134 135 /// Return the Triple for the target process. getTargetTriple()136 const Triple &getTargetTriple() const { return TargetTriple; } 137 138 /// Get the page size for the target process. getPageSize()139 unsigned getPageSize() const { return PageSize; } 140 141 /// Get the JIT dispatch function and context address for the executor. getJITDispatchInfo()142 const JITDispatchInfo &getJITDispatchInfo() const { return JDI; } 143 144 /// Return a MemoryAccess object for the target process. getMemoryAccess()145 MemoryAccess &getMemoryAccess() const { 146 assert(MemAccess && "No MemAccess object set."); 147 return *MemAccess; 148 } 149 150 /// Return a JITLinkMemoryManager for the target process. getMemMgr()151 jitlink::JITLinkMemoryManager &getMemMgr() const { 152 assert(MemMgr && "No MemMgr object set"); 153 return *MemMgr; 154 } 155 156 /// Load the dynamic library at the given path and return a handle to it. 157 /// If LibraryPath is null this function will return the global handle for 158 /// the target process. 159 virtual Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) = 0; 160 161 /// Search for symbols in the target process. 162 /// 163 /// The result of the lookup is a 2-dimentional array of target addresses 164 /// that correspond to the lookup order. If a required symbol is not 165 /// found then this method will return an error. If a weakly referenced 166 /// symbol is not found then it be assigned a '0' value in the result. 167 /// that correspond to the lookup order. 168 virtual Expected<std::vector<tpctypes::LookupResult>> 169 lookupSymbols(ArrayRef<LookupRequest> Request) = 0; 170 171 /// Run function with a main-like signature. 172 virtual Expected<int32_t> runAsMain(JITTargetAddress MainFnAddr, 173 ArrayRef<std::string> Args) = 0; 174 175 /// Run a wrapper function in the executor. 176 /// 177 /// The wrapper function should be callable as: 178 /// 179 /// \code{.cpp} 180 /// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size); 181 /// \endcode{.cpp} 182 /// 183 /// The given OnComplete function will be called to return the result. 184 virtual void callWrapperAsync(SendResultFunction OnComplete, 185 JITTargetAddress WrapperFnAddr, 186 ArrayRef<char> ArgBuffer) = 0; 187 188 /// Disconnect from the target process. 189 /// 190 /// This should be called after the JIT session is shut down. 191 virtual Error disconnect() = 0; 192 193 protected: ExecutorProcessControl(std::shared_ptr<SymbolStringPool> SSP)194 ExecutorProcessControl(std::shared_ptr<SymbolStringPool> SSP) 195 : SSP(std::move(SSP)) {} 196 197 std::shared_ptr<SymbolStringPool> SSP; 198 ExecutionSession *ES = nullptr; 199 Triple TargetTriple; 200 unsigned PageSize = 0; 201 JITDispatchInfo JDI; 202 MemoryAccess *MemAccess = nullptr; 203 jitlink::JITLinkMemoryManager *MemMgr = nullptr; 204 }; 205 206 /// A ExecutorProcessControl instance that asserts if any of its methods are 207 /// used. Suitable for use is unit tests, and by ORC clients who haven't moved 208 /// to ExecutorProcessControl-based APIs yet. 209 class UnsupportedExecutorProcessControl : public ExecutorProcessControl { 210 public: 211 UnsupportedExecutorProcessControl( 212 std::shared_ptr<SymbolStringPool> SSP = nullptr, 213 const std::string &TT = "", unsigned PageSize = 0) 214 : ExecutorProcessControl(SSP ? std::move(SSP) 215 : std::make_shared<SymbolStringPool>()) { 216 this->TargetTriple = Triple(TT); 217 this->PageSize = PageSize; 218 } 219 loadDylib(const char * DylibPath)220 Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override { 221 llvm_unreachable("Unsupported"); 222 } 223 224 Expected<std::vector<tpctypes::LookupResult>> lookupSymbols(ArrayRef<LookupRequest> Request)225 lookupSymbols(ArrayRef<LookupRequest> Request) override { 226 llvm_unreachable("Unsupported"); 227 } 228 runAsMain(JITTargetAddress MainFnAddr,ArrayRef<std::string> Args)229 Expected<int32_t> runAsMain(JITTargetAddress MainFnAddr, 230 ArrayRef<std::string> Args) override { 231 llvm_unreachable("Unsupported"); 232 } 233 callWrapperAsync(SendResultFunction OnComplete,JITTargetAddress WrapperFnAddr,ArrayRef<char> ArgBuffer)234 void callWrapperAsync(SendResultFunction OnComplete, 235 JITTargetAddress WrapperFnAddr, 236 ArrayRef<char> ArgBuffer) override { 237 llvm_unreachable("Unsupported"); 238 } 239 disconnect()240 Error disconnect() override { return Error::success(); } 241 }; 242 243 /// A ExecutorProcessControl implementation targeting the current process. 244 class SelfExecutorProcessControl 245 : public ExecutorProcessControl, 246 private ExecutorProcessControl::MemoryAccess { 247 public: 248 SelfExecutorProcessControl( 249 std::shared_ptr<SymbolStringPool> SSP, Triple TargetTriple, 250 unsigned PageSize, std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr); 251 252 /// Create a SelfExecutorProcessControl with the given symbol string pool and 253 /// memory manager. 254 /// If no symbol string pool is given then one will be created. 255 /// If no memory manager is given a jitlink::InProcessMemoryManager will 256 /// be created and used by default. 257 static Expected<std::unique_ptr<SelfExecutorProcessControl>> 258 Create(std::shared_ptr<SymbolStringPool> SSP = nullptr, 259 std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr = nullptr); 260 261 Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override; 262 263 Expected<std::vector<tpctypes::LookupResult>> 264 lookupSymbols(ArrayRef<LookupRequest> Request) override; 265 266 Expected<int32_t> runAsMain(JITTargetAddress MainFnAddr, 267 ArrayRef<std::string> Args) override; 268 269 void callWrapperAsync(SendResultFunction OnComplete, 270 JITTargetAddress WrapperFnAddr, 271 ArrayRef<char> ArgBuffer) override; 272 273 Error disconnect() override; 274 275 private: 276 void writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws, 277 WriteResultFn OnWriteComplete) override; 278 279 void writeUInt16s(ArrayRef<tpctypes::UInt16Write> Ws, 280 WriteResultFn OnWriteComplete) override; 281 282 void writeUInt32s(ArrayRef<tpctypes::UInt32Write> Ws, 283 WriteResultFn OnWriteComplete) override; 284 285 void writeUInt64s(ArrayRef<tpctypes::UInt64Write> Ws, 286 WriteResultFn OnWriteComplete) override; 287 288 void writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws, 289 WriteResultFn OnWriteComplete) override; 290 291 static shared::detail::CWrapperFunctionResult 292 jitDispatchViaWrapperFunctionManager(void *Ctx, const void *FnTag, 293 const char *Data, size_t Size); 294 295 std::unique_ptr<jitlink::JITLinkMemoryManager> OwnedMemMgr; 296 char GlobalManglingPrefix = 0; 297 std::vector<std::unique_ptr<sys::DynamicLibrary>> DynamicLibraries; 298 }; 299 300 } // end namespace orc 301 } // end namespace llvm 302 303 #endif // LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H 304