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