LLVM 20.0.0git
EPCGenericRTDyldMemoryManager.cpp
Go to the documentation of this file.
1//===----- EPCGenericRTDyldMemoryManager.cpp - EPC-bbasde MemMgr -----===//
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
13
14#define DEBUG_TYPE "orc"
15
16using namespace llvm::orc::shared;
17
18namespace llvm {
19namespace orc {
20
24 SymbolAddrs SAs;
25 if (auto Err = EPC.getBootstrapSymbols(
26 {{SAs.Instance, rt::SimpleExecutorMemoryManagerInstanceName},
27 {SAs.Reserve, rt::SimpleExecutorMemoryManagerReserveWrapperName},
28 {SAs.Finalize, rt::SimpleExecutorMemoryManagerFinalizeWrapperName},
29 {SAs.Deallocate,
30 rt::SimpleExecutorMemoryManagerDeallocateWrapperName},
31 {SAs.RegisterEHFrame, rt::RegisterEHFrameSectionWrapperName},
32 {SAs.DeregisterEHFrame, rt::DeregisterEHFrameSectionWrapperName}}))
33 return std::move(Err);
34 return std::make_unique<EPCGenericRTDyldMemoryManager>(EPC, std::move(SAs));
35}
36
37EPCGenericRTDyldMemoryManager::EPCGenericRTDyldMemoryManager(
39 : EPC(EPC), SAs(std::move(SAs)) {
40 LLVM_DEBUG(dbgs() << "Created remote allocator " << (void *)this << "\n");
41}
42
44 LLVM_DEBUG(dbgs() << "Destroyed remote allocator " << (void *)this << "\n");
45 if (!ErrMsg.empty())
46 errs() << "Destroying with existing errors:\n" << ErrMsg << "\n";
47
48 Error Err = Error::success();
49 if (auto Err2 = EPC.callSPSWrapper<
51 SAs.Reserve, Err, SAs.Instance, FinalizedAllocs)) {
52 // FIXME: Report errors through EPC once that functionality is available.
53 logAllUnhandledErrors(std::move(Err2), errs(), "");
54 return;
55 }
56
57 if (Err)
58 logAllUnhandledErrors(std::move(Err), errs(), "");
59}
60
62 uintptr_t Size, unsigned Alignment, unsigned SectionID,
64 std::lock_guard<std::mutex> Lock(M);
66 dbgs() << "Allocator " << (void *)this << " allocating code section "
67 << SectionName << ": size = " << formatv("{0:x}", Size)
68 << " bytes, alignment = " << Alignment << "\n";
69 });
70 auto &Seg = Unmapped.back().CodeAllocs;
71 Seg.emplace_back(Size, Alignment);
72 return reinterpret_cast<uint8_t *>(
73 alignAddr(Seg.back().Contents.get(), Align(Alignment)));
74}
75
77 uintptr_t Size, unsigned Alignment, unsigned SectionID,
78 StringRef SectionName, bool IsReadOnly) {
79 std::lock_guard<std::mutex> Lock(M);
81 dbgs() << "Allocator " << (void *)this << " allocating "
82 << (IsReadOnly ? "ro" : "rw") << "-data section " << SectionName
83 << ": size = " << formatv("{0:x}", Size) << " bytes, alignment "
84 << Alignment << ")\n";
85 });
86
87 auto &Seg =
88 IsReadOnly ? Unmapped.back().RODataAllocs : Unmapped.back().RWDataAllocs;
89
90 Seg.emplace_back(Size, Alignment);
91 return reinterpret_cast<uint8_t *>(
92 alignAddr(Seg.back().Contents.get(), Align(Alignment)));
93}
94
96 uintptr_t CodeSize, Align CodeAlign, uintptr_t RODataSize,
97 Align RODataAlign, uintptr_t RWDataSize, Align RWDataAlign) {
98
99 {
100 std::lock_guard<std::mutex> Lock(M);
101 // If there's already an error then bail out.
102 if (!ErrMsg.empty())
103 return;
104
105 if (CodeAlign > EPC.getPageSize()) {
106 ErrMsg = "Invalid code alignment in reserveAllocationSpace";
107 return;
108 }
109 if (RODataAlign > EPC.getPageSize()) {
110 ErrMsg = "Invalid ro-data alignment in reserveAllocationSpace";
111 return;
112 }
113 if (RWDataAlign > EPC.getPageSize()) {
114 ErrMsg = "Invalid rw-data alignment in reserveAllocationSpace";
115 return;
116 }
117 }
118
119 uint64_t TotalSize = 0;
120 TotalSize += alignTo(CodeSize, EPC.getPageSize());
121 TotalSize += alignTo(RODataSize, EPC.getPageSize());
122 TotalSize += alignTo(RWDataSize, EPC.getPageSize());
123
124 LLVM_DEBUG({
125 dbgs() << "Allocator " << (void *)this << " reserving "
126 << formatv("{0:x}", TotalSize) << " bytes.\n";
127 });
128
129 Expected<ExecutorAddr> TargetAllocAddr((ExecutorAddr()));
130 if (auto Err = EPC.callSPSWrapper<
132 SAs.Reserve, TargetAllocAddr, SAs.Instance, TotalSize)) {
133 std::lock_guard<std::mutex> Lock(M);
134 ErrMsg = toString(std::move(Err));
135 return;
136 }
137 if (!TargetAllocAddr) {
138 std::lock_guard<std::mutex> Lock(M);
139 ErrMsg = toString(TargetAllocAddr.takeError());
140 return;
141 }
142
143 std::lock_guard<std::mutex> Lock(M);
144 Unmapped.push_back(SectionAllocGroup());
145 Unmapped.back().RemoteCode = {
146 *TargetAllocAddr, ExecutorAddrDiff(alignTo(CodeSize, EPC.getPageSize()))};
147 Unmapped.back().RemoteROData = {
148 Unmapped.back().RemoteCode.End,
149 ExecutorAddrDiff(alignTo(RODataSize, EPC.getPageSize()))};
150 Unmapped.back().RemoteRWData = {
151 Unmapped.back().RemoteROData.End,
152 ExecutorAddrDiff(alignTo(RWDataSize, EPC.getPageSize()))};
153}
154
156 return true;
157}
158
160 uint64_t LoadAddr,
161 size_t Size) {
162 LLVM_DEBUG({
163 dbgs() << "Allocator " << (void *)this << " added unfinalized eh-frame "
164 << formatv("[ {0:x} {1:x} ]", LoadAddr, LoadAddr + Size) << "\n";
165 });
166 std::lock_guard<std::mutex> Lock(M);
167 // Bail out early if there's already an error.
168 if (!ErrMsg.empty())
169 return;
170
171 ExecutorAddr LA(LoadAddr);
172 for (auto &SecAllocGroup : llvm::reverse(Unfinalized)) {
173 if (SecAllocGroup.RemoteCode.contains(LA) ||
174 SecAllocGroup.RemoteROData.contains(LA) ||
175 SecAllocGroup.RemoteRWData.contains(LA)) {
176 SecAllocGroup.UnfinalizedEHFrames.push_back({LA, Size});
177 return;
178 }
179 }
180 ErrMsg = "eh-frame does not lie inside unfinalized alloc";
181}
182
184 // This is a no-op for us: We've registered a deallocation action for it.
185}
186
188 RuntimeDyld &Dyld, const object::ObjectFile &Obj) {
189 std::lock_guard<std::mutex> Lock(M);
190 LLVM_DEBUG(dbgs() << "Allocator " << (void *)this << " applied mappings:\n");
191 for (auto &ObjAllocs : Unmapped) {
192 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.CodeAllocs,
193 ObjAllocs.RemoteCode.Start);
194 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.RODataAllocs,
195 ObjAllocs.RemoteROData.Start);
196 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.RWDataAllocs,
197 ObjAllocs.RemoteRWData.Start);
198 Unfinalized.push_back(std::move(ObjAllocs));
199 }
200 Unmapped.clear();
201}
202
204 LLVM_DEBUG(dbgs() << "Allocator " << (void *)this << " finalizing:\n");
205
206 // If there's an error then bail out here.
207 std::vector<SectionAllocGroup> SecAllocGroups;
208 {
209 std::lock_guard<std::mutex> Lock(M);
210 if (ErrMsg && !this->ErrMsg.empty()) {
211 *ErrMsg = std::move(this->ErrMsg);
212 return true;
213 }
214 std::swap(SecAllocGroups, Unfinalized);
215 }
216
217 // Loop over unfinalized objects to make finalization requests.
218 for (auto &SecAllocGroup : SecAllocGroups) {
219
222
223 ExecutorAddrRange *RemoteAddrs[3] = {&SecAllocGroup.RemoteCode,
224 &SecAllocGroup.RemoteROData,
225 &SecAllocGroup.RemoteRWData};
226
227 std::vector<SectionAlloc> *SegSections[3] = {&SecAllocGroup.CodeAllocs,
228 &SecAllocGroup.RODataAllocs,
229 &SecAllocGroup.RWDataAllocs};
230
232 std::unique_ptr<char[]> AggregateContents[3];
233
234 for (unsigned I = 0; I != 3; ++I) {
235 FR.Segments.push_back({});
236 auto &Seg = FR.Segments.back();
237 Seg.RAG = SegMemProts[I];
238 Seg.Addr = RemoteAddrs[I]->Start;
239 for (auto &SecAlloc : *SegSections[I]) {
240 Seg.Size = alignTo(Seg.Size, SecAlloc.Align);
241 Seg.Size += SecAlloc.Size;
242 }
243 AggregateContents[I] = std::make_unique<char[]>(Seg.Size);
244 size_t SecOffset = 0;
245 for (auto &SecAlloc : *SegSections[I]) {
246 SecOffset = alignTo(SecOffset, SecAlloc.Align);
247 memcpy(&AggregateContents[I][SecOffset],
248 reinterpret_cast<const char *>(
249 alignAddr(SecAlloc.Contents.get(), Align(SecAlloc.Align))),
250 SecAlloc.Size);
251 SecOffset += SecAlloc.Size;
252 // FIXME: Can we reset SecAlloc.Content here, now that it's copied into
253 // the aggregated content?
254 }
255 Seg.Content = {AggregateContents[I].get(), SecOffset};
256 }
257
258 for (auto &Frame : SecAllocGroup.UnfinalizedEHFrames)
259 FR.Actions.push_back(
260 {cantFail(
262 SAs.RegisterEHFrame, Frame)),
263 cantFail(
265 SAs.DeregisterEHFrame, Frame))});
266
267 // We'll also need to make an extra allocation for the eh-frame wrapper call
268 // arguments.
269 Error FinalizeErr = Error::success();
270 if (auto Err = EPC.callSPSWrapper<
272 SAs.Finalize, FinalizeErr, SAs.Instance, std::move(FR))) {
273 std::lock_guard<std::mutex> Lock(M);
274 this->ErrMsg = toString(std::move(Err));
275 dbgs() << "Serialization error: " << this->ErrMsg << "\n";
276 if (ErrMsg)
277 *ErrMsg = this->ErrMsg;
278 return true;
279 }
280 if (FinalizeErr) {
281 std::lock_guard<std::mutex> Lock(M);
282 this->ErrMsg = toString(std::move(FinalizeErr));
283 dbgs() << "Finalization error: " << this->ErrMsg << "\n";
284 if (ErrMsg)
285 *ErrMsg = this->ErrMsg;
286 return true;
287 }
288 }
289
290 return false;
291}
292
293void EPCGenericRTDyldMemoryManager::mapAllocsToRemoteAddrs(
294 RuntimeDyld &Dyld, std::vector<SectionAlloc> &Allocs,
295 ExecutorAddr NextAddr) {
296 for (auto &Alloc : Allocs) {
297 NextAddr.setValue(alignTo(NextAddr.getValue(), Alloc.Align));
298 LLVM_DEBUG({
299 dbgs() << " " << static_cast<void *>(Alloc.Contents.get()) << " -> "
300 << format("0x%016" PRIx64, NextAddr.getValue()) << "\n";
301 });
302 Dyld.mapSectionAddress(reinterpret_cast<const void *>(alignAddr(
303 Alloc.Contents.get(), Align(Alloc.Align))),
304 NextAddr.getValue());
305 Alloc.RemoteAddr = NextAddr;
306 // Only advance NextAddr if it was non-null to begin with,
307 // otherwise leave it as null.
308 if (NextAddr)
309 NextAddr += ExecutorAddrDiff(Alloc.Size);
310 }
311}
312
313} // end namespace orc
314} // end namespace llvm
#define LLVM_DEBUG(...)
Definition: Debug.h:106
uint64_t Addr
uint64_t Size
#define I(x, y, z)
Definition: MD5.cpp:58
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:337
Tagged union holding either a T or a Error.
Definition: Error.h:481
void mapSectionAddress(const void *LocalAddress, uint64_t TargetAddress)
Map a section to its target address space value.
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
This class is the base class for all object file types.
Definition: ObjectFile.h:229
uint8_t * allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName, bool IsReadOnly) override
Allocate a memory block of (at least) the given size suitable for data.
bool finalizeMemory(std::string *ErrMsg=nullptr) override
This method is called when object loading is complete and section page permissions can be applied.
bool needsToReserveAllocationSpace() override
Override to return true to enable the reserveAllocationSpace callback.
static Expected< std::unique_ptr< EPCGenericRTDyldMemoryManager > > CreateWithDefaultBootstrapSymbols(ExecutorProcessControl &EPC)
Create an EPCGenericRTDyldMemoryManager using the given EPC, looking up the default symbol names in t...
void reserveAllocationSpace(uintptr_t CodeSize, Align CodeAlign, uintptr_t RODataSize, Align RODataAlign, uintptr_t RWDataSize, Align RWDataAlign) override
Inform the memory manager about the total amount of memory required to allocate all sections to be lo...
void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr, size_t Size) override
Register the EH frames with the runtime so that c++ exceptions work.
void notifyObjectLoaded(RuntimeDyld &Dyld, const object::ObjectFile &Obj) override
This method is called after an object has been loaded into memory but before relocations are applied ...
uint8_t * allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName) override
Allocate a memory block of (at least) the given size suitable for executable code.
Represents an address in the executor process.
uint64_t getValue() const
void setValue(uint64_t Addr)
ExecutorProcessControl supports interaction with a JIT target process.
unsigned getPageSize() const
Get the page size for the target process.
Error callSPSWrapper(ExecutorAddr WrapperFnAddr, WrapperCallArgTs &&...WrapperCallArgs)
Run a wrapper function using SPS to serialize the arguments and deserialize the results.
Error getBootstrapSymbols(ArrayRef< std::pair< ExecutorAddr &, StringRef > > Pairs) const
For each (ExecutorAddr&, StringRef) pair, looks up the string in the bootstrap symbols map and writes...
A utility class for serializing to a blob from a variadic list.
static Expected< WrapperFunctionCall > Create(ExecutorAddr FnAddr, const ArgTs &...Args)
Create a WrapperFunctionCall using the given SPS serializer to serialize the arguments.
shared::SPSExpected< shared::SPSExecutorAddr >(shared::SPSExecutorAddr, uint64_t) SPSSimpleExecutorMemoryManagerReserveSignature
Definition: OrcRTBridge.h:65
shared::SPSError(shared::SPSExecutorAddr, shared::SPSFinalizeRequest) SPSSimpleExecutorMemoryManagerFinalizeSignature
Definition: OrcRTBridge.h:67
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSSimpleExecutorMemoryManagerDeallocateSignature
Definition: OrcRTBridge.h:69
MemProt
Describes Read/Write/Exec permissions for memory.
Definition: MemoryFlags.h:27
uint64_t ExecutorAddrDiff
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner={})
Log all errors (if any) in E to OS.
Definition: Error.cpp:65
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
auto reverse(ContainerTy &&C)
Definition: STLExtras.h:420
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition: Format.h:125
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition: Error.h:756
uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition: Alignment.h:155
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1873
const char * toString(DWARFSectionKind Kind)
uintptr_t alignAddr(const void *Addr, Align Alignment)
Aligns Addr to Alignment bytes, rounding up.
Definition: Alignment.h:187
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition: BitVector.h:860
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39
Represents an address range in the exceutor process.
std::vector< SegFinalizeRequest > Segments