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
17
18#define DEBUG_TYPE "orc"
19
20using namespace llvm::orc::shared;
21
22namespace llvm {
23namespace orc {
24
25Expected<std::unique_ptr<EPCGenericRTDyldMemoryManager>>
28 SymbolAddrs SAs;
29 if (auto Err = lookupAndApply(
30 EPC.getExecutionSession().getBootstrapJITDylib(),
31 {recordAddr(rt::sps_ci::SimpleNativeMemoryMapInstanceName,
32 &SAs.MemMgr.Instance),
33 recordProxy<sps::MemMgrReserveProxySpec>(&SAs.MemMgr.Reserve),
34 recordProxy<sps::MemMgrInitializeProxySpec>(&SAs.MemMgr.Initialize),
35 recordProxy<sps::MemMgrReleaseProxySpec>(&SAs.MemMgr.Release),
36 recordAddr(rt::RegisterEHFrameSectionAllocActionName,
37 &SAs.RegisterEHFrame),
38 recordAddr(rt::DeregisterEHFrameSectionAllocActionName,
39 &SAs.DeregisterEHFrame)}))
40 return std::move(Err);
41 return std::make_unique<EPCGenericRTDyldMemoryManager>(EPC, std::move(SAs));
42}
43
46 : EPC(EPC), SAs(std::move(SAs)) {
47 LLVM_DEBUG(dbgs() << "Created remote allocator " << (void *)this << "\n");
48}
49
51 LLVM_DEBUG(dbgs() << "Destroyed remote allocator " << (void *)this << "\n");
52 if (!ErrMsg.empty())
53 errs() << "Destroying with existing errors:\n" << ErrMsg << "\n";
54
55 // FIXME: Report errors through EPC once that functionality is available.
56 if (auto Err = SAs.MemMgr.Release(EPC.getExecutionSession(),
57 SAs.MemMgr.Instance, FinalizedAllocs))
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 = SAs.MemMgr.Reserve(
130 EPC.getExecutionSession(), SAs.MemMgr.Instance, TotalSize);
131 if (!TargetAllocAddr) {
132 std::lock_guard<std::mutex> Lock(M);
133 ErrMsg = toString(TargetAllocAddr.takeError());
134 return;
135 }
136
137 std::lock_guard<std::mutex> Lock(M);
138 Unmapped.push_back(SectionAllocGroup());
139 Unmapped.back().RemoteCode = {
140 *TargetAllocAddr, ExecutorAddrDiff(alignTo(CodeSize, EPC.getPageSize()))};
141 Unmapped.back().RemoteROData = {
142 Unmapped.back().RemoteCode.End,
143 ExecutorAddrDiff(alignTo(RODataSize, EPC.getPageSize()))};
144 Unmapped.back().RemoteRWData = {
145 Unmapped.back().RemoteROData.End,
146 ExecutorAddrDiff(alignTo(RWDataSize, EPC.getPageSize()))};
147}
148
152
154 uint64_t LoadAddr,
155 size_t Size) {
156 LLVM_DEBUG({
157 dbgs() << "Allocator " << (void *)this << " added unfinalized eh-frame "
158 << formatv("[ {0:x} {1:x} ]", LoadAddr, LoadAddr + Size) << "\n";
159 });
160 std::lock_guard<std::mutex> Lock(M);
161 // Bail out early if there's already an error.
162 if (!ErrMsg.empty())
163 return;
164
165 ExecutorAddr LA(LoadAddr);
166 for (auto &SecAllocGroup : llvm::reverse(Unfinalized)) {
167 if (SecAllocGroup.RemoteCode.contains(LA) ||
168 SecAllocGroup.RemoteROData.contains(LA) ||
169 SecAllocGroup.RemoteRWData.contains(LA)) {
170 SecAllocGroup.UnfinalizedEHFrames.push_back({LA, Size});
171 return;
172 }
173 }
174 ErrMsg = "eh-frame does not lie inside unfinalized alloc";
175}
176
178 // This is a no-op for us: We've registered a deallocation action for it.
179}
180
182 RuntimeDyld &Dyld, const object::ObjectFile &Obj) {
183 std::lock_guard<std::mutex> Lock(M);
184 LLVM_DEBUG(dbgs() << "Allocator " << (void *)this << " applied mappings:\n");
185 for (auto &ObjAllocs : Unmapped) {
186 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.CodeAllocs,
187 ObjAllocs.RemoteCode.Start);
188 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.RODataAllocs,
189 ObjAllocs.RemoteROData.Start);
190 mapAllocsToRemoteAddrs(Dyld, ObjAllocs.RWDataAllocs,
191 ObjAllocs.RemoteRWData.Start);
192 Unfinalized.push_back(std::move(ObjAllocs));
193 }
194 Unmapped.clear();
195}
196
198 LLVM_DEBUG(dbgs() << "Allocator " << (void *)this << " finalizing:\n");
199
200 // If there's an error then bail out here.
201 std::vector<SectionAllocGroup> SecAllocGroups;
202 {
203 std::lock_guard<std::mutex> Lock(M);
204 if (ErrMsg && !this->ErrMsg.empty()) {
205 *ErrMsg = std::move(this->ErrMsg);
206 return true;
207 }
208 std::swap(SecAllocGroups, Unfinalized);
209 }
210
211 // Loop over unfinalized objects to make finalization requests.
212 for (auto &SecAllocGroup : SecAllocGroups) {
213
216
217 ExecutorAddrRange *RemoteAddrs[3] = {&SecAllocGroup.RemoteCode,
218 &SecAllocGroup.RemoteROData,
219 &SecAllocGroup.RemoteRWData};
220
221 std::vector<SectionAlloc> *SegSections[3] = {&SecAllocGroup.CodeAllocs,
222 &SecAllocGroup.RODataAllocs,
223 &SecAllocGroup.RWDataAllocs};
224
226 std::unique_ptr<char[]> AggregateContents[3];
227
228 for (unsigned I = 0; I != 3; ++I) {
229 FR.Segments.push_back({});
230 auto &Seg = FR.Segments.back();
231 Seg.RAG = SegMemProts[I];
232 Seg.Addr = RemoteAddrs[I]->Start;
233 for (auto &SecAlloc : *SegSections[I]) {
234 Seg.Size = alignTo(Seg.Size, SecAlloc.Align);
235 Seg.Size += SecAlloc.Size;
236 }
237 AggregateContents[I] = std::make_unique<char[]>(Seg.Size);
238 size_t SecOffset = 0;
239 for (auto &SecAlloc : *SegSections[I]) {
240 SecOffset = alignTo(SecOffset, SecAlloc.Align);
241 memcpy(&AggregateContents[I][SecOffset],
242 reinterpret_cast<const char *>(
243 alignAddr(SecAlloc.Contents.get(), Align(SecAlloc.Align))),
244 SecAlloc.Size);
245 SecOffset += SecAlloc.Size;
246 // FIXME: Can we reset SecAlloc.Content here, now that it's copied into
247 // the aggregated content?
248 }
249 Seg.Content = {AggregateContents[I].get(), SecOffset};
250 }
251
252 for (auto &Frame : SecAllocGroup.UnfinalizedEHFrames)
253 FR.Actions.push_back(
254 {cantFail(
256 SAs.RegisterEHFrame, Frame)),
257 cantFail(
259 SAs.DeregisterEHFrame, Frame))});
260
261 // We'll also need to make an extra allocation for the eh-frame wrapper call
262 // arguments.
263 Expected<ExecutorAddr> InitializeKey = SAs.MemMgr.Initialize(
264 EPC.getExecutionSession(), SAs.MemMgr.Instance, std::move(FR));
265 if (!InitializeKey) {
266 std::lock_guard<std::mutex> Lock(M);
267 this->ErrMsg = toString(InitializeKey.takeError());
268 dbgs() << "Finalization error: " << this->ErrMsg << "\n";
269 if (ErrMsg)
270 *ErrMsg = this->ErrMsg;
271 return true;
272 }
273 }
274
275 return false;
276}
277
278void EPCGenericRTDyldMemoryManager::mapAllocsToRemoteAddrs(
279 RuntimeDyld &Dyld, std::vector<SectionAlloc> &Allocs,
280 ExecutorAddr NextAddr) {
281 for (auto &Alloc : Allocs) {
282 NextAddr.setValue(alignTo(NextAddr.getValue(), Alloc.Align));
283 LLVM_DEBUG({
284 dbgs() << " " << static_cast<void *>(Alloc.Contents.get()) << " -> "
285 << format("0x%016" PRIx64, NextAddr.getValue()) << "\n";
286 });
287 Dyld.mapSectionAddress(reinterpret_cast<const void *>(alignAddr(
288 Alloc.Contents.get(), Align(Alloc.Align))),
289 NextAddr.getValue());
290 Alloc.RemoteAddr = NextAddr;
291 // Only advance NextAddr if it was non-null to begin with,
292 // otherwise leave it as null.
293 if (NextAddr)
294 NextAddr += ExecutorAddrDiff(Alloc.Size);
295 }
296}
297
298} // end namespace orc
299} // end namespace llvm
#define I(x, y, z)
Definition MD5.cpp:57
#define LLVM_DEBUG(...)
Definition Debug.h:119
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
LLVM_ABI void lookupAndApply(unique_function< void(Error)> OnApplied, LookupKind K, const JITDylibSearchOrder &SearchOrder, ArrayRef< LookupPrepareFn > PrepareFns)
Resolve the symbols contributed by every prepare function with a single lookup, then let each of thei...
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:408
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:1933
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
Bindings to the executor-side memory manager, plus the EH-frame registration alloc-action wrappers.
Represents an address range in the exceutor process.
std::vector< SegFinalizeRequest > Segments