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; } operator()53 void operator()(shared::WrapperFunctionResult WFR) { H(std::move(WFR)); } 54 private: IncomingWFRHandler(FnT && Fn)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> operator()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: RunAsTask(TaskDispatcher & D)82 RunAsTask(TaskDispatcher &D) : D(D) {} 83 84 template <typename FnT> operator()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 writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws)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 writeUInt16s(ArrayRef<tpctypes::UInt16Write> Ws)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 writeUInt32s(ArrayRef<tpctypes::UInt32Write> Ws)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 writeUInt64s(ArrayRef<tpctypes::UInt64Write> Ws)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 writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws)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 { LookupRequestLookupRequest166 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 ExecutorProcessControl(std::shared_ptr<SymbolStringPool> SSP,std::unique_ptr<TaskDispatcher> D)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. getExecutionSession()187 ExecutionSession &getExecutionSession() { 188 assert(ES && "No ExecutionSession associated yet"); 189 return *ES; 190 } 191 192 /// Intern a symbol name in the SymbolStringPool. intern(StringRef SymName)193 SymbolStringPtr intern(StringRef SymName) { return SSP->intern(SymName); } 194 195 /// Return a shared pointer to the SymbolStringPool for this instance. getSymbolStringPool()196 std::shared_ptr<SymbolStringPool> getSymbolStringPool() const { return SSP; } 197 getDispatcher()198 TaskDispatcher &getDispatcher() { return *D; } 199 200 /// Return the Triple for the target process. getTargetTriple()201 const Triple &getTargetTriple() const { return TargetTriple; } 202 203 /// Get the page size for the target process. getPageSize()204 unsigned getPageSize() const { return PageSize; } 205 206 /// Get the JIT dispatch function and context address for the executor. getJITDispatchInfo()207 const JITDispatchInfo &getJITDispatchInfo() const { return JDI; } 208 209 /// Return a MemoryAccess object for the target process. getMemoryAccess()210 MemoryAccess &getMemoryAccess() const { 211 assert(MemAccess && "No MemAccess object set."); 212 return *MemAccess; 213 } 214 215 /// Return a JITLinkMemoryManager for the target process. getMemMgr()216 jitlink::JITLinkMemoryManager &getMemMgr() const { 217 assert(MemMgr && "No MemMgr object set"); 218 return *MemMgr; 219 } 220 221 /// Returns the bootstrap symbol map. getBootstrapSymbolsMap()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. getBootstrapSymbols(ArrayRef<std::pair<ExecutorAddr &,StringRef>> Pairs)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> callWrapperAsync(RunPolicyT && Runner,ExecutorAddr WrapperFnAddr,FnT && OnComplete,ArrayRef<char> ArgBuffer)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> callWrapperAsync(ExecutorAddr WrapperFnAddr,FnT && OnComplete,ArrayRef<char> ArgBuffer)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} callWrapper(ExecutorAddr WrapperFnAddr,ArrayRef<char> ArgBuffer)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> callSPSWrapperAsync(RunPolicyT && Runner,ExecutorAddr WrapperFnAddr,SendResultT && SendResult,const ArgTs &...Args)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> callSPSWrapperAsync(ExecutorAddr WrapperFnAddr,SendResultT && SendResult,const ArgTs &...Args)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> callSPSWrapper(ExecutorAddr WrapperFnAddr,WrapperCallArgTs &&...WrapperCallArgs)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 loadDylib(const char * DylibPath)395 Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override { 396 llvm_unreachable("Unsupported"); 397 } 398 399 Expected<std::vector<tpctypes::LookupResult>> lookupSymbols(ArrayRef<LookupRequest> Request)400 lookupSymbols(ArrayRef<LookupRequest> Request) override { 401 llvm_unreachable("Unsupported"); 402 } 403 runAsMain(ExecutorAddr MainFnAddr,ArrayRef<std::string> Args)404 Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr, 405 ArrayRef<std::string> Args) override { 406 llvm_unreachable("Unsupported"); 407 } 408 runAsVoidFunction(ExecutorAddr VoidFnAddr)409 Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) override { 410 llvm_unreachable("Unsupported"); 411 } 412 runAsIntFunction(ExecutorAddr IntFnAddr,int Arg)413 Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override { 414 llvm_unreachable("Unsupported"); 415 } 416 callWrapperAsync(ExecutorAddr WrapperFnAddr,IncomingWFRHandler OnComplete,ArrayRef<char> ArgBuffer)417 void callWrapperAsync(ExecutorAddr WrapperFnAddr, 418 IncomingWFRHandler OnComplete, 419 ArrayRef<char> ArgBuffer) override { 420 llvm_unreachable("Unsupported"); 421 } 422 disconnect()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