30 return EPCIU.getIndirectStubs(NumStubs);
41 EPCTrampolinePool(EPCIndirectionUtils &EPCIU);
42 Error deallocatePool();
45 Error grow()
override;
47 using FinalizedAlloc = jitlink::JITLinkMemoryManager::FinalizedAlloc;
49 EPCIndirectionUtils &EPCIU;
50 unsigned TrampolineSize = 0;
51 unsigned TrampolinesPerPage = 0;
52 std::vector<FinalizedAlloc> TrampolineBlocks;
58 EPCIndirectStubsManager(EPCIndirectionUtils &EPCIU) : EPCIU(EPCIU) {}
60 Error deallocateStubs();
62 Error createStub(StringRef StubName, ExecutorAddr StubAddr,
63 JITSymbolFlags StubFlags)
override;
65 Error createStubs(
const StubInitsMap &StubInits)
override;
67 ExecutorSymbolDef findStub(StringRef Name,
bool ExportedStubsOnly)
override;
69 ExecutorSymbolDef findPointer(StringRef Name)
override;
71 Error updatePointer(StringRef Name, ExecutorAddr NewAddr)
override;
74 using StubInfo = std::pair<IndirectStubInfo, JITSymbolFlags>;
77 EPCIndirectionUtils &EPCIU;
78 StringMap<StubInfo> StubInfos;
86 TrampolineSize =
ABI.getTrampolineSize();
88 (EPC.getPageSize() -
ABI.getPointerSize()) / TrampolineSize;
91Error EPCTrampolinePool::deallocatePool() {
92 std::promise<MSVCPError> DeallocResultP;
93 auto DeallocResultF = DeallocResultP.get_future();
96 std::move(TrampolineBlocks),
97 [&](
Error Err) { DeallocResultP.set_value(std::move(Err)); });
99 return DeallocResultF.get();
102Error EPCTrampolinePool::grow() {
103 using namespace jitlink;
105 assert(AvailableTrampolines.empty() &&
106 "Grow called with trampolines still available");
109 assert(ResolverAddress &&
"Resolver address can not be null");
113 auto Alloc = SimpleSegmentAlloc::Create(
114 EPC.getMemMgr(), EPC.getSymbolStringPool(), EPC.getTargetTriple(),
115 nullptr, {{MemProt::Read | MemProt::Exec, {PageSize, Align(PageSize)}}});
117 return Alloc.takeError();
119 unsigned NumTrampolines = TrampolinesPerPage;
123 SegInfo.WorkingMem.data(), SegInfo.Addr, ResolverAddress, NumTrampolines);
124 for (
unsigned I = 0;
I < NumTrampolines; ++
I)
125 AvailableTrampolines.push_back(SegInfo.Addr + (
I * TrampolineSize));
127 auto FA =
Alloc->finalize();
129 return FA.takeError();
131 TrampolineBlocks.push_back(std::move(*FA));
136Error EPCIndirectStubsManager::createStub(StringRef StubName,
138 JITSymbolFlags StubFlags) {
140 SIM[StubName] = std::make_pair(StubAddr, StubFlags);
141 return createStubs(SIM);
144Error EPCIndirectStubsManager::createStubs(
const StubInitsMap &StubInits) {
145 auto AvailableStubInfos = getIndirectStubs(EPCIU, StubInits.size());
146 if (!AvailableStubInfos)
147 return AvailableStubInfos.takeError();
150 std::lock_guard<std::mutex> Lock(ISMMutex);
152 for (
auto &SI : StubInits) {
153 auto &
A = (*AvailableStubInfos)[ASIdx++];
154 StubInfos[
SI.first()] = std::make_pair(
A,
SI.second.second);
162 std::vector<tpctypes::UInt32Write> PtrUpdates;
163 for (
auto &SI : StubInits)
164 PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress,
165 static_cast<uint32_t
>(
SI.second.first.getValue())});
166 return MemAccess.writeUInt32s(PtrUpdates);
170 std::vector<tpctypes::UInt64Write> PtrUpdates;
171 for (
auto &SI : StubInits)
172 PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress,
173 SI.second.first.getValue()});
174 return MemAccess.writeUInt64s(PtrUpdates);
183 bool ExportedStubsOnly) {
184 std::lock_guard<std::mutex> Lock(ISMMutex);
185 auto I = StubInfos.find(Name);
186 if (
I == StubInfos.end())
188 return {
I->second.first.StubAddress,
I->second.second};
192 std::lock_guard<std::mutex> Lock(ISMMutex);
193 auto I = StubInfos.find(Name);
194 if (
I == StubInfos.end())
196 return {
I->second.first.PointerAddress,
I->second.second};
199Error EPCIndirectStubsManager::updatePointer(StringRef Name,
204 std::lock_guard<std::mutex> Lock(ISMMutex);
205 auto I = StubInfos.find(Name);
206 if (
I == StubInfos.end())
209 PtrAddr =
I->second.first.PointerAddress;
216 return MemAccess.writeUInt32s(PUpdate);
220 return MemAccess.writeUInt64s(PUpdate);
235Expected<std::unique_ptr<EPCIndirectionUtils>>
238 const auto &TT = EPC.getTargetTriple();
239 switch (TT.getArch()) {
242 std::string(
"No EPCIndirectionUtils available for ") + TT.str(),
277 auto &MemMgr = EPC.getMemMgr();
278 auto Err = MemMgr.deallocate(std::move(IndirectStubAllocs));
282 static_cast<EPCTrampolinePool &
>(*TP).deallocatePool());
286 joinErrors(std::move(Err), MemMgr.deallocate(std::move(ResolverBlock)));
296 assert(ABI &&
"ABI can not be null");
297 auto ResolverSize = ABI->getResolverCodeSize();
300 SimpleSegmentAlloc::Create(EPC.getMemMgr(), EPC.getSymbolStringPool(),
301 EPC.getTargetTriple(),
nullptr,
302 {{MemProt::Read | MemProt::Exec,
303 {ResolverSize, Align(EPC.getPageSize())}}});
306 return Alloc.takeError();
309 ResolverBlockAddr = SegInfo.Addr;
310 ABI->writeResolverCode(SegInfo.WorkingMem.data(), ResolverBlockAddr,
311 ReentryFnAddr, ReentryCtxAddr);
313 auto FA =
Alloc->finalize();
315 return FA.takeError();
317 ResolverBlock = std::move(*FA);
318 return ResolverBlockAddr;
321std::unique_ptr<IndirectStubsManager>
323 return std::make_unique<EPCIndirectStubsManager>(*
this);
328 TP = std::make_unique<EPCTrampolinePool>(*
this);
335 "createLazyCallThroughManager can not have been called before");
336 LCTM = std::make_unique<LazyCallThroughManager>(ES, ErrorHandlerAddr,
343 std::unique_ptr<ABISupport> ABI)
344 : EPC(EPC), MemAccess(MemAccess), ABI(
std::
move(ABI)) {
345 assert(this->ABI &&
"ABI can not be null");
348 "Stubs larger than one page are not supported");
352EPCIndirectionUtils::getIndirectStubs(
unsigned NumStubs) {
355 std::lock_guard<std::mutex> Lock(EPCUIMutex);
358 if (NumStubs > AvailableIndirectStubs.size()) {
359 auto NumStubsToAllocate = NumStubs;
361 auto StubBytes =
alignTo(NumStubsToAllocate * ABI->getStubSize(),
PageSize);
362 NumStubsToAllocate = StubBytes / ABI->getStubSize();
369 auto Alloc = SimpleSegmentAlloc::Create(
372 {{StubProt, {static_cast<size_t>(StubBytes), Align(PageSize)}},
373 {PtrProt, {static_cast<size_t>(PtrBytes), Align(PageSize)}}});
376 return Alloc.takeError();
378 auto StubSeg =
Alloc->getSegInfo(StubProt);
379 auto PtrSeg =
Alloc->getSegInfo(PtrProt);
381 ABI->writeIndirectStubsBlock(StubSeg.WorkingMem.data(), StubSeg.Addr,
382 PtrSeg.Addr, NumStubsToAllocate);
384 auto FA =
Alloc->finalize();
386 return FA.takeError();
388 IndirectStubAllocs.push_back(std::move(*FA));
390 auto StubExecutorAddr = StubSeg.Addr;
391 auto PtrExecutorAddr = PtrSeg.Addr;
392 for (
unsigned I = 0;
I != NumStubsToAllocate; ++
I) {
393 AvailableIndirectStubs.push_back(
394 IndirectStubInfo(StubExecutorAddr, PtrExecutorAddr));
395 StubExecutorAddr +=
ABI->getStubSize();
396 PtrExecutorAddr +=
ABI->getPointerSize();
400 assert(NumStubs <= AvailableIndirectStubs.size() &&
401 "Sufficient stubs should have been allocated above");
403 IndirectStubInfoVector
Result;
405 Result.push_back(AvailableIndirectStubs.back());
406 AvailableIndirectStubs.pop_back();
415 std::promise<ExecutorAddr> LandingAddrP;
416 auto LandingAddrF = LandingAddrP.get_future();
417 LCTM.resolveTrampolineLandingAddress(
419 [&](
ExecutorAddr Addr) { LandingAddrP.set_value(Addr); });
420 return LandingAddrF.get().getValue();
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static cl::opt< int > PageSize("imp-null-check-page-size", cl::desc("The page size of the target in bytes"), cl::init(4096), cl::Hidden)
Lightweight error class with error context and mandatory checking.
static ErrorSuccess success()
Create a success value.
Tagged union holding either a T or a Error.
virtual void deallocate(std::vector< FinalizedAlloc > Allocs, OnDeallocatedFunction OnDeallocated)=0
Deallocate a list of allocation objects.
EPCIndirectionUtils::IndirectStubInfo IndirectStubInfo
static Expected< IndirectStubInfoVector > getIndirectStubs(EPCIndirectionUtils &EPCIU, unsigned NumStubs)
EPCIndirectionUtils::IndirectStubInfoVector IndirectStubInfoVector
virtual void writeTrampolines(char *TrampolineBlockWorkingMem, ExecutorAddr TrampolineBlockTragetAddr, ExecutorAddr ResolverAddr, unsigned NumTrampolines) const =0
unsigned getPointerSize() const
Provides ExecutorProcessControl based indirect stubs, trampoline pool and lazy call through manager.
LLVM_ABI std::unique_ptr< IndirectStubsManager > createIndirectStubsManager()
Create an IndirectStubsManager for the executor process.
static LLVM_ABI Expected< std::unique_ptr< EPCIndirectionUtils > > Create(ExecutorProcessControl &EPC, MemoryAccess &MemAccess)
Create based on the ExecutorProcessControl triple.
LLVM_ABI Expected< ExecutorAddr > writeResolverBlock(ExecutorAddr ReentryFnAddr, ExecutorAddr ReentryCtxAddr)
Write resolver code to the executor process and return its address.
LazyCallThroughManager & getLazyCallThroughManager()
Create a LazyCallThroughManager for the executor process.
MemoryAccess & getMemoryAccess() const
Return a reference to the MemoryAccess object for this instance.
ExecutorProcessControl & getExecutorProcessControl() const
Return a reference to the ExecutorProcessControl object.
LLVM_ABI LazyCallThroughManager & createLazyCallThroughManager(ExecutionSession &ES, ExecutorAddr ErrorHandlerAddr)
Create a LazyCallThroughManager.
LLVM_ABI Error cleanup()
Release memory for resources held by this instance.
static std::unique_ptr< EPCIndirectionUtils > CreateWithABI(ExecutorProcessControl &EPC, MemoryAccess &MemAccess)
Create using the given ABI class.
LLVM_ABI TrampolinePool & getTrampolinePool()
Create a TrampolinePool for the executor process.
ABISupport & getABISupport() const
Return a reference to the ABISupport object for this instance.
ExecutorAddr getResolverBlockAddress() const
Returns the address of the Resolver block.
An ExecutionSession represents a running JIT program.
Represents an address in the executor process.
uint64_t getValue() const
static ExecutorAddr fromPtr(T *Ptr, UnwrapFn &&Unwrap=UnwrapFn())
Create an ExecutorAddr from the given pointer.
ExecutorProcessControl supports interaction with a JIT target process.
jitlink::JITLinkMemoryManager & getMemMgr() const
Return a JITLinkMemoryManager for the target process.
const Triple & getTargetTriple() const
Return the Triple for the target process.
std::shared_ptr< SymbolStringPool > getSymbolStringPool() const
Return a shared pointer to the SymbolStringPool for this instance.
unsigned getPageSize() const
Get the page size for the target process.
Represents a defining location for a JIT symbol.
Base class for managing collections of named indirect stubs.
Manages a set of 'lazy call-through' trampolines.
APIs for manipulating memory in the target process.
Base class for pools of compiler re-entry trampolines.
UIntWrite< uint64_t > UInt64Write
Describes a write to a uint64_t.
UIntWrite< uint32_t > UInt32Write
Describes a write to a uint32_t.
LLVM_ABI Error setUpInProcessLCTMReentryViaEPCIU(EPCIndirectionUtils &EPCIU)
This will call writeResolver on the given EPCIndirectionUtils instance to set up re-entry via a funct...
static JITTargetAddress reentry(JITTargetAddress LCTMAddr, JITTargetAddress TrampolineAddr)
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
T jitTargetAddressToPointer(JITTargetAddress Addr)
Convert a JITTargetAddress to a pointer.
Error joinErrors(Error E1, Error E2)
Concatenate errors.
constexpr uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
uint64_t JITTargetAddress
Represents an address in the target process's address space.
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Implement std::hash so that hash_code can be used in STL containers.