LLVM 24.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
22Expected<std::unique_ptr<EPCGenericRTDyldMemoryManager>>
25 SymbolAddrs SAs;
26 if (auto Err = EPC.getBootstrapSymbols(
27 {{SAs.Instance, rt::sps_ci::SimpleNativeMemoryMapInstanceName},
28 {SAs.Reserve, rt::sps_ci::MemMgrReserve::Name},
29 {SAs.Initialize, rt::sps_ci::MemMgrInitialize::Name},
30 {SAs.Release, rt::sps_ci::MemMgrRelease::Name},
31 {SAs.RegisterEHFrame, rt::RegisterEHFrameSectionAllocActionName},
32 {SAs.DeregisterEHFrame,
33 rt::DeregisterEHFrameSectionAllocActionName}}))
34 return std::move(Err);
35 return std::make_unique<EPCGenericRTDyldMemoryManager>(EPC, std::move(SAs));
36}
37
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<rt::sps_ci::MemMgrRelease::SPSSig>(
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<rt::sps_ci::MemMgrReserve::SPSSig>(
131 SAs.Reserve, TargetAllocAddr, SAs.Instance, TotalSize)) {
132 std::lock_guard<std::mutex> Lock(M);
133 ErrMsg = toString(std::move(Err));
134 return;
135 }
136 if (!TargetAllocAddr) {
137 std::lock_guard<std::mutex> Lock(M);
138 ErrMsg = toString(TargetAllocAddr.takeError());
139 return;
140 }
141
142 std::lock_guard<std::mutex> Lock(M);
143 Unmapped.push_back(SectionAllocGroup());
144 Unmapped.back().RemoteCode = {
145 *TargetAllocAddr, ExecutorAddrDiff(alignTo(CodeSize, EPC.getPageSize()))};
146 Unmapped.back().RemoteROData = {
147 Unmapped.back().RemoteCode.End,
148 ExecutorAddrDiff(alignTo(RODataSize, EPC.getPageSize()))};
149 Unmapped.back().RemoteRWData = {
150 Unmapped.back().RemoteROData.End,
151 ExecutorAddrDiff(alignTo(RWDataSize, EPC.getPageSize()))};
152}
153
157
159 uint64_t LoadAddr,
160 size_t Size) {
161 LLVM_DEBUG({
162 dbgs() << "Allocator " << (void *)this << " added unfinalized eh-frame "
163 << formatv("[ {0:x} {1:x} ]", LoadAddr, LoadAddr + Size) << "\n";
164 });
165 std::lock_guard<std::mutex> Lock(M);
166 // Bail out early if there's already an error.
167 if (!ErrMsg.empty())
168 return;
169
170 ExecutorAddr LA(LoadAddr);
171 for (auto &SecAllocGroup : llvm::reverse(Unfinalized)) {
172 if (SecAllocGroup.RemoteCode.contains(LA) ||
173 SecAllocGroup.RemoteROData.contains(LA) ||
174 SecAllocGroup.RemoteRWData.contains(LA)) {
175 SecAllocGroup.UnfinalizedEHFrames.push_back({LA, Size});
176 return;
177 }
178 }
179 ErrMsg = "eh-frame does not lie inside unfinalized alloc";
180}
181
183 // This is a no-op for us: We've registered a deallocation action for it.
184}
185
187 RuntimeDyld &Dyld, const object::ObjectFile &Obj) {
188 std::lock_guard<std::mutex> Lock(M);
189 LLVM_DEBUG(dbgs() << "Allocator " << (void *)this << " applied mappings:\n");
190 for (auto &ObjAllocs : Unmapped) {
191 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.CodeAllocs,
192 ObjAllocs.RemoteCode.Start);
193 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.RODataAllocs,
194 ObjAllocs.RemoteROData.Start);
195 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.RWDataAllocs,
196 ObjAllocs.RemoteRWData.Start);
197 Unfinalized.push_back(std::move(ObjAllocs));
198 }
199 Unmapped.clear();
200}
201
203 LLVM_DEBUG(dbgs() << "Allocator " << (void *)this << " finalizing:\n");
204
205 // If there's an error then bail out here.
206 std::vector<SectionAllocGroup> SecAllocGroups;
207 {
208 std::lock_guard<std::mutex> Lock(M);
209 if (ErrMsg && !this->ErrMsg.empty()) {
210 *ErrMsg = std::move(this->ErrMsg);
211 return true;
212 }
213 std::swap(SecAllocGroups, Unfinalized);
214 }
215
216 // Loop over unfinalized objects to make finalization requests.
217 for (auto &SecAllocGroup : SecAllocGroups) {
218
221
222 ExecutorAddrRange *RemoteAddrs[3] = {&SecAllocGroup.RemoteCode,
223 &SecAllocGroup.RemoteROData,
224 &SecAllocGroup.RemoteRWData};
225
226 std::vector<SectionAlloc> *SegSections[3] = {&SecAllocGroup.CodeAllocs,
227 &SecAllocGroup.RODataAllocs,
228 &SecAllocGroup.RWDataAllocs};
229
231 std::unique_ptr<char[]> AggregateContents[3];
232
233 for (unsigned I = 0; I != 3; ++I) {
234 FR.Segments.push_back({});
235 auto &Seg = FR.Segments.back();
236 Seg.RAG = SegMemProts[I];
237 Seg.Addr = RemoteAddrs[I]->Start;
238 for (auto &SecAlloc : *SegSections[I]) {
239 Seg.Size = alignTo(Seg.Size, SecAlloc.Align);
240 Seg.Size += SecAlloc.Size;
241 }
242 AggregateContents[I] = std::make_unique<char[]>(Seg.Size);
243 size_t SecOffset = 0;
244 for (auto &SecAlloc : *SegSections[I]) {
245 SecOffset = alignTo(SecOffset, SecAlloc.Align);
246 memcpy(&AggregateContents[I][SecOffset],
247 reinterpret_cast<const char *>(
248 alignAddr(SecAlloc.Contents.get(), Align(SecAlloc.Align))),
249 SecAlloc.Size);
250 SecOffset += SecAlloc.Size;
251 // FIXME: Can we reset SecAlloc.Content here, now that it's copied into
252 // the aggregated content?
253 }
254 Seg.Content = {AggregateContents[I].get(), SecOffset};
255 }
256
257 for (auto &Frame : SecAllocGroup.UnfinalizedEHFrames)
258 FR.Actions.push_back(
259 {cantFail(
261 SAs.RegisterEHFrame, Frame)),
262 cantFail(
264 SAs.DeregisterEHFrame, Frame))});
265
266 // We'll also need to make an extra allocation for the eh-frame wrapper call
267 // arguments.
268 Expected<ExecutorAddr> InitializeKey((ExecutorAddr()));
269 if (auto Err = EPC.callSPSWrapper<rt::sps_ci::MemMgrInitialize::SPSSig>(
270 SAs.Initialize, InitializeKey, SAs.Instance, std::move(FR))) {
271 std::lock_guard<std::mutex> Lock(M);
272 this->ErrMsg = toString(std::move(Err));
273 dbgs() << "Serialization error: " << this->ErrMsg << "\n";
274 if (ErrMsg)
275 *ErrMsg = this->ErrMsg;
276 return true;
277 }
278 if (!InitializeKey) {
279 std::lock_guard<std::mutex> Lock(M);
280 this->ErrMsg = toString(InitializeKey.takeError());
281 dbgs() << "Finalization error: " << this->ErrMsg << "\n";
282 if (ErrMsg)
283 *ErrMsg = this->ErrMsg;
284 return true;
285 }
286 }
287
288 return false;
289}
290
291void EPCGenericRTDyldMemoryManager::mapAllocsToRemoteAddrs(
292 RuntimeDyld &Dyld, std::vector<SectionAlloc> &Allocs,
293 ExecutorAddr NextAddr) {
294 for (auto &Alloc : Allocs) {
295 NextAddr.setValue(alignTo(NextAddr.getValue(), Alloc.Align));
296 LLVM_DEBUG({
297 dbgs() << " " << static_cast<void *>(Alloc.Contents.get()) << " -> "
298 << format("0x%016" PRIx64, NextAddr.getValue()) << "\n";
299 });
300 Dyld.mapSectionAddress(reinterpret_cast<const void *>(alignAddr(
301 Alloc.Contents.get(), Align(Alloc.Align))),
302 NextAddr.getValue());
303 Alloc.RemoteAddr = NextAddr;
304 // Only advance NextAddr if it was non-null to begin with,
305 // otherwise leave it as null.
306 if (NextAddr)
307 NextAddr += ExecutorAddrDiff(Alloc.Size);
308 }
309}
310
311} // end namespace orc
312} // end namespace llvm
#define I(x, y, z)
Definition MD5.cpp:57
#define LLVM_DEBUG(...)
Definition Debug.h:119
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
LLVM_ABI void mapSectionAddress(const void *LocalAddress, uint64_t TargetAddress)
Map a section to its target address space value.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
This class is the base class for all object file types.
Definition ObjectFile.h:231
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...
EPCGenericRTDyldMemoryManager(ExecutorProcessControl &EPC, SymbolAddrs SAs)
Create an EPCGenericRTDyldMemoryManager using the given EPC and symbol addrs.
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.
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.
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.
LLVM_ABI void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner={})
Log all errors (if any) in E to OS.
Definition Error.cpp:61
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
constexpr uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition Alignment.h:144
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition Format.h:102
LLVM_ABI 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:769
std::string toString(const APInt &I, unsigned Radix, bool Signed, bool formatAsCLiteral=false, bool UpperCase=true, bool InsertSeparators=false)
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:1917
uintptr_t alignAddr(const void *Addr, Align Alignment)
Aligns Addr to Alignment bytes, rounding up.
Definition Alignment.h:176
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:878
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:880
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.
shared::SPSExpected< shared::SPSExecutorAddr >( shared::SPSExecutorAddr, shared::SPSFinalizeRequest) SPSSig
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSSig
shared::SPSExpected< shared::SPSExecutorAddr >( shared::SPSExecutorAddr, uint64_t) SPSSig
std::vector< SegFinalizeRequest > Segments