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
14
15#define DEBUG_TYPE "orc"
16
17using namespace llvm::orc::shared;
18
19namespace llvm {
20namespace orc {
21
25 SymbolAddrs SAs;
26 if (auto Err = EPC.getBootstrapSymbols(
27 {{SAs.Instance, rt::SimpleExecutorMemoryManagerInstanceName},
28 {SAs.Reserve, rt::SimpleExecutorMemoryManagerReserveWrapperName},
29 {SAs.Finalize, rt::SimpleExecutorMemoryManagerFinalizeWrapperName},
30 {SAs.Deallocate,
31 rt::SimpleExecutorMemoryManagerDeallocateWrapperName},
32 {SAs.RegisterEHFrame, rt::RegisterEHFrameSectionWrapperName},
33 {SAs.DeregisterEHFrame, rt::DeregisterEHFrameSectionWrapperName}}))
34 return std::move(Err);
35 return std::make_unique<EPCGenericRTDyldMemoryManager>(EPC, std::move(SAs));
36}
37
38EPCGenericRTDyldMemoryManager::EPCGenericRTDyldMemoryManager(
40 : EPC(EPC), SAs(std::move(SAs)) {
41 LLVM_DEBUG(dbgs() << "Created remote allocator " << (void *)this << "\n");
42}
43
45 LLVM_DEBUG(dbgs() << "Destroyed remote allocator " << (void *)this << "\n");
46 if (!ErrMsg.empty())
47 errs() << "Destroying with existing errors:\n" << ErrMsg << "\n";
48
49 Error Err = Error::success();
50 if (auto Err2 = EPC.callSPSWrapper<
52 SAs.Reserve, Err, SAs.Instance, FinalizedAllocs)) {
53 // FIXME: Report errors through EPC once that functionality is available.
54 logAllUnhandledErrors(std::move(Err2), errs(), "");
55 return;
56 }
57
58 if (Err)
59 logAllUnhandledErrors(std::move(Err), errs(), "");
60}
61
63 uintptr_t Size, unsigned Alignment, unsigned SectionID,
65 std::lock_guard<std::mutex> Lock(M);
67 dbgs() << "Allocator " << (void *)this << " allocating code section "
68 << SectionName << ": size = " << formatv("{0:x}", Size)
69 << " bytes, alignment = " << Alignment << "\n";
70 });
71 auto &Seg = Unmapped.back().CodeAllocs;
72 Seg.emplace_back(Size, Alignment);
73 return reinterpret_cast<uint8_t *>(
74 alignAddr(Seg.back().Contents.get(), Align(Alignment)));
75}
76
78 uintptr_t Size, unsigned Alignment, unsigned SectionID,
79 StringRef SectionName, bool IsReadOnly) {
80 std::lock_guard<std::mutex> Lock(M);
82 dbgs() << "Allocator " << (void *)this << " allocating "
83 << (IsReadOnly ? "ro" : "rw") << "-data section " << SectionName
84 << ": size = " << formatv("{0:x}", Size) << " bytes, alignment "
85 << Alignment << ")\n";
86 });
87
88 auto &Seg =
89 IsReadOnly ? Unmapped.back().RODataAllocs : Unmapped.back().RWDataAllocs;
90
91 Seg.emplace_back(Size, Alignment);
92 return reinterpret_cast<uint8_t *>(
93 alignAddr(Seg.back().Contents.get(), Align(Alignment)));
94}
95
97 uintptr_t CodeSize, Align CodeAlign, uintptr_t RODataSize,
98 Align RODataAlign, uintptr_t RWDataSize, Align RWDataAlign) {
99
100 {
101 std::lock_guard<std::mutex> Lock(M);
102 // If there's already an error then bail out.
103 if (!ErrMsg.empty())
104 return;
105
106 if (CodeAlign > EPC.getPageSize()) {
107 ErrMsg = "Invalid code alignment in reserveAllocationSpace";
108 return;
109 }
110 if (RODataAlign > EPC.getPageSize()) {
111 ErrMsg = "Invalid ro-data alignment in reserveAllocationSpace";
112 return;
113 }
114 if (RWDataAlign > EPC.getPageSize()) {
115 ErrMsg = "Invalid rw-data alignment in reserveAllocationSpace";
116 return;
117 }
118 }
119
120 uint64_t TotalSize = 0;
121 TotalSize += alignTo(CodeSize, EPC.getPageSize());
122 TotalSize += alignTo(RODataSize, EPC.getPageSize());
123 TotalSize += alignTo(RWDataSize, EPC.getPageSize());
124
125 LLVM_DEBUG({
126 dbgs() << "Allocator " << (void *)this << " reserving "
127 << formatv("{0:x}", TotalSize) << " bytes.\n";
128 });
129
130 Expected<ExecutorAddr> TargetAllocAddr((ExecutorAddr()));
131 if (auto Err = EPC.callSPSWrapper<
133 SAs.Reserve, TargetAllocAddr, SAs.Instance, TotalSize)) {
134 std::lock_guard<std::mutex> Lock(M);
135 ErrMsg = toString(std::move(Err));
136 return;
137 }
138 if (!TargetAllocAddr) {
139 std::lock_guard<std::mutex> Lock(M);
140 ErrMsg = toString(TargetAllocAddr.takeError());
141 return;
142 }
143
144 std::lock_guard<std::mutex> Lock(M);
145 Unmapped.push_back(SectionAllocGroup());
146 Unmapped.back().RemoteCode = {
147 *TargetAllocAddr, ExecutorAddrDiff(alignTo(CodeSize, EPC.getPageSize()))};
148 Unmapped.back().RemoteROData = {
149 Unmapped.back().RemoteCode.End,
150 ExecutorAddrDiff(alignTo(RODataSize, EPC.getPageSize()))};
151 Unmapped.back().RemoteRWData = {
152 Unmapped.back().RemoteROData.End,
153 ExecutorAddrDiff(alignTo(RWDataSize, EPC.getPageSize()))};
154}
155
157 return true;
158}
159
161 uint64_t LoadAddr,
162 size_t Size) {
163 LLVM_DEBUG({
164 dbgs() << "Allocator " << (void *)this << " added unfinalized eh-frame "
165 << formatv("[ {0:x} {1:x} ]", LoadAddr, LoadAddr + Size) << "\n";
166 });
167 std::lock_guard<std::mutex> Lock(M);
168 // Bail out early if there's already an error.
169 if (!ErrMsg.empty())
170 return;
171
172 ExecutorAddr LA(LoadAddr);
173 for (auto &SecAllocGroup : llvm::reverse(Unfinalized)) {
174 if (SecAllocGroup.RemoteCode.contains(LA) ||
175 SecAllocGroup.RemoteROData.contains(LA) ||
176 SecAllocGroup.RemoteRWData.contains(LA)) {
177 SecAllocGroup.UnfinalizedEHFrames.push_back({LA, Size});
178 return;
179 }
180 }
181 ErrMsg = "eh-frame does not lie inside unfinalized alloc";
182}
183
185 // This is a no-op for us: We've registered a deallocation action for it.
186}
187
189 RuntimeDyld &Dyld, const object::ObjectFile &Obj) {
190 std::lock_guard<std::mutex> Lock(M);
191 LLVM_DEBUG(dbgs() << "Allocator " << (void *)this << " applied mappings:\n");
192 for (auto &ObjAllocs : Unmapped) {
193 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.CodeAllocs,
194 ObjAllocs.RemoteCode.Start);
195 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.RODataAllocs,
196 ObjAllocs.RemoteROData.Start);
197 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.RWDataAllocs,
198 ObjAllocs.RemoteRWData.Start);
199 Unfinalized.push_back(std::move(ObjAllocs));
200 }
201 Unmapped.clear();
202}
203
205 LLVM_DEBUG(dbgs() << "Allocator " << (void *)this << " finalizing:\n");
206
207 // If there's an error then bail out here.
208 std::vector<SectionAllocGroup> SecAllocGroups;
209 {
210 std::lock_guard<std::mutex> Lock(M);
211 if (ErrMsg && !this->ErrMsg.empty()) {
212 *ErrMsg = std::move(this->ErrMsg);
213 return true;
214 }
215 std::swap(SecAllocGroups, Unfinalized);
216 }
217
218 // Loop over unfinalized objects to make finalization requests.
219 for (auto &SecAllocGroup : SecAllocGroups) {
220
223
224 ExecutorAddrRange *RemoteAddrs[3] = {&SecAllocGroup.RemoteCode,
225 &SecAllocGroup.RemoteROData,
226 &SecAllocGroup.RemoteRWData};
227
228 std::vector<SectionAlloc> *SegSections[3] = {&SecAllocGroup.CodeAllocs,
229 &SecAllocGroup.RODataAllocs,
230 &SecAllocGroup.RWDataAllocs};
231
233 std::unique_ptr<char[]> AggregateContents[3];
234
235 for (unsigned I = 0; I != 3; ++I) {
236 FR.Segments.push_back({});
237 auto &Seg = FR.Segments.back();
238 Seg.RAG = SegMemProts[I];
239 Seg.Addr = RemoteAddrs[I]->Start;
240 for (auto &SecAlloc : *SegSections[I]) {
241 Seg.Size = alignTo(Seg.Size, SecAlloc.Align);
242 Seg.Size += SecAlloc.Size;
243 }
244 AggregateContents[I] = std::make_unique<char[]>(Seg.Size);
245 size_t SecOffset = 0;
246 for (auto &SecAlloc : *SegSections[I]) {
247 SecOffset = alignTo(SecOffset, SecAlloc.Align);
248 memcpy(&AggregateContents[I][SecOffset],
249 reinterpret_cast<const char *>(
250 alignAddr(SecAlloc.Contents.get(), Align(SecAlloc.Align))),
251 SecAlloc.Size);
252 SecOffset += SecAlloc.Size;
253 // FIXME: Can we reset SecAlloc.Content here, now that it's copied into
254 // the aggregated content?
255 }
256 Seg.Content = {AggregateContents[I].get(), SecOffset};
257 }
258
259 for (auto &Frame : SecAllocGroup.UnfinalizedEHFrames)
260 FR.Actions.push_back(
261 {cantFail(
263 SAs.RegisterEHFrame, Frame)),
264 cantFail(
266 SAs.DeregisterEHFrame, Frame))});
267
268 // We'll also need to make an extra allocation for the eh-frame wrapper call
269 // arguments.
270 Error FinalizeErr = Error::success();
271 if (auto Err = EPC.callSPSWrapper<
273 SAs.Finalize, FinalizeErr, SAs.Instance, std::move(FR))) {
274 std::lock_guard<std::mutex> Lock(M);
275 this->ErrMsg = toString(std::move(Err));
276 dbgs() << "Serialization error: " << this->ErrMsg << "\n";
277 if (ErrMsg)
278 *ErrMsg = this->ErrMsg;
279 return true;
280 }
281 if (FinalizeErr) {
282 std::lock_guard<std::mutex> Lock(M);
283 this->ErrMsg = toString(std::move(FinalizeErr));
284 dbgs() << "Finalization error: " << this->ErrMsg << "\n";
285 if (ErrMsg)
286 *ErrMsg = this->ErrMsg;
287 return true;
288 }
289 }
290
291 return false;
292}
293
294void EPCGenericRTDyldMemoryManager::mapAllocsToRemoteAddrs(
295 RuntimeDyld &Dyld, std::vector<SectionAlloc> &Allocs,
296 ExecutorAddr NextAddr) {
297 for (auto &Alloc : Allocs) {
298 NextAddr.setValue(alignTo(NextAddr.getValue(), Alloc.Align));
299 LLVM_DEBUG({
300 dbgs() << " " << static_cast<void *>(Alloc.Contents.get()) << " -> "
301 << format("0x%016" PRIx64, NextAddr.getValue()) << "\n";
302 });
303 Dyld.mapSectionAddress(reinterpret_cast<const void *>(alignAddr(
304 Alloc.Contents.get(), Align(Alloc.Align))),
305 NextAddr.getValue());
306 Alloc.RemoteAddr = NextAddr;
307 // Only advance NextAddr if it was non-null to begin with,
308 // otherwise leave it as null.
309 if (NextAddr)
310 NextAddr += ExecutorAddrDiff(Alloc.Size);
311 }
312}
313
314} // end namespace orc
315} // end namespace llvm
#define LLVM_DEBUG(X)
Definition: Debug.h:101
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:50
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.
std::optional< const char * > toString(const std::optional< DWARFFormValue > &V)
Take an optional DWARFFormValue and try to extract a string value from it.
shared::SPSExpected< shared::SPSExecutorAddr >(shared::SPSExecutorAddr, uint64_t) SPSSimpleExecutorMemoryManagerReserveSignature
Definition: OrcRTBridge.h:64
shared::SPSError(shared::SPSExecutorAddr, shared::SPSFinalizeRequest) SPSSimpleExecutorMemoryManagerFinalizeSignature
Definition: OrcRTBridge.h:66
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSSimpleExecutorMemoryManagerDeallocateSignature
Definition: OrcRTBridge.h:68
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(const char *Fmt, Ts &&...Vals) -> formatv_object< decltype(std::make_tuple(support::detail::build_format_adapter(std::forward< Ts >(Vals))...))>
auto reverse(ContainerTy &&C)
Definition: STLExtras.h:419
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:1856
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