1 //===--- EPCIndirectionUtils.h - EPC based indirection utils ----*- 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 // Indirection utilities (stubs, trampolines, lazy call-throughs) that use the
10 // ExecutorProcessControl API to interact with the executor process.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_EXECUTIONENGINE_ORC_EPCINDIRECTIONUTILS_H
15 #define LLVM_EXECUTIONENGINE_ORC_EPCINDIRECTIONUTILS_H
16 
17 #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
18 #include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
19 #include "llvm/ExecutionEngine/Orc/LazyReexports.h"
20 
21 #include <mutex>
22 
23 namespace llvm {
24 namespace orc {
25 
26 class ExecutorProcessControl;
27 
28 /// Provides ExecutorProcessControl based indirect stubs, trampoline pool and
29 /// lazy call through manager.
30 class EPCIndirectionUtils {
31   friend class EPCIndirectionUtilsAccess;
32 
33 public:
34   /// ABI support base class. Used to write resolver, stub, and trampoline
35   /// blocks.
36   class ABISupport {
37   protected:
ABISupport(unsigned PointerSize,unsigned TrampolineSize,unsigned StubSize,unsigned StubToPointerMaxDisplacement,unsigned ResolverCodeSize)38     ABISupport(unsigned PointerSize, unsigned TrampolineSize, unsigned StubSize,
39                unsigned StubToPointerMaxDisplacement, unsigned ResolverCodeSize)
40         : PointerSize(PointerSize), TrampolineSize(TrampolineSize),
41           StubSize(StubSize),
42           StubToPointerMaxDisplacement(StubToPointerMaxDisplacement),
43           ResolverCodeSize(ResolverCodeSize) {}
44 
45   public:
46     virtual ~ABISupport();
47 
getPointerSize()48     unsigned getPointerSize() const { return PointerSize; }
getTrampolineSize()49     unsigned getTrampolineSize() const { return TrampolineSize; }
getStubSize()50     unsigned getStubSize() const { return StubSize; }
getStubToPointerMaxDisplacement()51     unsigned getStubToPointerMaxDisplacement() const {
52       return StubToPointerMaxDisplacement;
53     }
getResolverCodeSize()54     unsigned getResolverCodeSize() const { return ResolverCodeSize; }
55 
56     virtual void writeResolverCode(char *ResolverWorkingMem,
57                                    ExecutorAddr ResolverTargetAddr,
58                                    ExecutorAddr ReentryFnAddr,
59                                    ExecutorAddr ReentryCtxAddr) const = 0;
60 
61     virtual void writeTrampolines(char *TrampolineBlockWorkingMem,
62                                   ExecutorAddr TrampolineBlockTragetAddr,
63                                   ExecutorAddr ResolverAddr,
64                                   unsigned NumTrampolines) const = 0;
65 
66     virtual void writeIndirectStubsBlock(
67         char *StubsBlockWorkingMem, ExecutorAddr StubsBlockTargetAddress,
68         ExecutorAddr PointersBlockTargetAddress, unsigned NumStubs) const = 0;
69 
70   private:
71     unsigned PointerSize = 0;
72     unsigned TrampolineSize = 0;
73     unsigned StubSize = 0;
74     unsigned StubToPointerMaxDisplacement = 0;
75     unsigned ResolverCodeSize = 0;
76   };
77 
78   /// Create using the given ABI class.
79   template <typename ORCABI>
80   static std::unique_ptr<EPCIndirectionUtils>
81   CreateWithABI(ExecutorProcessControl &EPC);
82 
83   /// Create based on the ExecutorProcessControl triple.
84   static Expected<std::unique_ptr<EPCIndirectionUtils>>
85   Create(ExecutorProcessControl &EPC);
86 
87   /// Create based on the ExecutorProcessControl triple.
88   static Expected<std::unique_ptr<EPCIndirectionUtils>>
Create(ExecutionSession & ES)89   Create(ExecutionSession &ES) {
90     return Create(ES.getExecutorProcessControl());
91   }
92 
93   /// Return a reference to the ExecutorProcessControl object.
getExecutorProcessControl()94   ExecutorProcessControl &getExecutorProcessControl() const { return EPC; }
95 
96   /// Return a reference to the ABISupport object for this instance.
getABISupport()97   ABISupport &getABISupport() const { return *ABI; }
98 
99   /// Release memory for resources held by this instance. This *must* be called
100   /// prior to destruction of the class.
101   Error cleanup();
102 
103   /// Write resolver code to the executor process and return its address.
104   /// This must be called before any call to createTrampolinePool or
105   /// createLazyCallThroughManager.
106   Expected<ExecutorAddr> writeResolverBlock(ExecutorAddr ReentryFnAddr,
107                                             ExecutorAddr ReentryCtxAddr);
108 
109   /// Returns the address of the Resolver block. Returns zero if the
110   /// writeResolverBlock method has not previously been called.
getResolverBlockAddress()111   ExecutorAddr getResolverBlockAddress() const { return ResolverBlockAddr; }
112 
113   /// Create an IndirectStubsManager for the executor process.
114   std::unique_ptr<IndirectStubsManager> createIndirectStubsManager();
115 
116   /// Create a TrampolinePool for the executor process.
117   TrampolinePool &getTrampolinePool();
118 
119   /// Create a LazyCallThroughManager.
120   /// This function should only be called once.
121   LazyCallThroughManager &
122   createLazyCallThroughManager(ExecutionSession &ES,
123                                ExecutorAddr ErrorHandlerAddr);
124 
125   /// Create a LazyCallThroughManager for the executor process.
getLazyCallThroughManager()126   LazyCallThroughManager &getLazyCallThroughManager() {
127     assert(LCTM && "createLazyCallThroughManager must be called first");
128     return *LCTM;
129   }
130 
131 private:
132   using FinalizedAlloc = jitlink::JITLinkMemoryManager::FinalizedAlloc;
133 
134   struct IndirectStubInfo {
135     IndirectStubInfo() = default;
IndirectStubInfoIndirectStubInfo136     IndirectStubInfo(ExecutorAddr StubAddress, ExecutorAddr PointerAddress)
137         : StubAddress(StubAddress), PointerAddress(PointerAddress) {}
138     ExecutorAddr StubAddress;
139     ExecutorAddr PointerAddress;
140   };
141 
142   using IndirectStubInfoVector = std::vector<IndirectStubInfo>;
143 
144   /// Create an EPCIndirectionUtils instance.
145   EPCIndirectionUtils(ExecutorProcessControl &EPC,
146                       std::unique_ptr<ABISupport> ABI);
147 
148   Expected<IndirectStubInfoVector> getIndirectStubs(unsigned NumStubs);
149 
150   std::mutex EPCUIMutex;
151   ExecutorProcessControl &EPC;
152   std::unique_ptr<ABISupport> ABI;
153   ExecutorAddr ResolverBlockAddr;
154   FinalizedAlloc ResolverBlock;
155   std::unique_ptr<TrampolinePool> TP;
156   std::unique_ptr<LazyCallThroughManager> LCTM;
157 
158   std::vector<IndirectStubInfo> AvailableIndirectStubs;
159   std::vector<FinalizedAlloc> IndirectStubAllocs;
160 };
161 
162 /// This will call writeResolver on the given EPCIndirectionUtils instance
163 /// to set up re-entry via a function that will directly return the trampoline
164 /// landing address.
165 ///
166 /// The EPCIndirectionUtils' LazyCallThroughManager must have been previously
167 /// created via EPCIndirectionUtils::createLazyCallThroughManager.
168 ///
169 /// The EPCIndirectionUtils' writeResolver method must not have been previously
170 /// called.
171 ///
172 /// This function is experimental and likely subject to revision.
173 Error setUpInProcessLCTMReentryViaEPCIU(EPCIndirectionUtils &EPCIU);
174 
175 namespace detail {
176 
177 template <typename ORCABI>
178 class ABISupportImpl : public EPCIndirectionUtils::ABISupport {
179 public:
ABISupportImpl()180   ABISupportImpl()
181       : ABISupport(ORCABI::PointerSize, ORCABI::TrampolineSize,
182                    ORCABI::StubSize, ORCABI::StubToPointerMaxDisplacement,
183                    ORCABI::ResolverCodeSize) {}
184 
writeResolverCode(char * ResolverWorkingMem,ExecutorAddr ResolverTargetAddr,ExecutorAddr ReentryFnAddr,ExecutorAddr ReentryCtxAddr)185   void writeResolverCode(char *ResolverWorkingMem,
186                          ExecutorAddr ResolverTargetAddr,
187                          ExecutorAddr ReentryFnAddr,
188                          ExecutorAddr ReentryCtxAddr) const override {
189     ORCABI::writeResolverCode(ResolverWorkingMem, ResolverTargetAddr,
190                               ReentryFnAddr, ReentryCtxAddr);
191   }
192 
writeTrampolines(char * TrampolineBlockWorkingMem,ExecutorAddr TrampolineBlockTargetAddr,ExecutorAddr ResolverAddr,unsigned NumTrampolines)193   void writeTrampolines(char *TrampolineBlockWorkingMem,
194                         ExecutorAddr TrampolineBlockTargetAddr,
195                         ExecutorAddr ResolverAddr,
196                         unsigned NumTrampolines) const override {
197     ORCABI::writeTrampolines(TrampolineBlockWorkingMem,
198                              TrampolineBlockTargetAddr, ResolverAddr,
199                              NumTrampolines);
200   }
201 
writeIndirectStubsBlock(char * StubsBlockWorkingMem,ExecutorAddr StubsBlockTargetAddress,ExecutorAddr PointersBlockTargetAddress,unsigned NumStubs)202   void writeIndirectStubsBlock(char *StubsBlockWorkingMem,
203                                ExecutorAddr StubsBlockTargetAddress,
204                                ExecutorAddr PointersBlockTargetAddress,
205                                unsigned NumStubs) const override {
206     ORCABI::writeIndirectStubsBlock(StubsBlockWorkingMem,
207                                     StubsBlockTargetAddress,
208                                     PointersBlockTargetAddress, NumStubs);
209   }
210 };
211 
212 } // end namespace detail
213 
214 template <typename ORCABI>
215 std::unique_ptr<EPCIndirectionUtils>
CreateWithABI(ExecutorProcessControl & EPC)216 EPCIndirectionUtils::CreateWithABI(ExecutorProcessControl &EPC) {
217   return std::unique_ptr<EPCIndirectionUtils>(new EPCIndirectionUtils(
218       EPC, std::make_unique<detail::ABISupportImpl<ORCABI>>()));
219 }
220 
221 } // end namespace orc
222 } // end namespace llvm
223 
224 #endif // LLVM_EXECUTIONENGINE_ORC_EPCINDIRECTIONUTILS_H
225