LLVM 24.0.0git
MemoryMapper.cpp
Go to the documentation of this file.
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
10
11#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX
16
17#if defined(LLVM_ON_UNIX) && !defined(__ANDROID__)
18#include <fcntl.h>
19#include <sys/mman.h>
20#if defined(__MVS__)
21#include "llvm/Support/BLAKE3.h"
22#include <sys/shm.h>
23#endif
24#include <unistd.h>
25#elif defined(_WIN32)
26#include <windows.h>
27#endif
28
29namespace llvm {
30namespace orc {
31
33
35 : PageSize(PageSize) {}
36
39 auto PageSize = sys::Process::getPageSize();
40 if (!PageSize)
41 return PageSize.takeError();
42 return std::make_unique<InProcessMemoryMapper>(*PageSize);
43}
44
45void InProcessMemoryMapper::reserve(size_t NumBytes,
46 OnReservedFunction OnReserved) {
47 std::error_code EC;
49 NumBytes, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
50
51 if (EC)
52 return OnReserved(errorCodeToError(EC));
53
54 {
55 std::lock_guard<std::mutex> Lock(Mutex);
56 Reservations[MB.base()].Size = MB.allocatedSize();
57 }
58
59 OnReserved(
60 ExecutorAddrRange(ExecutorAddr::fromPtr(MB.base()), MB.allocatedSize()));
61}
62
64 size_t ContentSize) {
65 return Addr.toPtr<char *>();
66}
67
69 OnInitializedFunction OnInitialized) {
70 ExecutorAddr MinAddr(~0ULL);
71 ExecutorAddr MaxAddr(0);
72
73 // FIXME: Release finalize lifetime segments.
74 for (auto &Segment : AI.Segments) {
75 auto Base = AI.MappingBase + Segment.Offset;
76 auto Size = Segment.ContentSize + Segment.ZeroFillSize;
77
78 if (Base < MinAddr)
79 MinAddr = Base;
80
81 if (Base + Size > MaxAddr)
82 MaxAddr = Base + Size;
83
84 std::memset((Base + Segment.ContentSize).toPtr<void *>(), 0,
85 Segment.ZeroFillSize);
86
88 {Base.toPtr<void *>(), Size},
89 toSysMemoryProtectionFlags(Segment.AG.getMemProt()))) {
90 return OnInitialized(errorCodeToError(EC));
91 }
92 if ((Segment.AG.getMemProt() & MemProt::Exec) == MemProt::Exec)
94 }
95
96 auto DeinitializeActions = shared::runFinalizeActions(AI.Actions);
97 if (!DeinitializeActions)
98 return OnInitialized(DeinitializeActions.takeError());
99
100 {
101 std::lock_guard<std::mutex> Lock(Mutex);
102
103 // This is the maximum range whose permission have been possibly modified
104 auto &Alloc = Allocations[MinAddr];
105 Alloc.Size = MaxAddr - MinAddr;
106 Alloc.DeinitializationActions = std::move(*DeinitializeActions);
107 Reservations[AI.MappingBase.toPtr<void *>()].Allocations.push_back(MinAddr);
108 }
109
110 OnInitialized(MinAddr);
111}
112
116 Error AllErr = Error::success();
117
118 {
119 std::lock_guard<std::mutex> Lock(Mutex);
120
121 for (auto Base : llvm::reverse(Bases)) {
122
124 Allocations[Base].DeinitializationActions)) {
125 AllErr = joinErrors(std::move(AllErr), std::move(Err));
126 }
127
128 // Reset protections to read/write so the area can be reused
130 {Base.toPtr<void *>(), Allocations[Base].Size},
133 AllErr = joinErrors(std::move(AllErr), errorCodeToError(EC));
134 }
135
136 Allocations.erase(Base);
137 }
138 }
139
140 OnDeinitialized(std::move(AllErr));
141}
142
144 OnReleasedFunction OnReleased) {
145 Error Err = Error::success();
146
147 for (auto Base : Bases) {
148 std::vector<ExecutorAddr> AllocAddrs;
149 size_t Size;
150 {
151 std::lock_guard<std::mutex> Lock(Mutex);
152 auto &R = Reservations[Base.toPtr<void *>()];
153 Size = R.Size;
154 AllocAddrs.swap(R.Allocations);
155 }
156
157 // deinitialize sub allocations
158 std::promise<MSVCPError> P;
159 auto F = P.get_future();
160 deinitialize(AllocAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
161 if (Error E = F.get()) {
162 Err = joinErrors(std::move(Err), std::move(E));
163 }
164
165 // free the memory
166 auto MB = sys::MemoryBlock(Base.toPtr<void *>(), Size);
167
169 if (EC) {
170 Err = joinErrors(std::move(Err), errorCodeToError(EC));
171 }
172
173 std::lock_guard<std::mutex> Lock(Mutex);
174 Reservations.erase(Base.toPtr<void *>());
175 }
176
177 OnReleased(std::move(Err));
178}
179
181 std::vector<ExecutorAddr> ReservationAddrs;
182 {
183 std::lock_guard<std::mutex> Lock(Mutex);
184
185 ReservationAddrs.reserve(Reservations.size());
186 for (const auto &R : Reservations) {
187 ReservationAddrs.push_back(ExecutorAddr::fromPtr(R.getFirst()));
188 }
189 }
190
191 std::promise<MSVCPError> P;
192 auto F = P.get_future();
193 release(ReservationAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
194 cantFail(F.get());
195}
196
197// SharedMemoryMapper
198
200 SymbolAddrs SAs, size_t PageSize)
201 : EPC(EPC), SAs(SAs), PageSize(PageSize) {
202#if (!defined(LLVM_ON_UNIX) || defined(__ANDROID__)) && !defined(_WIN32)
203 llvm_unreachable("SharedMemoryMapper is not supported on this platform yet");
204#endif
205}
206
209#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
210 auto PageSize = sys::Process::getPageSize();
211 if (!PageSize)
212 return PageSize.takeError();
213
214 return std::make_unique<SharedMemoryMapper>(EPC, SAs, *PageSize);
215#else
217 "SharedMemoryMapper is not supported on this platform yet",
219#endif
220}
221
222void SharedMemoryMapper::reserve(size_t NumBytes,
223 OnReservedFunction OnReserved) {
224#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
225
226 int SharedMemoryId = -1;
227 EPC.callSPSWrapperAsync<rt::sps_ci::SharedMemoryMapperReserve::SPSSig>(
228 SAs.Reserve,
229 [this, NumBytes, OnReserved = std::move(OnReserved), SharedMemoryId](
230 Error SerializationErr,
232 if (SerializationErr) {
233 cantFail(Result.takeError());
234 return OnReserved(std::move(SerializationErr));
235 }
236
237 if (!Result)
238 return OnReserved(Result.takeError());
239
240 ExecutorAddr RemoteAddr;
241 std::string SharedMemoryName;
242 std::tie(RemoteAddr, SharedMemoryName) = std::move(*Result);
243
244 void *LocalAddr = nullptr;
245
246#if defined(LLVM_ON_UNIX)
247
248#if defined(__MVS__)
250 reinterpret_cast<const uint8_t *>(SharedMemoryName.c_str()),
251 SharedMemoryName.size());
252 auto HashedName = BLAKE3::hash<sizeof(key_t)>(Data);
253 key_t Key = *reinterpret_cast<key_t *>(HashedName.data());
254 SharedMemoryId =
255 shmget(Key, NumBytes, IPC_CREAT | __IPC_SHAREAS | 0700);
256 if (SharedMemoryId < 0) {
257 return OnReserved(errorCodeToError(
258 std::error_code(errno, std::generic_category())));
259 }
260 LocalAddr = shmat(SharedMemoryId, nullptr, 0);
261 if (LocalAddr == reinterpret_cast<void *>(-1)) {
262 return OnReserved(errorCodeToError(
263 std::error_code(errno, std::generic_category())));
264 }
265#else
266 int SharedMemoryFile = shm_open(SharedMemoryName.c_str(), O_RDWR, 0700);
267 if (SharedMemoryFile < 0) {
268 return OnReserved(errorCodeToError(errnoAsErrorCode()));
269 }
270
271 // this prevents other processes from accessing it by name
272 shm_unlink(SharedMemoryName.c_str());
273
274 LocalAddr = mmap(nullptr, NumBytes, PROT_READ | PROT_WRITE, MAP_SHARED,
275 SharedMemoryFile, 0);
276 if (LocalAddr == MAP_FAILED) {
277 return OnReserved(errorCodeToError(errnoAsErrorCode()));
278 }
279
280 close(SharedMemoryFile);
281#endif
282
283#elif defined(_WIN32)
284
285 std::wstring WideSharedMemoryName(SharedMemoryName.begin(),
286 SharedMemoryName.end());
287 HANDLE SharedMemoryFile = OpenFileMappingW(
288 FILE_MAP_ALL_ACCESS, FALSE, WideSharedMemoryName.c_str());
289 if (!SharedMemoryFile)
290 return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
291
292 LocalAddr =
293 MapViewOfFile(SharedMemoryFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
294 if (!LocalAddr) {
295 CloseHandle(SharedMemoryFile);
296 return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
297 }
298
299 CloseHandle(SharedMemoryFile);
300
301#endif
302 {
303 std::lock_guard<std::mutex> Lock(Mutex);
304 Reservations.insert(
305 {RemoteAddr, {LocalAddr, NumBytes, SharedMemoryId}});
306 }
307
308 OnReserved(ExecutorAddrRange(RemoteAddr, NumBytes));
309 },
310 SAs.Instance, static_cast<uint64_t>(NumBytes));
311
312#else
313 OnReserved(make_error<StringError>(
314 "SharedMemoryMapper is not supported on this platform yet",
316#endif
317}
318
320 size_t ContentSize) {
321 auto R = Reservations.upper_bound(Addr);
322 assert(R != Reservations.begin() && "Attempt to prepare unreserved range");
323 R--;
324
325 ExecutorAddrDiff Offset = Addr - R->first;
326
327 return static_cast<char *>(R->second.LocalAddr) + Offset;
328}
329
331 OnInitializedFunction OnInitialized) {
332 auto Reservation = Reservations.upper_bound(AI.MappingBase);
333 assert(Reservation != Reservations.begin() && "Attempt to initialize unreserved range");
334 Reservation--;
335
336 auto AllocationOffset = AI.MappingBase - Reservation->first;
337
339
340 AI.Actions.swap(FR.Actions);
341
342 FR.Segments.reserve(AI.Segments.size());
343
344 for (auto Segment : AI.Segments) {
345 char *Base = static_cast<char *>(Reservation->second.LocalAddr) +
346 AllocationOffset + Segment.Offset;
347 std::memset(Base + Segment.ContentSize, 0, Segment.ZeroFillSize);
348
350 SegReq.RAG = {Segment.AG.getMemProt(),
351 Segment.AG.getMemLifetime() == MemLifetime::Finalize};
352 SegReq.Addr = AI.MappingBase + Segment.Offset;
353 SegReq.Size = Segment.ContentSize + Segment.ZeroFillSize;
354
355 FR.Segments.push_back(SegReq);
356 }
357
359 SAs.Initialize,
360 [OnInitialized = std::move(OnInitialized)](
361 Error SerializationErr, Expected<ExecutorAddr> Result) mutable {
362 if (SerializationErr) {
363 cantFail(Result.takeError());
364 return OnInitialized(std::move(SerializationErr));
365 }
366
367 OnInitialized(std::move(Result));
368 },
369 SAs.Instance, Reservation->first, std::move(FR));
370}
371
373 ArrayRef<ExecutorAddr> Allocations,
376 SAs.Deinitialize,
377 [OnDeinitialized = std::move(OnDeinitialized)](Error SerializationErr,
378 Error Result) mutable {
379 if (SerializationErr) {
380 cantFail(std::move(Result));
381 return OnDeinitialized(std::move(SerializationErr));
382 }
383
384 OnDeinitialized(std::move(Result));
385 },
386 SAs.Instance, Allocations);
387}
388
390 OnReleasedFunction OnReleased) {
391#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
392 Error Err = Error::success();
393
394 {
395 std::lock_guard<std::mutex> Lock(Mutex);
396
397 for (auto Base : Bases) {
398
399#if defined(LLVM_ON_UNIX)
400
401#if defined(__MVS__)
402 if (shmdt(Reservations[Base].LocalAddr) < 0 ||
403 shmctl(Reservations[Base].SharedMemoryId, IPC_RMID, NULL) < 0)
404 Err = joinErrors(std::move(Err), errorCodeToError(errnoAsErrorCode()));
405#else
406 if (munmap(Reservations[Base].LocalAddr, Reservations[Base].Size) != 0)
407 Err = joinErrors(std::move(Err), errorCodeToError(errnoAsErrorCode()));
408#endif
409
410#elif defined(_WIN32)
411
412 if (!UnmapViewOfFile(Reservations[Base].LocalAddr))
413 Err = joinErrors(std::move(Err),
414 errorCodeToError(mapWindowsError(GetLastError())));
415
416#endif
417
418 Reservations.erase(Base);
419 }
420 }
421
422 EPC.callSPSWrapperAsync<rt::sps_ci::SharedMemoryMapperRelease::SPSSig>(
423 SAs.Release,
424 [OnReleased = std::move(OnReleased),
425 Err = std::move(Err)](Error SerializationErr, Error Result) mutable {
426 if (SerializationErr) {
427 cantFail(std::move(Result));
428 return OnReleased(
429 joinErrors(std::move(Err), std::move(SerializationErr)));
430 }
431
432 return OnReleased(joinErrors(std::move(Err), std::move(Result)));
433 },
434 SAs.Instance, Bases);
435#else
436 OnReleased(make_error<StringError>(
437 "SharedMemoryMapper is not supported on this platform yet",
439#endif
440}
441
443 std::lock_guard<std::mutex> Lock(Mutex);
444 for (const auto &R : Reservations) {
445
446#if defined(LLVM_ON_UNIX) && !defined(__ANDROID__)
447
448#if defined(__MVS__)
449 shmdt(R.second.LocalAddr);
450#else
451 munmap(R.second.LocalAddr, R.second.Size);
452#endif
453
454#elif defined(_WIN32)
455
456 UnmapViewOfFile(R.second.LocalAddr);
457
458#else
459
460 (void)R;
461
462#endif
463 }
464}
465
466} // namespace orc
467
468} // namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define F(x, y, z)
Definition MD5.cpp:54
#define G(x, y, z)
Definition MD5.cpp:55
#define P(N)
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
static BLAKE3Result< NumBytes > hash(ArrayRef< uint8_t > Data)
Returns a BLAKE3 hash for the given data.
Definition BLAKE3.h:92
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Represents an address in the executor process.
static ExecutorAddr fromPtr(T *Ptr, UnwrapFn &&Unwrap=UnwrapFn())
Create an ExecutorAddr from the given pointer.
std::enable_if_t< std::is_pointer< T >::value, T > toPtr(WrapFn &&Wrap=WrapFn()) const
Cast this ExecutorAddr to a pointer of the given type.
ExecutorProcessControl supports interaction with a JIT target process.
void initialize(AllocInfo &AI, OnInitializedFunction OnInitialized) override
Ensures executor memory is synchronized with working copy memory, sends functions to be called after ...
void reserve(size_t NumBytes, OnReservedFunction OnReserved) override
Reserves address space in executor process.
char * prepare(jitlink::LinkGraph &G, ExecutorAddr Addr, size_t ContentSize) override
Provides working memory The LinkGraph parameter is included to allow implementations to allocate work...
void deinitialize(ArrayRef< ExecutorAddr > Allocations, OnDeinitializedFunction OnDeInitialized) override
Runs previously specified deinitialization actions Executor addresses returned by initialize should b...
static Expected< std::unique_ptr< InProcessMemoryMapper > > Create()
void release(ArrayRef< ExecutorAddr > Reservations, OnReleasedFunction OnRelease) override
Release address space acquired through reserve()
unique_function< void(Error)> OnReleasedFunction
unique_function< void(Expected< ExecutorAddr >)> OnInitializedFunction
unique_function< void(Expected< ExecutorAddrRange >)> OnReservedFunction
unique_function< void(Error)> OnDeinitializedFunction
static Expected< std::unique_ptr< SharedMemoryMapper > > Create(ExecutorProcessControl &EPC, SymbolAddrs SAs)
char * prepare(jitlink::LinkGraph &G, ExecutorAddr Addr, size_t ContentSize) override
Provides working memory The LinkGraph parameter is included to allow implementations to allocate work...
void reserve(size_t NumBytes, OnReservedFunction OnReserved) override
Reserves address space in executor process.
void deinitialize(ArrayRef< ExecutorAddr > Allocations, OnDeinitializedFunction OnDeInitialized) override
Runs previously specified deinitialization actions Executor addresses returned by initialize should b...
void initialize(AllocInfo &AI, OnInitializedFunction OnInitialized) override
Ensures executor memory is synchronized with working copy memory, sends functions to be called after ...
void release(ArrayRef< ExecutorAddr > Reservations, OnReleasedFunction OnRelease) override
Release address space acquired through reserve()
SharedMemoryMapper(ExecutorProcessControl &EPC, SymbolAddrs SAs, size_t PageSize)
This class encapsulates the notion of a memory block which has an address and a size.
Definition Memory.h:33
static LLVM_ABI std::error_code protectMappedMemory(const MemoryBlock &Block, unsigned Flags)
This method sets the protection flags for a block of memory to the state specified by /p Flags.
static LLVM_ABI std::error_code releaseMappedMemory(MemoryBlock &Block)
This method releases a block of memory that was allocated with the allocateMappedMemory method.
static LLVM_ABI void InvalidateInstructionCache(const void *Addr, size_t Len)
InvalidateInstructionCache - Before the JIT can run a block of code that has been emitted it must inv...
static LLVM_ABI MemoryBlock allocateMappedMemory(size_t NumBytes, const MemoryBlock *const NearBlock, unsigned Flags, std::error_code &EC)
This method allocates a block of memory that is suitable for loading dynamically generated code (e....
static LLVM_ABI Expected< unsigned > getPageSize()
Get the process's page size.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
LLVM_ABI Error runDeallocActions(ArrayRef< WrapperFunctionCall > DAs)
Run deallocation actions.
LLVM_ABI Expected< std::vector< WrapperFunctionCall > > runFinalizeActions(AllocActions &AAs)
Run finalize actions.
uint64_t ExecutorAddrDiff
@ Finalize
Finalize memory should be allocated by the allocator, and then be overwritten and deallocated after a...
Definition MemoryFlags.h:83
sys::Memory::ProtectionFlags toSysMemoryProtectionFlags(MemProt MP)
Convert a MemProt value to a corresponding sys::Memory::ProtectionFlags value.
Definition MemoryFlags.h:44
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:577
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition Error.h:442
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
LLVM_ABI Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition Error.cpp:107
LLVM_ABI std::error_code mapWindowsError(unsigned EV)
std::error_code errnoAsErrorCode()
Helper to get errno as an std::error_code.
Definition Error.h:1256
Represents an address range in the exceutor process.
Represents a single allocation containing multiple segments and initialization and deinitialization a...
std::vector< SegInfo > Segments
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSSig
shared::SPSExpected< shared::SPSExecutorAddr >( shared::SPSExecutorAddr, shared::SPSExecutorAddr, shared::SPSSharedMemoryFinalizeRequest) SPSSig
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSSig
shared::SPSExpected< shared::SPSTuple< shared::SPSExecutorAddr, shared::SPSString > >( shared::SPSExecutorAddr, uint64_t) SPSSig
std::vector< SharedMemorySegFinalizeRequest > Segments