xref: /llvm-project/llvm/lib/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.cpp (revision 1f4d91ecb8529678a3d3919d7523743bd21942ca)
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 
9 #include "llvm/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.h"
10 #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
11 #include "llvm/Support/Alignment.h"
12 #include "llvm/Support/FormatVariadic.h"
13 
14 #define DEBUG_TYPE "orc"
15 
16 using namespace llvm::orc::shared;
17 
18 namespace llvm {
19 namespace orc {
20 
21 Expected<std::unique_ptr<EPCGenericRTDyldMemoryManager>>
22 EPCGenericRTDyldMemoryManager::CreateWithDefaultBootstrapSymbols(
23     ExecutorProcessControl &EPC) {
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 
37 EPCGenericRTDyldMemoryManager::EPCGenericRTDyldMemoryManager(
38     ExecutorProcessControl &EPC, SymbolAddrs SAs)
39     : EPC(EPC), SAs(std::move(SAs)) {
40   LLVM_DEBUG(dbgs() << "Created remote allocator " << (void *)this << "\n");
41 }
42 
43 EPCGenericRTDyldMemoryManager::~EPCGenericRTDyldMemoryManager() {
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<
50                   rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>(
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 
61 uint8_t *EPCGenericRTDyldMemoryManager::allocateCodeSection(
62     uintptr_t Size, unsigned Alignment, unsigned SectionID,
63     StringRef SectionName) {
64   std::lock_guard<std::mutex> Lock(M);
65   LLVM_DEBUG({
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 
76 uint8_t *EPCGenericRTDyldMemoryManager::allocateDataSection(
77     uintptr_t Size, unsigned Alignment, unsigned SectionID,
78     StringRef SectionName, bool IsReadOnly) {
79   std::lock_guard<std::mutex> Lock(M);
80   LLVM_DEBUG({
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 
95 void EPCGenericRTDyldMemoryManager::reserveAllocationSpace(
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<
131                  rt::SPSSimpleExecutorMemoryManagerReserveSignature>(
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 
155 bool EPCGenericRTDyldMemoryManager::needsToReserveAllocationSpace() {
156   return true;
157 }
158 
159 void EPCGenericRTDyldMemoryManager::registerEHFrames(uint8_t *Addr,
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 
183 void EPCGenericRTDyldMemoryManager::deregisterEHFrames() {
184   // This is a no-op for us: We've registered a deallocation action for it.
185 }
186 
187 void EPCGenericRTDyldMemoryManager::notifyObjectLoaded(
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 
203 bool EPCGenericRTDyldMemoryManager::finalizeMemory(std::string *ErrMsg) {
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 
220     MemProt SegMemProts[3] = {MemProt::Read | MemProt::Exec, MemProt::Read,
221                               MemProt::Read | MemProt::Write};
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 
231     tpctypes::FinalizeRequest FR;
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(
261                WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddrRange>>(
262                    SAs.RegisterEHFrame, Frame)),
263            cantFail(
264                WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddrRange>>(
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<
271                    rt::SPSSimpleExecutorMemoryManagerFinalizeSignature>(
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 
293 void 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
315