xref: /llvm-project/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp (revision 349e5bd24e72ebc568c0d171cc9f3aca2674a1db)
1 //===- MemoryMapper.cpp - Cross-process memory mapper ------------*- C++ -*-==//
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/MemoryMapper.h"
10 
11 #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
12 #include "llvm/Support/WindowsError.h"
13 
14 #include <algorithm>
15 
16 #if defined(LLVM_ON_UNIX) && !defined(__ANDROID__)
17 #include <fcntl.h>
18 #include <sys/mman.h>
19 #include <unistd.h>
20 #elif defined(_WIN32)
21 #include <windows.h>
22 #endif
23 
24 namespace llvm {
25 namespace orc {
26 
27 MemoryMapper::~MemoryMapper() {}
28 
29 InProcessMemoryMapper::InProcessMemoryMapper(size_t PageSize)
30     : PageSize(PageSize) {}
31 
32 Expected<std::unique_ptr<InProcessMemoryMapper>>
33 InProcessMemoryMapper::Create() {
34   auto PageSize = sys::Process::getPageSize();
35   if (!PageSize)
36     return PageSize.takeError();
37   return std::make_unique<InProcessMemoryMapper>(*PageSize);
38 }
39 
40 void InProcessMemoryMapper::reserve(size_t NumBytes,
41                                     OnReservedFunction OnReserved) {
42   std::error_code EC;
43   auto MB = sys::Memory::allocateMappedMemory(
44       NumBytes, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
45 
46   if (EC)
47     return OnReserved(errorCodeToError(EC));
48 
49   {
50     std::lock_guard<std::mutex> Lock(Mutex);
51     Reservations[MB.base()].Size = MB.allocatedSize();
52   }
53 
54   OnReserved(
55       ExecutorAddrRange(ExecutorAddr::fromPtr(MB.base()), MB.allocatedSize()));
56 }
57 
58 char *InProcessMemoryMapper::prepare(ExecutorAddr Addr, size_t ContentSize) {
59   return Addr.toPtr<char *>();
60 }
61 
62 void InProcessMemoryMapper::initialize(MemoryMapper::AllocInfo &AI,
63                                        OnInitializedFunction OnInitialized) {
64   ExecutorAddr MinAddr(~0ULL);
65   ExecutorAddr MaxAddr(0);
66 
67   for (auto &Segment : AI.Segments) {
68     auto Base = AI.MappingBase + Segment.Offset;
69     auto Size = Segment.ContentSize + Segment.ZeroFillSize;
70 
71     if (Base < MinAddr)
72       MinAddr = Base;
73 
74     if (Base + Size > MaxAddr)
75       MaxAddr = Base + Size;
76 
77     std::memset((Base + Segment.ContentSize).toPtr<void *>(), 0,
78                 Segment.ZeroFillSize);
79 
80     if (auto EC = sys::Memory::protectMappedMemory({Base.toPtr<void *>(), Size},
81                                                    Segment.Prot)) {
82       return OnInitialized(errorCodeToError(EC));
83     }
84     if (Segment.Prot & sys::Memory::MF_EXEC)
85       sys::Memory::InvalidateInstructionCache(Base.toPtr<void *>(), Size);
86   }
87 
88   auto DeinitializeActions = shared::runFinalizeActions(AI.Actions);
89   if (!DeinitializeActions)
90     return OnInitialized(DeinitializeActions.takeError());
91 
92   {
93     std::lock_guard<std::mutex> Lock(Mutex);
94 
95     // This is the maximum range whose permission have been possibly modified
96     Allocations[MinAddr].Size = MaxAddr - MinAddr;
97     Allocations[MinAddr].DeinitializationActions =
98         std::move(*DeinitializeActions);
99     Reservations[AI.MappingBase.toPtr<void *>()].Allocations.push_back(MinAddr);
100   }
101 
102   OnInitialized(MinAddr);
103 }
104 
105 void InProcessMemoryMapper::deinitialize(
106     ArrayRef<ExecutorAddr> Bases,
107     MemoryMapper::OnDeinitializedFunction OnDeinitialized) {
108   Error AllErr = Error::success();
109 
110   {
111     std::lock_guard<std::mutex> Lock(Mutex);
112 
113     for (auto Base : llvm::reverse(Bases)) {
114 
115       if (Error Err = shared::runDeallocActions(
116               Allocations[Base].DeinitializationActions)) {
117         AllErr = joinErrors(std::move(AllErr), std::move(Err));
118       }
119 
120       // Reset protections to read/write so the area can be reused
121       if (auto EC = sys::Memory::protectMappedMemory(
122               {Base.toPtr<void *>(), Allocations[Base].Size},
123               sys::Memory::ProtectionFlags::MF_READ |
124                   sys::Memory::ProtectionFlags::MF_WRITE)) {
125         AllErr = joinErrors(std::move(AllErr), errorCodeToError(EC));
126       }
127 
128       Allocations.erase(Base);
129     }
130   }
131 
132   OnDeinitialized(std::move(AllErr));
133 }
134 
135 void InProcessMemoryMapper::release(ArrayRef<ExecutorAddr> Bases,
136                                     OnReleasedFunction OnReleased) {
137   Error Err = Error::success();
138 
139   for (auto Base : Bases) {
140     std::vector<ExecutorAddr> AllocAddrs;
141     size_t Size;
142     {
143       std::lock_guard<std::mutex> Lock(Mutex);
144       auto &R = Reservations[Base.toPtr<void *>()];
145       Size = R.Size;
146       AllocAddrs.swap(R.Allocations);
147     }
148 
149     // deinitialize sub allocations
150     std::promise<MSVCPError> P;
151     auto F = P.get_future();
152     deinitialize(AllocAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
153     if (Error E = F.get()) {
154       Err = joinErrors(std::move(Err), std::move(E));
155     }
156 
157     // free the memory
158     auto MB = sys::MemoryBlock(Base.toPtr<void *>(), Size);
159 
160     auto EC = sys::Memory::releaseMappedMemory(MB);
161     if (EC) {
162       Err = joinErrors(std::move(Err), errorCodeToError(EC));
163     }
164 
165     std::lock_guard<std::mutex> Lock(Mutex);
166     Reservations.erase(Base.toPtr<void *>());
167   }
168 
169   OnReleased(std::move(Err));
170 }
171 
172 InProcessMemoryMapper::~InProcessMemoryMapper() {
173   std::vector<ExecutorAddr> ReservationAddrs;
174   {
175     std::lock_guard<std::mutex> Lock(Mutex);
176 
177     ReservationAddrs.reserve(Reservations.size());
178     for (const auto &R : Reservations) {
179       ReservationAddrs.push_back(ExecutorAddr::fromPtr(R.getFirst()));
180     }
181   }
182 
183   std::promise<MSVCPError> P;
184   auto F = P.get_future();
185   release(ReservationAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
186   cantFail(F.get());
187 }
188 
189 // SharedMemoryMapper
190 
191 SharedMemoryMapper::SharedMemoryMapper(ExecutorProcessControl &EPC,
192                                        SymbolAddrs SAs, size_t PageSize)
193     : EPC(EPC), SAs(SAs), PageSize(PageSize) {
194 #if (!defined(LLVM_ON_UNIX) || defined(__ANDROID__)) && !defined(_WIN32)
195   llvm_unreachable("SharedMemoryMapper is not supported on this platform yet");
196 #endif
197 }
198 
199 Expected<std::unique_ptr<SharedMemoryMapper>>
200 SharedMemoryMapper::Create(ExecutorProcessControl &EPC, SymbolAddrs SAs) {
201 #if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
202   auto PageSize = sys::Process::getPageSize();
203   if (!PageSize)
204     return PageSize.takeError();
205 
206   return std::make_unique<SharedMemoryMapper>(EPC, SAs, *PageSize);
207 #else
208   return make_error<StringError>(
209       "SharedMemoryMapper is not supported on this platform yet",
210       inconvertibleErrorCode());
211 #endif
212 }
213 
214 void SharedMemoryMapper::reserve(size_t NumBytes,
215                                  OnReservedFunction OnReserved) {
216 #if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
217 
218   EPC.callSPSWrapperAsync<
219       rt::SPSExecutorSharedMemoryMapperServiceReserveSignature>(
220       SAs.Reserve,
221       [this, NumBytes, OnReserved = std::move(OnReserved)](
222           Error SerializationErr,
223           Expected<std::pair<ExecutorAddr, std::string>> Result) mutable {
224         if (SerializationErr) {
225           cantFail(Result.takeError());
226           return OnReserved(std::move(SerializationErr));
227         }
228 
229         if (!Result)
230           return OnReserved(Result.takeError());
231 
232         ExecutorAddr RemoteAddr;
233         std::string SharedMemoryName;
234         std::tie(RemoteAddr, SharedMemoryName) = std::move(*Result);
235 
236         void *LocalAddr = nullptr;
237 
238 #if defined(LLVM_ON_UNIX)
239 
240         int SharedMemoryFile = shm_open(SharedMemoryName.c_str(), O_RDWR, 0700);
241         if (SharedMemoryFile < 0) {
242           return OnReserved(errorCodeToError(
243               std::error_code(errno, std::generic_category())));
244         }
245 
246         // this prevents other processes from accessing it by name
247         shm_unlink(SharedMemoryName.c_str());
248 
249         LocalAddr = mmap(nullptr, NumBytes, PROT_READ | PROT_WRITE, MAP_SHARED,
250                          SharedMemoryFile, 0);
251         if (LocalAddr == MAP_FAILED) {
252           return OnReserved(errorCodeToError(
253               std::error_code(errno, std::generic_category())));
254         }
255 
256         close(SharedMemoryFile);
257 
258 #elif defined(_WIN32)
259 
260         std::wstring WideSharedMemoryName(SharedMemoryName.begin(),
261                                           SharedMemoryName.end());
262         HANDLE SharedMemoryFile = OpenFileMappingW(
263             FILE_MAP_ALL_ACCESS, FALSE, WideSharedMemoryName.c_str());
264         if (!SharedMemoryFile)
265           return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
266 
267         LocalAddr =
268             MapViewOfFile(SharedMemoryFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
269         if (!LocalAddr) {
270           CloseHandle(SharedMemoryFile);
271           return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
272         }
273 
274         CloseHandle(SharedMemoryFile);
275 
276 #endif
277         {
278           std::lock_guard<std::mutex> Lock(Mutex);
279           Reservations.insert({RemoteAddr, {LocalAddr, NumBytes}});
280         }
281 
282         OnReserved(ExecutorAddrRange(RemoteAddr, NumBytes));
283       },
284       SAs.Instance, static_cast<uint64_t>(NumBytes));
285 
286 #else
287   OnReserved(make_error<StringError>(
288       "SharedMemoryMapper is not supported on this platform yet",
289       inconvertibleErrorCode()));
290 #endif
291 }
292 
293 char *SharedMemoryMapper::prepare(ExecutorAddr Addr, size_t ContentSize) {
294   auto R = Reservations.upper_bound(Addr);
295   assert(R != Reservations.begin() && "Attempt to prepare unreserved range");
296   R--;
297 
298   ExecutorAddrDiff Offset = Addr - R->first;
299 
300   return static_cast<char *>(R->second.LocalAddr) + Offset;
301 }
302 
303 void SharedMemoryMapper::initialize(MemoryMapper::AllocInfo &AI,
304                                     OnInitializedFunction OnInitialized) {
305   auto Reservation = Reservations.upper_bound(AI.MappingBase);
306   assert(Reservation != Reservations.begin() && "Attempt to initialize unreserved range");
307   Reservation--;
308 
309   auto AllocationOffset = AI.MappingBase - Reservation->first;
310 
311   tpctypes::SharedMemoryFinalizeRequest FR;
312 
313   AI.Actions.swap(FR.Actions);
314 
315   FR.Segments.reserve(AI.Segments.size());
316 
317   for (auto Segment : AI.Segments) {
318     char *Base = static_cast<char *>(Reservation->second.LocalAddr) +
319                  AllocationOffset + Segment.Offset;
320     std::memset(Base + Segment.ContentSize, 0, Segment.ZeroFillSize);
321 
322     tpctypes::SharedMemorySegFinalizeRequest SegReq;
323     SegReq.Prot = tpctypes::toWireProtectionFlags(
324         static_cast<sys::Memory::ProtectionFlags>(Segment.Prot));
325     SegReq.Addr = AI.MappingBase + Segment.Offset;
326     SegReq.Size = Segment.ContentSize + Segment.ZeroFillSize;
327 
328     FR.Segments.push_back(SegReq);
329   }
330 
331   EPC.callSPSWrapperAsync<
332       rt::SPSExecutorSharedMemoryMapperServiceInitializeSignature>(
333       SAs.Initialize,
334       [OnInitialized = std::move(OnInitialized)](
335           Error SerializationErr, Expected<ExecutorAddr> Result) mutable {
336         if (SerializationErr) {
337           cantFail(Result.takeError());
338           return OnInitialized(std::move(SerializationErr));
339         }
340 
341         OnInitialized(std::move(Result));
342       },
343       SAs.Instance, Reservation->first, std::move(FR));
344 }
345 
346 void SharedMemoryMapper::deinitialize(
347     ArrayRef<ExecutorAddr> Allocations,
348     MemoryMapper::OnDeinitializedFunction OnDeinitialized) {
349   EPC.callSPSWrapperAsync<
350       rt::SPSExecutorSharedMemoryMapperServiceDeinitializeSignature>(
351       SAs.Deinitialize,
352       [OnDeinitialized = std::move(OnDeinitialized)](Error SerializationErr,
353                                                      Error Result) mutable {
354         if (SerializationErr) {
355           cantFail(std::move(Result));
356           return OnDeinitialized(std::move(SerializationErr));
357         }
358 
359         OnDeinitialized(std::move(Result));
360       },
361       SAs.Instance, Allocations);
362 }
363 
364 void SharedMemoryMapper::release(ArrayRef<ExecutorAddr> Bases,
365                                  OnReleasedFunction OnReleased) {
366 #if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
367   Error Err = Error::success();
368 
369   {
370     std::lock_guard<std::mutex> Lock(Mutex);
371 
372     for (auto Base : Bases) {
373 
374 #if defined(LLVM_ON_UNIX)
375 
376       if (munmap(Reservations[Base].LocalAddr, Reservations[Base].Size) != 0)
377         Err = joinErrors(std::move(Err), errorCodeToError(std::error_code(
378                                              errno, std::generic_category())));
379 
380 #elif defined(_WIN32)
381 
382       if (!UnmapViewOfFile(Reservations[Base].LocalAddr))
383         Err = joinErrors(std::move(Err),
384                          errorCodeToError(mapWindowsError(GetLastError())));
385 
386 #endif
387 
388       Reservations.erase(Base);
389     }
390   }
391 
392   EPC.callSPSWrapperAsync<
393       rt::SPSExecutorSharedMemoryMapperServiceReleaseSignature>(
394       SAs.Release,
395       [OnReleased = std::move(OnReleased),
396        Err = std::move(Err)](Error SerializationErr, Error Result) mutable {
397         if (SerializationErr) {
398           cantFail(std::move(Result));
399           return OnReleased(
400               joinErrors(std::move(Err), std::move(SerializationErr)));
401         }
402 
403         return OnReleased(joinErrors(std::move(Err), std::move(Result)));
404       },
405       SAs.Instance, Bases);
406 #else
407   OnReleased(make_error<StringError>(
408       "SharedMemoryMapper is not supported on this platform yet",
409       inconvertibleErrorCode()));
410 #endif
411 }
412 
413 SharedMemoryMapper::~SharedMemoryMapper() {
414   for (const auto R : Reservations) {
415 
416 #if defined(LLVM_ON_UNIX) && !defined(__ANDROID__)
417 
418     munmap(R.second.LocalAddr, R.second.Size);
419 
420 #elif defined(_WIN32)
421 
422     UnmapViewOfFile(R.second.LocalAddr);
423 
424 #endif
425   }
426 }
427 
428 } // namespace orc
429 
430 } // namespace llvm
431