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
23namespace llvm {
24namespace orc {
25
26class ExecutorProcessControl;
27
28/// Provides ExecutorProcessControl based indirect stubs, trampoline pool and
29/// lazy call through manager.
30class EPCIndirectionUtils {
31  friend class EPCIndirectionUtilsAccess;
32
33public:
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(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
131private:
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.
173Error setUpInProcessLCTMReentryViaEPCIU(EPCIndirectionUtils &EPCIU);
174
175namespace detail {
176
177template <typename ORCABI>
178class ABISupportImpl : public EPCIndirectionUtils::ABISupport {
179public:
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
214template <typename ORCABI>
215std::unique_ptr<EPCIndirectionUtils>
216EPCIndirectionUtils::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