1 //===- MemoryMapper.cpp - Cross-process memory mapper ------------*- C++ -*-==//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/ExecutionEngine/Orc/MemoryMapper.h"
11 #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
12 #include "llvm/Support/WindowsError.h"
16 #if defined(LLVM_ON_UNIX) && !defined(__ANDROID__)
27 MemoryMapper::~MemoryMapper() {}
29 InProcessMemoryMapper::InProcessMemoryMapper(size_t PageSize
)
30 : PageSize(PageSize
) {}
32 Expected
<std::unique_ptr
<InProcessMemoryMapper
>>
33 InProcessMemoryMapper::Create() {
34 auto PageSize
= sys::Process::getPageSize();
36 return PageSize
.takeError();
37 return std::make_unique
<InProcessMemoryMapper
>(*PageSize
);
40 void InProcessMemoryMapper::reserve(size_t NumBytes
,
41 OnReservedFunction OnReserved
) {
43 auto MB
= sys::Memory::allocateMappedMemory(
44 NumBytes
, nullptr, sys::Memory::MF_READ
| sys::Memory::MF_WRITE
, EC
);
47 return OnReserved(errorCodeToError(EC
));
50 std::lock_guard
<std::mutex
> Lock(Mutex
);
51 Reservations
[MB
.base()].Size
= MB
.allocatedSize();
55 ExecutorAddrRange(ExecutorAddr::fromPtr(MB
.base()), MB
.allocatedSize()));
58 char *InProcessMemoryMapper::prepare(ExecutorAddr Addr
, size_t ContentSize
) {
59 return Addr
.toPtr
<char *>();
62 void InProcessMemoryMapper::initialize(MemoryMapper::AllocInfo
&AI
,
63 OnInitializedFunction OnInitialized
) {
64 ExecutorAddr
MinAddr(~0ULL);
65 ExecutorAddr
MaxAddr(0);
67 // FIXME: Release finalize lifetime segments.
68 for (auto &Segment
: AI
.Segments
) {
69 auto Base
= AI
.MappingBase
+ Segment
.Offset
;
70 auto Size
= Segment
.ContentSize
+ Segment
.ZeroFillSize
;
75 if (Base
+ Size
> MaxAddr
)
76 MaxAddr
= Base
+ Size
;
78 std::memset((Base
+ Segment
.ContentSize
).toPtr
<void *>(), 0,
79 Segment
.ZeroFillSize
);
81 if (auto EC
= sys::Memory::protectMappedMemory(
82 {Base
.toPtr
<void *>(), Size
},
83 toSysMemoryProtectionFlags(Segment
.AG
.getMemProt()))) {
84 return OnInitialized(errorCodeToError(EC
));
86 if ((Segment
.AG
.getMemProt() & MemProt::Exec
) == MemProt::Exec
)
87 sys::Memory::InvalidateInstructionCache(Base
.toPtr
<void *>(), Size
);
90 auto DeinitializeActions
= shared::runFinalizeActions(AI
.Actions
);
91 if (!DeinitializeActions
)
92 return OnInitialized(DeinitializeActions
.takeError());
95 std::lock_guard
<std::mutex
> Lock(Mutex
);
97 // This is the maximum range whose permission have been possibly modified
98 Allocations
[MinAddr
].Size
= MaxAddr
- MinAddr
;
99 Allocations
[MinAddr
].DeinitializationActions
=
100 std::move(*DeinitializeActions
);
101 Reservations
[AI
.MappingBase
.toPtr
<void *>()].Allocations
.push_back(MinAddr
);
104 OnInitialized(MinAddr
);
107 void InProcessMemoryMapper::deinitialize(
108 ArrayRef
<ExecutorAddr
> Bases
,
109 MemoryMapper::OnDeinitializedFunction OnDeinitialized
) {
110 Error AllErr
= Error::success();
113 std::lock_guard
<std::mutex
> Lock(Mutex
);
115 for (auto Base
: llvm::reverse(Bases
)) {
117 if (Error Err
= shared::runDeallocActions(
118 Allocations
[Base
].DeinitializationActions
)) {
119 AllErr
= joinErrors(std::move(AllErr
), std::move(Err
));
122 // Reset protections to read/write so the area can be reused
123 if (auto EC
= sys::Memory::protectMappedMemory(
124 {Base
.toPtr
<void *>(), Allocations
[Base
].Size
},
125 sys::Memory::ProtectionFlags::MF_READ
|
126 sys::Memory::ProtectionFlags::MF_WRITE
)) {
127 AllErr
= joinErrors(std::move(AllErr
), errorCodeToError(EC
));
130 Allocations
.erase(Base
);
134 OnDeinitialized(std::move(AllErr
));
137 void InProcessMemoryMapper::release(ArrayRef
<ExecutorAddr
> Bases
,
138 OnReleasedFunction OnReleased
) {
139 Error Err
= Error::success();
141 for (auto Base
: Bases
) {
142 std::vector
<ExecutorAddr
> AllocAddrs
;
145 std::lock_guard
<std::mutex
> Lock(Mutex
);
146 auto &R
= Reservations
[Base
.toPtr
<void *>()];
148 AllocAddrs
.swap(R
.Allocations
);
151 // deinitialize sub allocations
152 std::promise
<MSVCPError
> P
;
153 auto F
= P
.get_future();
154 deinitialize(AllocAddrs
, [&](Error Err
) { P
.set_value(std::move(Err
)); });
155 if (Error E
= F
.get()) {
156 Err
= joinErrors(std::move(Err
), std::move(E
));
160 auto MB
= sys::MemoryBlock(Base
.toPtr
<void *>(), Size
);
162 auto EC
= sys::Memory::releaseMappedMemory(MB
);
164 Err
= joinErrors(std::move(Err
), errorCodeToError(EC
));
167 std::lock_guard
<std::mutex
> Lock(Mutex
);
168 Reservations
.erase(Base
.toPtr
<void *>());
171 OnReleased(std::move(Err
));
174 InProcessMemoryMapper::~InProcessMemoryMapper() {
175 std::vector
<ExecutorAddr
> ReservationAddrs
;
177 std::lock_guard
<std::mutex
> Lock(Mutex
);
179 ReservationAddrs
.reserve(Reservations
.size());
180 for (const auto &R
: Reservations
) {
181 ReservationAddrs
.push_back(ExecutorAddr::fromPtr(R
.getFirst()));
185 std::promise
<MSVCPError
> P
;
186 auto F
= P
.get_future();
187 release(ReservationAddrs
, [&](Error Err
) { P
.set_value(std::move(Err
)); });
191 // SharedMemoryMapper
193 SharedMemoryMapper::SharedMemoryMapper(ExecutorProcessControl
&EPC
,
194 SymbolAddrs SAs
, size_t PageSize
)
195 : EPC(EPC
), SAs(SAs
), PageSize(PageSize
) {
196 #if (!defined(LLVM_ON_UNIX) || defined(__ANDROID__)) && !defined(_WIN32)
197 llvm_unreachable("SharedMemoryMapper is not supported on this platform yet");
201 Expected
<std::unique_ptr
<SharedMemoryMapper
>>
202 SharedMemoryMapper::Create(ExecutorProcessControl
&EPC
, SymbolAddrs SAs
) {
203 #if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
204 auto PageSize
= sys::Process::getPageSize();
206 return PageSize
.takeError();
208 return std::make_unique
<SharedMemoryMapper
>(EPC
, SAs
, *PageSize
);
210 return make_error
<StringError
>(
211 "SharedMemoryMapper is not supported on this platform yet",
212 inconvertibleErrorCode());
216 void SharedMemoryMapper::reserve(size_t NumBytes
,
217 OnReservedFunction OnReserved
) {
218 #if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
220 EPC
.callSPSWrapperAsync
<
221 rt::SPSExecutorSharedMemoryMapperServiceReserveSignature
>(
223 [this, NumBytes
, OnReserved
= std::move(OnReserved
)](
224 Error SerializationErr
,
225 Expected
<std::pair
<ExecutorAddr
, std::string
>> Result
) mutable {
226 if (SerializationErr
) {
227 cantFail(Result
.takeError());
228 return OnReserved(std::move(SerializationErr
));
232 return OnReserved(Result
.takeError());
234 ExecutorAddr RemoteAddr
;
235 std::string SharedMemoryName
;
236 std::tie(RemoteAddr
, SharedMemoryName
) = std::move(*Result
);
238 void *LocalAddr
= nullptr;
240 #if defined(LLVM_ON_UNIX)
242 int SharedMemoryFile
= shm_open(SharedMemoryName
.c_str(), O_RDWR
, 0700);
243 if (SharedMemoryFile
< 0) {
244 return OnReserved(errorCodeToError(
245 std::error_code(errno
, std::generic_category())));
248 // this prevents other processes from accessing it by name
249 shm_unlink(SharedMemoryName
.c_str());
251 LocalAddr
= mmap(nullptr, NumBytes
, PROT_READ
| PROT_WRITE
, MAP_SHARED
,
252 SharedMemoryFile
, 0);
253 if (LocalAddr
== MAP_FAILED
) {
254 return OnReserved(errorCodeToError(
255 std::error_code(errno
, std::generic_category())));
258 close(SharedMemoryFile
);
260 #elif defined(_WIN32)
262 std::wstring
WideSharedMemoryName(SharedMemoryName
.begin(),
263 SharedMemoryName
.end());
264 HANDLE SharedMemoryFile
= OpenFileMappingW(
265 FILE_MAP_ALL_ACCESS
, FALSE
, WideSharedMemoryName
.c_str());
266 if (!SharedMemoryFile
)
267 return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
270 MapViewOfFile(SharedMemoryFile
, FILE_MAP_ALL_ACCESS
, 0, 0, 0);
272 CloseHandle(SharedMemoryFile
);
273 return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
276 CloseHandle(SharedMemoryFile
);
280 std::lock_guard
<std::mutex
> Lock(Mutex
);
281 Reservations
.insert({RemoteAddr
, {LocalAddr
, NumBytes
}});
284 OnReserved(ExecutorAddrRange(RemoteAddr
, NumBytes
));
286 SAs
.Instance
, static_cast<uint64_t>(NumBytes
));
289 OnReserved(make_error
<StringError
>(
290 "SharedMemoryMapper is not supported on this platform yet",
291 inconvertibleErrorCode()));
295 char *SharedMemoryMapper::prepare(ExecutorAddr Addr
, size_t ContentSize
) {
296 auto R
= Reservations
.upper_bound(Addr
);
297 assert(R
!= Reservations
.begin() && "Attempt to prepare unreserved range");
300 ExecutorAddrDiff Offset
= Addr
- R
->first
;
302 return static_cast<char *>(R
->second
.LocalAddr
) + Offset
;
305 void SharedMemoryMapper::initialize(MemoryMapper::AllocInfo
&AI
,
306 OnInitializedFunction OnInitialized
) {
307 auto Reservation
= Reservations
.upper_bound(AI
.MappingBase
);
308 assert(Reservation
!= Reservations
.begin() && "Attempt to initialize unreserved range");
311 auto AllocationOffset
= AI
.MappingBase
- Reservation
->first
;
313 tpctypes::SharedMemoryFinalizeRequest FR
;
315 AI
.Actions
.swap(FR
.Actions
);
317 FR
.Segments
.reserve(AI
.Segments
.size());
319 for (auto Segment
: AI
.Segments
) {
320 char *Base
= static_cast<char *>(Reservation
->second
.LocalAddr
) +
321 AllocationOffset
+ Segment
.Offset
;
322 std::memset(Base
+ Segment
.ContentSize
, 0, Segment
.ZeroFillSize
);
324 tpctypes::SharedMemorySegFinalizeRequest SegReq
;
325 SegReq
.RAG
= {Segment
.AG
.getMemProt(),
326 Segment
.AG
.getMemLifetime() == MemLifetime::Finalize
};
327 SegReq
.Addr
= AI
.MappingBase
+ Segment
.Offset
;
328 SegReq
.Size
= Segment
.ContentSize
+ Segment
.ZeroFillSize
;
330 FR
.Segments
.push_back(SegReq
);
333 EPC
.callSPSWrapperAsync
<
334 rt::SPSExecutorSharedMemoryMapperServiceInitializeSignature
>(
336 [OnInitialized
= std::move(OnInitialized
)](
337 Error SerializationErr
, Expected
<ExecutorAddr
> Result
) mutable {
338 if (SerializationErr
) {
339 cantFail(Result
.takeError());
340 return OnInitialized(std::move(SerializationErr
));
343 OnInitialized(std::move(Result
));
345 SAs
.Instance
, Reservation
->first
, std::move(FR
));
348 void SharedMemoryMapper::deinitialize(
349 ArrayRef
<ExecutorAddr
> Allocations
,
350 MemoryMapper::OnDeinitializedFunction OnDeinitialized
) {
351 EPC
.callSPSWrapperAsync
<
352 rt::SPSExecutorSharedMemoryMapperServiceDeinitializeSignature
>(
354 [OnDeinitialized
= std::move(OnDeinitialized
)](Error SerializationErr
,
355 Error Result
) mutable {
356 if (SerializationErr
) {
357 cantFail(std::move(Result
));
358 return OnDeinitialized(std::move(SerializationErr
));
361 OnDeinitialized(std::move(Result
));
363 SAs
.Instance
, Allocations
);
366 void SharedMemoryMapper::release(ArrayRef
<ExecutorAddr
> Bases
,
367 OnReleasedFunction OnReleased
) {
368 #if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
369 Error Err
= Error::success();
372 std::lock_guard
<std::mutex
> Lock(Mutex
);
374 for (auto Base
: Bases
) {
376 #if defined(LLVM_ON_UNIX)
378 if (munmap(Reservations
[Base
].LocalAddr
, Reservations
[Base
].Size
) != 0)
379 Err
= joinErrors(std::move(Err
), errorCodeToError(std::error_code(
380 errno
, std::generic_category())));
382 #elif defined(_WIN32)
384 if (!UnmapViewOfFile(Reservations
[Base
].LocalAddr
))
385 Err
= joinErrors(std::move(Err
),
386 errorCodeToError(mapWindowsError(GetLastError())));
390 Reservations
.erase(Base
);
394 EPC
.callSPSWrapperAsync
<
395 rt::SPSExecutorSharedMemoryMapperServiceReleaseSignature
>(
397 [OnReleased
= std::move(OnReleased
),
398 Err
= std::move(Err
)](Error SerializationErr
, Error Result
) mutable {
399 if (SerializationErr
) {
400 cantFail(std::move(Result
));
402 joinErrors(std::move(Err
), std::move(SerializationErr
)));
405 return OnReleased(joinErrors(std::move(Err
), std::move(Result
)));
407 SAs
.Instance
, Bases
);
409 OnReleased(make_error
<StringError
>(
410 "SharedMemoryMapper is not supported on this platform yet",
411 inconvertibleErrorCode()));
415 SharedMemoryMapper::~SharedMemoryMapper() {
416 std::lock_guard
<std::mutex
> Lock(Mutex
);
417 for (const auto &R
: Reservations
) {
419 #if defined(LLVM_ON_UNIX) && !defined(__ANDROID__)
421 munmap(R
.second
.LocalAddr
, R
.second
.Size
);
423 #elif defined(_WIN32)
425 UnmapViewOfFile(R
.second
.LocalAddr
);