xref: /llvm-project/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp (revision 2ce73f13c98ad3bfb904ac991f5810ddff9e77e7)
1 //===- SimpleExecuorMemoryManagare.cpp - Simple executor-side memory mgmt -===//
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/TargetProcess/SimpleExecutorMemoryManager.h"
10 
11 #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
12 #include "llvm/Support/FormatVariadic.h"
13 
14 #define DEBUG_TYPE "orc"
15 
16 namespace llvm {
17 namespace orc {
18 namespace rt_bootstrap {
19 
20 SimpleExecutorMemoryManager::~SimpleExecutorMemoryManager() {
21   assert(Allocations.empty() && "shutdown not called?");
22 }
23 
24 Expected<ExecutorAddress> SimpleExecutorMemoryManager::allocate(uint64_t Size) {
25   std::error_code EC;
26   auto MB = sys::Memory::allocateMappedMemory(
27       Size, 0, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
28   if (EC)
29     return errorCodeToError(EC);
30   std::lock_guard<std::mutex> Lock(M);
31   assert(!Allocations.count(MB.base()) && "Duplicate allocation addr");
32   Allocations[MB.base()].Size = Size;
33   return ExecutorAddress::fromPtr(MB.base());
34 }
35 
36 Error SimpleExecutorMemoryManager::finalize(tpctypes::FinalizeRequest &FR) {
37   ExecutorAddress Base(~0ULL);
38   std::vector<tpctypes::SupportFunctionCall> DeallocationActions;
39   size_t SuccessfulFinalizationActions = 0;
40 
41   for (auto &Seg : FR.Segments)
42     Base = std::min(Base, Seg.Addr);
43 
44   for (auto &ActPair : FR.Actions)
45     if (ActPair.Deallocate.Func)
46       DeallocationActions.push_back(ActPair.Deallocate);
47 
48   // Get the Allocation for this finalization.
49   size_t AllocSize = 0;
50   {
51     std::lock_guard<std::mutex> Lock(M);
52     auto I = Allocations.find(Base.toPtr<void *>());
53     if (I == Allocations.end())
54       return make_error<StringError>("Attempt to finalize unrecognized "
55                                      "allocation " +
56                                          formatv("{0:x}", Base.getValue()),
57                                      inconvertibleErrorCode());
58     AllocSize = I->second.Size;
59     I->second.DeallocationActions = std::move(DeallocationActions);
60   }
61   ExecutorAddress AllocEnd = Base + ExecutorAddrDiff(AllocSize);
62 
63   // Bail-out function: this will run deallocation actions corresponding to any
64   // completed finalization actions, then deallocate memory.
65   auto BailOut = [&](Error Err) {
66     std::pair<void *, Allocation> AllocToDestroy;
67 
68     // Get allocation to destory.
69     {
70       std::lock_guard<std::mutex> Lock(M);
71       auto I = Allocations.find(Base.toPtr<void *>());
72 
73       // Check for missing allocation (effective a double free).
74       if (I == Allocations.end())
75         return joinErrors(
76             std::move(Err),
77             make_error<StringError>("No allocation entry found "
78                                     "for " +
79                                         formatv("{0:x}", Base.getValue()),
80                                     inconvertibleErrorCode()));
81       AllocToDestroy = std::move(*I);
82       Allocations.erase(I);
83     }
84 
85     // Run deallocation actions for all completed finalization actions.
86     while (SuccessfulFinalizationActions)
87       Err = joinErrors(
88           std::move(Err),
89           FR.Actions[--SuccessfulFinalizationActions].Deallocate.run());
90 
91     // Deallocate memory.
92     sys::MemoryBlock MB(AllocToDestroy.first, AllocToDestroy.second.Size);
93     if (auto EC = sys::Memory::releaseMappedMemory(MB))
94       Err = joinErrors(std::move(Err), errorCodeToError(EC));
95 
96     return Err;
97   };
98 
99   // Copy content and apply permissions.
100   for (auto &Seg : FR.Segments) {
101 
102     // Check segment ranges.
103     if (LLVM_UNLIKELY(Seg.Size < Seg.Content.size()))
104       return BailOut(make_error<StringError>(
105           formatv("Segment {0:x} content size ({1:x} bytes) "
106                   "exceeds segment size ({2:x} bytes)",
107                   Seg.Addr.getValue(), Seg.Content.size(), Seg.Size),
108           inconvertibleErrorCode()));
109     ExecutorAddress SegEnd = Seg.Addr + ExecutorAddrDiff(Seg.Size);
110     if (LLVM_UNLIKELY(Seg.Addr < Base || SegEnd > AllocEnd))
111       return BailOut(make_error<StringError>(
112           formatv("Segment {0:x} -- {1:x} crosses boundary of "
113                   "allocation {2:x} -- {3:x}",
114                   Seg.Addr.getValue(), SegEnd.getValue(), Base.getValue(),
115                   AllocEnd.getValue()),
116           inconvertibleErrorCode()));
117 
118     char *Mem = Seg.Addr.toPtr<char *>();
119     memcpy(Mem, Seg.Content.data(), Seg.Content.size());
120     memset(Mem + Seg.Content.size(), 0, Seg.Size - Seg.Content.size());
121     assert(Seg.Size <= std::numeric_limits<size_t>::max());
122     if (auto EC = sys::Memory::protectMappedMemory(
123             {Mem, static_cast<size_t>(Seg.Size)},
124             tpctypes::fromWireProtectionFlags(Seg.Prot)))
125       return BailOut(errorCodeToError(EC));
126     if (Seg.Prot & tpctypes::WPF_Exec)
127       sys::Memory::InvalidateInstructionCache(Mem, Seg.Size);
128   }
129 
130   // Run finalization actions.
131   for (auto &ActPair : FR.Actions) {
132     if (auto Err = ActPair.Finalize.run())
133       return BailOut(std::move(Err));
134     ++SuccessfulFinalizationActions;
135   }
136 
137   return Error::success();
138 }
139 
140 Error SimpleExecutorMemoryManager::deallocate(
141     const std::vector<ExecutorAddress> &Bases) {
142   std::vector<std::pair<void *, Allocation>> AllocPairs;
143   AllocPairs.reserve(Bases.size());
144 
145   // Get allocation to destory.
146   Error Err = Error::success();
147   {
148     std::lock_guard<std::mutex> Lock(M);
149     for (auto &Base : Bases) {
150       auto I = Allocations.find(Base.toPtr<void *>());
151 
152       // Check for missing allocation (effective a double free).
153       if (I != Allocations.end()) {
154         AllocPairs.push_back(std::move(*I));
155         Allocations.erase(I);
156       } else
157         Err = joinErrors(
158             std::move(Err),
159             make_error<StringError>("No allocation entry found "
160                                     "for " +
161                                         formatv("{0:x}", Base.getValue()),
162                                     inconvertibleErrorCode()));
163     }
164   }
165 
166   while (!AllocPairs.empty()) {
167     auto &P = AllocPairs.back();
168     Err = joinErrors(std::move(Err), deallocateImpl(P.first, P.second));
169     AllocPairs.pop_back();
170   }
171 
172   return Err;
173 }
174 
175 Error SimpleExecutorMemoryManager::shutdown() {
176 
177   AllocationsMap AM;
178   {
179     std::lock_guard<std::mutex> Lock(M);
180     AM = std::move(Allocations);
181   }
182 
183   Error Err = Error::success();
184   for (auto &KV : AM)
185     Err = joinErrors(std::move(Err), deallocateImpl(KV.first, KV.second));
186   return Err;
187 }
188 
189 void SimpleExecutorMemoryManager::addBootstrapSymbols(
190     StringMap<ExecutorAddress> &M) {
191   M[rt::SimpleExecutorMemoryManagerInstanceName] =
192       ExecutorAddress::fromPtr(this);
193   M[rt::SimpleExecutorMemoryManagerReserveWrapperName] =
194       ExecutorAddress::fromPtr(&reserveWrapper);
195   M[rt::SimpleExecutorMemoryManagerFinalizeWrapperName] =
196       ExecutorAddress::fromPtr(&finalizeWrapper);
197   M[rt::SimpleExecutorMemoryManagerDeallocateWrapperName] =
198       ExecutorAddress::fromPtr(&deallocateWrapper);
199 }
200 
201 Error SimpleExecutorMemoryManager::deallocateImpl(void *Base, Allocation &A) {
202   Error Err = Error::success();
203 
204   while (!A.DeallocationActions.empty()) {
205     Err = joinErrors(std::move(Err), A.DeallocationActions.back().run());
206     A.DeallocationActions.pop_back();
207   }
208 
209   sys::MemoryBlock MB(Base, A.Size);
210   if (auto EC = sys::Memory::releaseMappedMemory(MB))
211     Err = joinErrors(std::move(Err), errorCodeToError(EC));
212 
213   return Err;
214 }
215 
216 llvm::orc::shared::detail::CWrapperFunctionResult
217 SimpleExecutorMemoryManager::reserveWrapper(const char *ArgData,
218                                             size_t ArgSize) {
219   return shared::WrapperFunction<
220              rt::SPSSimpleExecutorMemoryManagerReserveSignature>::
221       handle(ArgData, ArgSize,
222              shared::makeMethodWrapperHandler(
223                  &SimpleExecutorMemoryManager::allocate))
224           .release();
225 }
226 
227 llvm::orc::shared::detail::CWrapperFunctionResult
228 SimpleExecutorMemoryManager::finalizeWrapper(const char *ArgData,
229                                              size_t ArgSize) {
230   return shared::WrapperFunction<
231              rt::SPSSimpleExecutorMemoryManagerFinalizeSignature>::
232       handle(ArgData, ArgSize,
233              shared::makeMethodWrapperHandler(
234                  &SimpleExecutorMemoryManager::finalize))
235           .release();
236 }
237 
238 llvm::orc::shared::detail::CWrapperFunctionResult
239 SimpleExecutorMemoryManager::deallocateWrapper(const char *ArgData,
240                                                size_t ArgSize) {
241   return shared::WrapperFunction<
242              rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>::
243       handle(ArgData, ArgSize,
244              shared::makeMethodWrapperHandler(
245                  &SimpleExecutorMemoryManager::deallocate))
246           .release();
247 }
248 
249 } // namespace rt_bootstrap
250 } // end namespace orc
251 } // end namespace llvm
252