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 FinalizedAlloc
= jitlink::JITLinkMemoryManager::FinalizedAlloc
;
48 EPCIndirectionUtils
&EPCIU
;
49 unsigned TrampolineSize
= 0;
50 unsigned TrampolinesPerPage
= 0;
51 std::vector
<FinalizedAlloc
> TrampolineBlocks
;
54 class EPCIndirectStubsManager
: public IndirectStubsManager
,
55 private EPCIndirectionUtilsAccess
{
57 EPCIndirectStubsManager(EPCIndirectionUtils
&EPCIU
) : EPCIU(EPCIU
) {}
59 Error
deallocateStubs();
61 Error
createStub(StringRef StubName
, ExecutorAddr StubAddr
,
62 JITSymbolFlags StubFlags
) override
;
64 Error
createStubs(const StubInitsMap
&StubInits
) override
;
66 ExecutorSymbolDef
findStub(StringRef Name
, bool ExportedStubsOnly
) override
;
68 ExecutorSymbolDef
findPointer(StringRef Name
) override
;
70 Error
updatePointer(StringRef Name
, ExecutorAddr 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 std::promise
<MSVCPError
> DeallocResultP
;
92 auto DeallocResultF
= DeallocResultP
.get_future();
94 EPCIU
.getExecutorProcessControl().getMemMgr().deallocate(
95 std::move(TrampolineBlocks
),
96 [&](Error Err
) { DeallocResultP
.set_value(std::move(Err
)); });
98 return DeallocResultF
.get();
101 Error
EPCTrampolinePool::grow() {
102 using namespace jitlink
;
104 assert(AvailableTrampolines
.empty() &&
105 "Grow called with trampolines still available");
107 auto ResolverAddress
= EPCIU
.getResolverBlockAddress();
108 assert(ResolverAddress
&& "Resolver address can not be null");
110 auto &EPC
= EPCIU
.getExecutorProcessControl();
111 auto PageSize
= EPC
.getPageSize();
112 auto Alloc
= SimpleSegmentAlloc::Create(
113 EPC
.getMemMgr(), nullptr,
114 {{MemProt::Read
| MemProt::Exec
, {PageSize
, Align(PageSize
)}}});
116 return Alloc
.takeError();
118 unsigned NumTrampolines
= TrampolinesPerPage
;
120 auto SegInfo
= Alloc
->getSegInfo(MemProt::Read
| MemProt::Exec
);
121 EPCIU
.getABISupport().writeTrampolines(
122 SegInfo
.WorkingMem
.data(), SegInfo
.Addr
, ResolverAddress
, NumTrampolines
);
123 for (unsigned I
= 0; I
< NumTrampolines
; ++I
)
124 AvailableTrampolines
.push_back(SegInfo
.Addr
+ (I
* TrampolineSize
));
126 auto FA
= Alloc
->finalize();
128 return FA
.takeError();
130 TrampolineBlocks
.push_back(std::move(*FA
));
132 return Error::success();
135 Error
EPCIndirectStubsManager::createStub(StringRef StubName
,
136 ExecutorAddr StubAddr
,
137 JITSymbolFlags StubFlags
) {
139 SIM
[StubName
] = std::make_pair(StubAddr
, StubFlags
);
140 return createStubs(SIM
);
143 Error
EPCIndirectStubsManager::createStubs(const StubInitsMap
&StubInits
) {
144 auto AvailableStubInfos
= getIndirectStubs(EPCIU
, StubInits
.size());
145 if (!AvailableStubInfos
)
146 return AvailableStubInfos
.takeError();
149 std::lock_guard
<std::mutex
> Lock(ISMMutex
);
151 for (auto &SI
: StubInits
) {
152 auto &A
= (*AvailableStubInfos
)[ASIdx
++];
153 StubInfos
[SI
.first()] = std::make_pair(A
, SI
.second
.second
);
157 auto &MemAccess
= EPCIU
.getExecutorProcessControl().getMemoryAccess();
158 switch (EPCIU
.getABISupport().getPointerSize()) {
161 std::vector
<tpctypes::UInt32Write
> PtrUpdates
;
162 for (auto &SI
: StubInits
)
163 PtrUpdates
.push_back({(*AvailableStubInfos
)[ASIdx
++].PointerAddress
,
164 static_cast<uint32_t>(SI
.second
.first
.getValue())});
165 return MemAccess
.writeUInt32s(PtrUpdates
);
169 std::vector
<tpctypes::UInt64Write
> PtrUpdates
;
170 for (auto &SI
: StubInits
)
171 PtrUpdates
.push_back({(*AvailableStubInfos
)[ASIdx
++].PointerAddress
,
172 static_cast<uint64_t>(SI
.second
.first
.getValue())});
173 return MemAccess
.writeUInt64s(PtrUpdates
);
176 return make_error
<StringError
>("Unsupported pointer size",
177 inconvertibleErrorCode());
181 ExecutorSymbolDef
EPCIndirectStubsManager::findStub(StringRef Name
,
182 bool ExportedStubsOnly
) {
183 std::lock_guard
<std::mutex
> Lock(ISMMutex
);
184 auto I
= StubInfos
.find(Name
);
185 if (I
== StubInfos
.end())
186 return ExecutorSymbolDef();
187 return {I
->second
.first
.StubAddress
, I
->second
.second
};
190 ExecutorSymbolDef
EPCIndirectStubsManager::findPointer(StringRef Name
) {
191 std::lock_guard
<std::mutex
> Lock(ISMMutex
);
192 auto I
= StubInfos
.find(Name
);
193 if (I
== StubInfos
.end())
194 return ExecutorSymbolDef();
195 return {I
->second
.first
.PointerAddress
, I
->second
.second
};
198 Error
EPCIndirectStubsManager::updatePointer(StringRef Name
,
199 ExecutorAddr NewAddr
) {
201 ExecutorAddr PtrAddr
;
203 std::lock_guard
<std::mutex
> Lock(ISMMutex
);
204 auto I
= StubInfos
.find(Name
);
205 if (I
== StubInfos
.end())
206 return make_error
<StringError
>("Unknown stub name",
207 inconvertibleErrorCode());
208 PtrAddr
= I
->second
.first
.PointerAddress
;
211 auto &MemAccess
= EPCIU
.getExecutorProcessControl().getMemoryAccess();
212 switch (EPCIU
.getABISupport().getPointerSize()) {
214 tpctypes::UInt32Write
PUpdate(PtrAddr
, NewAddr
.getValue());
215 return MemAccess
.writeUInt32s(PUpdate
);
218 tpctypes::UInt64Write
PUpdate(PtrAddr
, NewAddr
.getValue());
219 return MemAccess
.writeUInt64s(PUpdate
);
222 return make_error
<StringError
>("Unsupported pointer size",
223 inconvertibleErrorCode());
227 } // end anonymous namespace.
232 EPCIndirectionUtils::ABISupport::~ABISupport() = default;
234 Expected
<std::unique_ptr
<EPCIndirectionUtils
>>
235 EPCIndirectionUtils::Create(ExecutorProcessControl
&EPC
) {
236 const auto &TT
= EPC
.getTargetTriple();
237 switch (TT
.getArch()) {
239 return make_error
<StringError
>(
240 std::string("No EPCIndirectionUtils available for ") + TT
.str(),
241 inconvertibleErrorCode());
242 case Triple::aarch64
:
243 case Triple::aarch64_32
:
244 return CreateWithABI
<OrcAArch64
>(EPC
);
247 return CreateWithABI
<OrcI386
>(EPC
);
249 case Triple::loongarch64
:
250 return CreateWithABI
<OrcLoongArch64
>(EPC
);
253 return CreateWithABI
<OrcMips32Be
>(EPC
);
256 return CreateWithABI
<OrcMips32Le
>(EPC
);
259 case Triple::mips64el
:
260 return CreateWithABI
<OrcMips64
>(EPC
);
262 case Triple::riscv64
:
263 return CreateWithABI
<OrcRiscv64
>(EPC
);
266 if (TT
.getOS() == Triple::OSType::Win32
)
267 return CreateWithABI
<OrcX86_64_Win32
>(EPC
);
269 return CreateWithABI
<OrcX86_64_SysV
>(EPC
);
273 Error
EPCIndirectionUtils::cleanup() {
275 auto &MemMgr
= EPC
.getMemMgr();
276 auto Err
= MemMgr
.deallocate(std::move(IndirectStubAllocs
));
279 Err
= joinErrors(std::move(Err
),
280 static_cast<EPCTrampolinePool
&>(*TP
).deallocatePool());
284 joinErrors(std::move(Err
), MemMgr
.deallocate(std::move(ResolverBlock
)));
289 Expected
<ExecutorAddr
>
290 EPCIndirectionUtils::writeResolverBlock(ExecutorAddr ReentryFnAddr
,
291 ExecutorAddr ReentryCtxAddr
) {
292 using namespace jitlink
;
294 assert(ABI
&& "ABI can not be null");
295 auto ResolverSize
= ABI
->getResolverCodeSize();
298 SimpleSegmentAlloc::Create(EPC
.getMemMgr(), nullptr,
299 {{MemProt::Read
| MemProt::Exec
,
300 {ResolverSize
, Align(EPC
.getPageSize())}}});
303 return Alloc
.takeError();
305 auto SegInfo
= Alloc
->getSegInfo(MemProt::Read
| MemProt::Exec
);
306 ResolverBlockAddr
= SegInfo
.Addr
;
307 ABI
->writeResolverCode(SegInfo
.WorkingMem
.data(), ResolverBlockAddr
,
308 ReentryFnAddr
, ReentryCtxAddr
);
310 auto FA
= Alloc
->finalize();
312 return FA
.takeError();
314 ResolverBlock
= std::move(*FA
);
315 return ResolverBlockAddr
;
318 std::unique_ptr
<IndirectStubsManager
>
319 EPCIndirectionUtils::createIndirectStubsManager() {
320 return std::make_unique
<EPCIndirectStubsManager
>(*this);
323 TrampolinePool
&EPCIndirectionUtils::getTrampolinePool() {
325 TP
= std::make_unique
<EPCTrampolinePool
>(*this);
329 LazyCallThroughManager
&EPCIndirectionUtils::createLazyCallThroughManager(
330 ExecutionSession
&ES
, ExecutorAddr ErrorHandlerAddr
) {
332 "createLazyCallThroughManager can not have been called before");
333 LCTM
= std::make_unique
<LazyCallThroughManager
>(ES
, ErrorHandlerAddr
,
334 &getTrampolinePool());
338 EPCIndirectionUtils::EPCIndirectionUtils(ExecutorProcessControl
&EPC
,
339 std::unique_ptr
<ABISupport
> ABI
)
340 : EPC(EPC
), ABI(std::move(ABI
)) {
341 assert(this->ABI
&& "ABI can not be null");
343 assert(EPC
.getPageSize() > getABISupport().getStubSize() &&
344 "Stubs larger than one page are not supported");
347 Expected
<EPCIndirectionUtils::IndirectStubInfoVector
>
348 EPCIndirectionUtils::getIndirectStubs(unsigned NumStubs
) {
349 using namespace jitlink
;
351 std::lock_guard
<std::mutex
> Lock(EPCUIMutex
);
353 // If there aren't enough stubs available then allocate some more.
354 if (NumStubs
> AvailableIndirectStubs
.size()) {
355 auto NumStubsToAllocate
= NumStubs
;
356 auto PageSize
= EPC
.getPageSize();
357 auto StubBytes
= alignTo(NumStubsToAllocate
* ABI
->getStubSize(), PageSize
);
358 NumStubsToAllocate
= StubBytes
/ ABI
->getStubSize();
360 alignTo(NumStubsToAllocate
* ABI
->getPointerSize(), PageSize
);
362 auto StubProt
= MemProt::Read
| MemProt::Exec
;
363 auto PtrProt
= MemProt::Read
| MemProt::Write
;
365 auto Alloc
= SimpleSegmentAlloc::Create(
366 EPC
.getMemMgr(), nullptr,
367 {{StubProt
, {static_cast<size_t>(StubBytes
), Align(PageSize
)}},
368 {PtrProt
, {static_cast<size_t>(PtrBytes
), Align(PageSize
)}}});
371 return Alloc
.takeError();
373 auto StubSeg
= Alloc
->getSegInfo(StubProt
);
374 auto PtrSeg
= Alloc
->getSegInfo(PtrProt
);
376 ABI
->writeIndirectStubsBlock(StubSeg
.WorkingMem
.data(), StubSeg
.Addr
,
377 PtrSeg
.Addr
, NumStubsToAllocate
);
379 auto FA
= Alloc
->finalize();
381 return FA
.takeError();
383 IndirectStubAllocs
.push_back(std::move(*FA
));
385 auto StubExecutorAddr
= StubSeg
.Addr
;
386 auto PtrExecutorAddr
= PtrSeg
.Addr
;
387 for (unsigned I
= 0; I
!= NumStubsToAllocate
; ++I
) {
388 AvailableIndirectStubs
.push_back(
389 IndirectStubInfo(StubExecutorAddr
, PtrExecutorAddr
));
390 StubExecutorAddr
+= ABI
->getStubSize();
391 PtrExecutorAddr
+= ABI
->getPointerSize();
395 assert(NumStubs
<= AvailableIndirectStubs
.size() &&
396 "Sufficient stubs should have been allocated above");
398 IndirectStubInfoVector Result
;
400 Result
.push_back(AvailableIndirectStubs
.back());
401 AvailableIndirectStubs
.pop_back();
404 return std::move(Result
);
407 static JITTargetAddress
reentry(JITTargetAddress LCTMAddr
,
408 JITTargetAddress TrampolineAddr
) {
409 auto &LCTM
= *jitTargetAddressToPointer
<LazyCallThroughManager
*>(LCTMAddr
);
410 std::promise
<ExecutorAddr
> LandingAddrP
;
411 auto LandingAddrF
= LandingAddrP
.get_future();
412 LCTM
.resolveTrampolineLandingAddress(
413 ExecutorAddr(TrampolineAddr
),
414 [&](ExecutorAddr Addr
) { LandingAddrP
.set_value(Addr
); });
415 return LandingAddrF
.get().getValue();
418 Error
setUpInProcessLCTMReentryViaEPCIU(EPCIndirectionUtils
&EPCIU
) {
419 auto &LCTM
= EPCIU
.getLazyCallThroughManager();
421 .writeResolverBlock(ExecutorAddr::fromPtr(&reentry
),
422 ExecutorAddr::fromPtr(&LCTM
))
426 } // end namespace orc
427 } // end namespace llvm