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 | 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>> |
89 | Create(ExecutionSession &ES) { |
90 | return Create(EPC&: ES.getExecutorProcessControl()); |
91 | } |
92 | |
93 | /// Return a reference to the ExecutorProcessControl object. |
94 | ExecutorProcessControl &getExecutorProcessControl() const { return EPC; } |
95 | |
96 | /// Return a reference to the ABISupport object for this instance. |
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. |
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. |
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; |
136 | 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: |
180 | ABISupportImpl() |
181 | : ABISupport(ORCABI::PointerSize, ORCABI::TrampolineSize, |
182 | ORCABI::StubSize, ORCABI::StubToPointerMaxDisplacement, |
183 | ORCABI::ResolverCodeSize) {} |
184 | |
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 | |
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 | |
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> |
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 | |