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