1 //===------- EPCIndirectionUtils.cpp -- EPC based indirection APIs --------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h"
11 #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
12 #include "llvm/Support/MathExtras.h"
17 using namespace llvm::orc
;
22 class EPCIndirectionUtilsAccess
{
24 using IndirectStubInfo
= EPCIndirectionUtils::IndirectStubInfo
;
25 using IndirectStubInfoVector
= EPCIndirectionUtils::IndirectStubInfoVector
;
27 static Expected
<IndirectStubInfoVector
>
28 getIndirectStubs(EPCIndirectionUtils
&EPCIU
, unsigned NumStubs
) {
29 return EPCIU
.getIndirectStubs(NumStubs
);
33 } // end namespace orc
34 } // end namespace llvm
38 class EPCTrampolinePool
: public TrampolinePool
{
40 EPCTrampolinePool(EPCIndirectionUtils
&EPCIU
);
41 Error
deallocatePool();
44 Error
grow() override
;
46 using Allocation
= jitlink::JITLinkMemoryManager::Allocation
;
48 EPCIndirectionUtils
&EPCIU
;
49 unsigned TrampolineSize
= 0;
50 unsigned TrampolinesPerPage
= 0;
51 std::vector
<std::unique_ptr
<Allocation
>> TrampolineBlocks
;
54 class EPCIndirectStubsManager
: public IndirectStubsManager
,
55 private EPCIndirectionUtilsAccess
{
57 EPCIndirectStubsManager(EPCIndirectionUtils
&EPCIU
) : EPCIU(EPCIU
) {}
59 Error
deallocateStubs();
61 Error
createStub(StringRef StubName
, JITTargetAddress StubAddr
,
62 JITSymbolFlags StubFlags
) override
;
64 Error
createStubs(const StubInitsMap
&StubInits
) override
;
66 JITEvaluatedSymbol
findStub(StringRef Name
, bool ExportedStubsOnly
) override
;
68 JITEvaluatedSymbol
findPointer(StringRef Name
) override
;
70 Error
updatePointer(StringRef Name
, JITTargetAddress NewAddr
) override
;
73 using StubInfo
= std::pair
<IndirectStubInfo
, JITSymbolFlags
>;
76 EPCIndirectionUtils
&EPCIU
;
77 StringMap
<StubInfo
> StubInfos
;
80 EPCTrampolinePool::EPCTrampolinePool(EPCIndirectionUtils
&EPCIU
)
82 auto &EPC
= EPCIU
.getExecutorProcessControl();
83 auto &ABI
= EPCIU
.getABISupport();
85 TrampolineSize
= ABI
.getTrampolineSize();
87 (EPC
.getPageSize() - ABI
.getPointerSize()) / TrampolineSize
;
90 Error
EPCTrampolinePool::deallocatePool() {
91 Error Err
= Error::success();
92 for (auto &Alloc
: TrampolineBlocks
)
93 Err
= joinErrors(std::move(Err
), Alloc
->deallocate());
97 Error
EPCTrampolinePool::grow() {
98 assert(AvailableTrampolines
.empty() &&
99 "Grow called with trampolines still available");
101 auto ResolverAddress
= EPCIU
.getResolverBlockAddress();
102 assert(ResolverAddress
&& "Resolver address can not be null");
104 auto &EPC
= EPCIU
.getExecutorProcessControl();
105 constexpr auto TrampolinePagePermissions
=
106 static_cast<sys::Memory::ProtectionFlags
>(sys::Memory::MF_READ
|
107 sys::Memory::MF_EXEC
);
108 auto PageSize
= EPC
.getPageSize();
109 jitlink::JITLinkMemoryManager::SegmentsRequestMap Request
;
110 Request
[TrampolinePagePermissions
] = {PageSize
, static_cast<size_t>(PageSize
),
112 auto Alloc
= EPC
.getMemMgr().allocate(nullptr, Request
);
115 return Alloc
.takeError();
117 unsigned NumTrampolines
= TrampolinesPerPage
;
119 auto WorkingMemory
= (*Alloc
)->getWorkingMemory(TrampolinePagePermissions
);
120 auto TargetAddress
= (*Alloc
)->getTargetMemory(TrampolinePagePermissions
);
122 EPCIU
.getABISupport().writeTrampolines(WorkingMemory
.data(), TargetAddress
,
123 ResolverAddress
, NumTrampolines
);
125 auto TargetAddr
= (*Alloc
)->getTargetMemory(TrampolinePagePermissions
);
126 for (unsigned I
= 0; I
< NumTrampolines
; ++I
)
127 AvailableTrampolines
.push_back(TargetAddr
+ (I
* TrampolineSize
));
129 if (auto Err
= (*Alloc
)->finalize())
132 TrampolineBlocks
.push_back(std::move(*Alloc
));
134 return Error::success();
137 Error
EPCIndirectStubsManager::createStub(StringRef StubName
,
138 JITTargetAddress StubAddr
,
139 JITSymbolFlags StubFlags
) {
141 SIM
[StubName
] = std::make_pair(StubAddr
, StubFlags
);
142 return createStubs(SIM
);
145 Error
EPCIndirectStubsManager::createStubs(const StubInitsMap
&StubInits
) {
146 auto AvailableStubInfos
= getIndirectStubs(EPCIU
, StubInits
.size());
147 if (!AvailableStubInfos
)
148 return AvailableStubInfos
.takeError();
151 std::lock_guard
<std::mutex
> Lock(ISMMutex
);
153 for (auto &SI
: StubInits
) {
154 auto &A
= (*AvailableStubInfos
)[ASIdx
++];
155 StubInfos
[SI
.first()] = std::make_pair(A
, SI
.second
.second
);
159 auto &MemAccess
= EPCIU
.getExecutorProcessControl().getMemoryAccess();
160 switch (EPCIU
.getABISupport().getPointerSize()) {
163 std::vector
<tpctypes::UInt32Write
> PtrUpdates
;
164 for (auto &SI
: StubInits
)
165 PtrUpdates
.push_back({(*AvailableStubInfos
)[ASIdx
++].PointerAddress
,
166 static_cast<uint32_t>(SI
.second
.first
)});
167 return MemAccess
.writeUInt32s(PtrUpdates
);
171 std::vector
<tpctypes::UInt64Write
> PtrUpdates
;
172 for (auto &SI
: StubInits
)
173 PtrUpdates
.push_back({(*AvailableStubInfos
)[ASIdx
++].PointerAddress
,
174 static_cast<uint64_t>(SI
.second
.first
)});
175 return MemAccess
.writeUInt64s(PtrUpdates
);
178 return make_error
<StringError
>("Unsupported pointer size",
179 inconvertibleErrorCode());
183 JITEvaluatedSymbol
EPCIndirectStubsManager::findStub(StringRef Name
,
184 bool ExportedStubsOnly
) {
185 std::lock_guard
<std::mutex
> Lock(ISMMutex
);
186 auto I
= StubInfos
.find(Name
);
187 if (I
== StubInfos
.end())
189 return {I
->second
.first
.StubAddress
, I
->second
.second
};
192 JITEvaluatedSymbol
EPCIndirectStubsManager::findPointer(StringRef Name
) {
193 std::lock_guard
<std::mutex
> Lock(ISMMutex
);
194 auto I
= StubInfos
.find(Name
);
195 if (I
== StubInfos
.end())
197 return {I
->second
.first
.PointerAddress
, I
->second
.second
};
200 Error
EPCIndirectStubsManager::updatePointer(StringRef Name
,
201 JITTargetAddress NewAddr
) {
203 JITTargetAddress PtrAddr
= 0;
205 std::lock_guard
<std::mutex
> Lock(ISMMutex
);
206 auto I
= StubInfos
.find(Name
);
207 if (I
== StubInfos
.end())
208 return make_error
<StringError
>("Unknown stub name",
209 inconvertibleErrorCode());
210 PtrAddr
= I
->second
.first
.PointerAddress
;
213 auto &MemAccess
= EPCIU
.getExecutorProcessControl().getMemoryAccess();
214 switch (EPCIU
.getABISupport().getPointerSize()) {
216 tpctypes::UInt32Write
PUpdate(PtrAddr
, NewAddr
);
217 return MemAccess
.writeUInt32s(PUpdate
);
220 tpctypes::UInt64Write
PUpdate(PtrAddr
, NewAddr
);
221 return MemAccess
.writeUInt64s(PUpdate
);
224 return make_error
<StringError
>("Unsupported pointer size",
225 inconvertibleErrorCode());
229 } // end anonymous namespace.
234 EPCIndirectionUtils::ABISupport::~ABISupport() {}
236 Expected
<std::unique_ptr
<EPCIndirectionUtils
>>
237 EPCIndirectionUtils::Create(ExecutorProcessControl
&EPC
) {
238 const auto &TT
= EPC
.getTargetTriple();
239 switch (TT
.getArch()) {
241 return make_error
<StringError
>(
242 std::string("No EPCIndirectionUtils available for ") + TT
.str(),
243 inconvertibleErrorCode());
244 case Triple::aarch64
:
245 case Triple::aarch64_32
:
246 return CreateWithABI
<OrcAArch64
>(EPC
);
249 return CreateWithABI
<OrcI386
>(EPC
);
252 return CreateWithABI
<OrcMips32Be
>(EPC
);
255 return CreateWithABI
<OrcMips32Le
>(EPC
);
258 case Triple::mips64el
:
259 return CreateWithABI
<OrcMips64
>(EPC
);
262 if (TT
.getOS() == Triple::OSType::Win32
)
263 return CreateWithABI
<OrcX86_64_Win32
>(EPC
);
265 return CreateWithABI
<OrcX86_64_SysV
>(EPC
);
269 Error
EPCIndirectionUtils::cleanup() {
270 Error Err
= Error::success();
272 for (auto &A
: IndirectStubAllocs
)
273 Err
= joinErrors(std::move(Err
), A
->deallocate());
276 Err
= joinErrors(std::move(Err
),
277 static_cast<EPCTrampolinePool
&>(*TP
).deallocatePool());
280 Err
= joinErrors(std::move(Err
), ResolverBlock
->deallocate());
285 Expected
<JITTargetAddress
>
286 EPCIndirectionUtils::writeResolverBlock(JITTargetAddress ReentryFnAddr
,
287 JITTargetAddress ReentryCtxAddr
) {
288 assert(ABI
&& "ABI can not be null");
289 constexpr auto ResolverBlockPermissions
=
290 static_cast<sys::Memory::ProtectionFlags
>(sys::Memory::MF_READ
|
291 sys::Memory::MF_EXEC
);
292 auto ResolverSize
= ABI
->getResolverCodeSize();
294 jitlink::JITLinkMemoryManager::SegmentsRequestMap Request
;
295 Request
[ResolverBlockPermissions
] = {EPC
.getPageSize(),
296 static_cast<size_t>(ResolverSize
), 0};
297 auto Alloc
= EPC
.getMemMgr().allocate(nullptr, Request
);
299 return Alloc
.takeError();
301 auto WorkingMemory
= (*Alloc
)->getWorkingMemory(ResolverBlockPermissions
);
302 ResolverBlockAddr
= (*Alloc
)->getTargetMemory(ResolverBlockPermissions
);
303 ABI
->writeResolverCode(WorkingMemory
.data(), ResolverBlockAddr
, ReentryFnAddr
,
306 if (auto Err
= (*Alloc
)->finalize())
307 return std::move(Err
);
309 ResolverBlock
= std::move(*Alloc
);
310 return ResolverBlockAddr
;
313 std::unique_ptr
<IndirectStubsManager
>
314 EPCIndirectionUtils::createIndirectStubsManager() {
315 return std::make_unique
<EPCIndirectStubsManager
>(*this);
318 TrampolinePool
&EPCIndirectionUtils::getTrampolinePool() {
320 TP
= std::make_unique
<EPCTrampolinePool
>(*this);
324 LazyCallThroughManager
&EPCIndirectionUtils::createLazyCallThroughManager(
325 ExecutionSession
&ES
, JITTargetAddress ErrorHandlerAddr
) {
327 "createLazyCallThroughManager can not have been called before");
328 LCTM
= std::make_unique
<LazyCallThroughManager
>(ES
, ErrorHandlerAddr
,
329 &getTrampolinePool());
333 EPCIndirectionUtils::EPCIndirectionUtils(ExecutorProcessControl
&EPC
,
334 std::unique_ptr
<ABISupport
> ABI
)
335 : EPC(EPC
), ABI(std::move(ABI
)) {
336 assert(this->ABI
&& "ABI can not be null");
338 assert(EPC
.getPageSize() > getABISupport().getStubSize() &&
339 "Stubs larger than one page are not supported");
342 Expected
<EPCIndirectionUtils::IndirectStubInfoVector
>
343 EPCIndirectionUtils::getIndirectStubs(unsigned NumStubs
) {
345 std::lock_guard
<std::mutex
> Lock(EPCUIMutex
);
347 // If there aren't enough stubs available then allocate some more.
348 if (NumStubs
> AvailableIndirectStubs
.size()) {
349 auto NumStubsToAllocate
= NumStubs
;
350 auto PageSize
= EPC
.getPageSize();
351 auto StubBytes
= alignTo(NumStubsToAllocate
* ABI
->getStubSize(), PageSize
);
352 NumStubsToAllocate
= StubBytes
/ ABI
->getStubSize();
354 alignTo(NumStubsToAllocate
* ABI
->getPointerSize(), PageSize
);
356 constexpr auto StubPagePermissions
=
357 static_cast<sys::Memory::ProtectionFlags
>(sys::Memory::MF_READ
|
358 sys::Memory::MF_EXEC
);
359 constexpr auto PointerPagePermissions
=
360 static_cast<sys::Memory::ProtectionFlags
>(sys::Memory::MF_READ
|
361 sys::Memory::MF_WRITE
);
363 jitlink::JITLinkMemoryManager::SegmentsRequestMap Request
;
364 Request
[StubPagePermissions
] = {PageSize
, static_cast<size_t>(StubBytes
),
366 Request
[PointerPagePermissions
] = {PageSize
, 0, PointerBytes
};
367 auto Alloc
= EPC
.getMemMgr().allocate(nullptr, Request
);
369 return Alloc
.takeError();
371 auto StubTargetAddr
= (*Alloc
)->getTargetMemory(StubPagePermissions
);
372 auto PointerTargetAddr
= (*Alloc
)->getTargetMemory(PointerPagePermissions
);
374 ABI
->writeIndirectStubsBlock(
375 (*Alloc
)->getWorkingMemory(StubPagePermissions
).data(), StubTargetAddr
,
376 PointerTargetAddr
, NumStubsToAllocate
);
378 if (auto Err
= (*Alloc
)->finalize())
379 return std::move(Err
);
381 for (unsigned I
= 0; I
!= NumStubsToAllocate
; ++I
) {
382 AvailableIndirectStubs
.push_back(
383 IndirectStubInfo(StubTargetAddr
, PointerTargetAddr
));
384 StubTargetAddr
+= ABI
->getStubSize();
385 PointerTargetAddr
+= ABI
->getPointerSize();
388 IndirectStubAllocs
.push_back(std::move(*Alloc
));
391 assert(NumStubs
<= AvailableIndirectStubs
.size() &&
392 "Sufficient stubs should have been allocated above");
394 IndirectStubInfoVector Result
;
396 Result
.push_back(AvailableIndirectStubs
.back());
397 AvailableIndirectStubs
.pop_back();
400 return std::move(Result
);
403 static JITTargetAddress
reentry(JITTargetAddress LCTMAddr
,
404 JITTargetAddress TrampolineAddr
) {
405 auto &LCTM
= *jitTargetAddressToPointer
<LazyCallThroughManager
*>(LCTMAddr
);
406 std::promise
<JITTargetAddress
> LandingAddrP
;
407 auto LandingAddrF
= LandingAddrP
.get_future();
408 LCTM
.resolveTrampolineLandingAddress(
410 [&](JITTargetAddress Addr
) { LandingAddrP
.set_value(Addr
); });
411 return LandingAddrF
.get();
414 Error
setUpInProcessLCTMReentryViaEPCIU(EPCIndirectionUtils
&EPCIU
) {
415 auto &LCTM
= EPCIU
.getLazyCallThroughManager();
417 .writeResolverBlock(pointerToJITTargetAddress(&reentry
),
418 pointerToJITTargetAddress(&LCTM
))
422 } // end namespace orc
423 } // end namespace llvm