LLVM 19.0.0git
ExecutorSharedMemoryMapperService.cpp
Go to the documentation of this file.
1//===---------- ExecutorSharedMemoryMapperService.cpp -----------*- 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
14
15#include <sstream>
16
17#if defined(LLVM_ON_UNIX)
18#include <errno.h>
19#include <fcntl.h>
20#include <sys/mman.h>
21#if defined(__MVS__)
22#include "llvm/Support/BLAKE3.h"
23#include <sys/shm.h>
24#endif
25#include <unistd.h>
26#endif
27
28namespace llvm {
29namespace orc {
30namespace rt_bootstrap {
31
32#if defined(_WIN32)
33static DWORD getWindowsProtectionFlags(MemProt MP) {
34 if (MP == MemProt::Read)
35 return PAGE_READONLY;
36 if (MP == MemProt::Write ||
37 MP == (MemProt::Write | MemProt::Read)) {
38 // Note: PAGE_WRITE is not supported by VirtualProtect
39 return PAGE_READWRITE;
40 }
41 if (MP == (MemProt::Read | MemProt::Exec))
42 return PAGE_EXECUTE_READ;
44 return PAGE_EXECUTE_READWRITE;
45 if (MP == MemProt::Exec)
46 return PAGE_EXECUTE;
47
48 return PAGE_NOACCESS;
49}
50#endif
51
52Expected<std::pair<ExecutorAddr, std::string>>
54#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
55
56#if defined(LLVM_ON_UNIX)
57
58 std::string SharedMemoryName;
59 {
60 std::stringstream SharedMemoryNameStream;
61 SharedMemoryNameStream << "/jitlink_" << sys::Process::getProcessId() << '_'
62 << (++SharedMemoryCount);
63 SharedMemoryName = SharedMemoryNameStream.str();
64 }
65
66#if defined(__MVS__)
68 reinterpret_cast<const uint8_t *>(SharedMemoryName.c_str()),
69 SharedMemoryName.size());
70 auto HashedName = BLAKE3::hash<sizeof(key_t)>(Data);
71 key_t Key = *reinterpret_cast<key_t *>(HashedName.data());
72 int SharedMemoryId =
73 shmget(Key, Size, IPC_CREAT | IPC_EXCL | __IPC_SHAREAS | 0700);
74 if (SharedMemoryId < 0)
76
77 void *Addr = shmat(SharedMemoryId, nullptr, 0);
78 if (Addr == reinterpret_cast<void *>(-1))
80#else
81 int SharedMemoryFile =
82 shm_open(SharedMemoryName.c_str(), O_RDWR | O_CREAT | O_EXCL, 0700);
83 if (SharedMemoryFile < 0)
85
86 // by default size is 0
87 if (ftruncate(SharedMemoryFile, Size) < 0)
89
90 void *Addr = mmap(nullptr, Size, PROT_NONE, MAP_SHARED, SharedMemoryFile, 0);
91 if (Addr == MAP_FAILED)
93
94 close(SharedMemoryFile);
95#endif
96
97#elif defined(_WIN32)
98
99 std::string SharedMemoryName;
100 {
101 std::stringstream SharedMemoryNameStream;
102 SharedMemoryNameStream << "jitlink_" << sys::Process::getProcessId() << '_'
103 << (++SharedMemoryCount);
104 SharedMemoryName = SharedMemoryNameStream.str();
105 }
106
107 std::wstring WideSharedMemoryName(SharedMemoryName.begin(),
108 SharedMemoryName.end());
109 HANDLE SharedMemoryFile = CreateFileMappingW(
110 INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, Size >> 32,
111 Size & 0xffffffff, WideSharedMemoryName.c_str());
112 if (!SharedMemoryFile)
113 return errorCodeToError(mapWindowsError(GetLastError()));
114
115 void *Addr = MapViewOfFile(SharedMemoryFile,
116 FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0);
117 if (!Addr) {
118 CloseHandle(SharedMemoryFile);
119 return errorCodeToError(mapWindowsError(GetLastError()));
120 }
121
122#endif
123
124 {
125 std::lock_guard<std::mutex> Lock(Mutex);
126 Reservations[Addr].Size = Size;
127#if defined(_WIN32)
128 Reservations[Addr].SharedMemoryFile = SharedMemoryFile;
129#endif
130 }
131
132 return std::make_pair(ExecutorAddr::fromPtr(Addr),
133 std::move(SharedMemoryName));
134#else
135 return make_error<StringError>(
136 "SharedMemoryMapper is not supported on this platform yet",
138#endif
139}
140
143#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
144
145 ExecutorAddr MinAddr(~0ULL);
146
147 // Contents are already in place
148 for (auto &Segment : FR.Segments) {
149 if (Segment.Addr < MinAddr)
150 MinAddr = Segment.Addr;
151
152#if defined(LLVM_ON_UNIX)
153
154#if defined(__MVS__)
155 // TODO Is it possible to change the protection level?
156#else
157 int NativeProt = 0;
158 if ((Segment.RAG.Prot & MemProt::Read) == MemProt::Read)
159 NativeProt |= PROT_READ;
160 if ((Segment.RAG.Prot & MemProt::Write) == MemProt::Write)
161 NativeProt |= PROT_WRITE;
162 if ((Segment.RAG.Prot & MemProt::Exec) == MemProt::Exec)
163 NativeProt |= PROT_EXEC;
164
165 if (mprotect(Segment.Addr.toPtr<void *>(), Segment.Size, NativeProt))
167#endif
168
169#elif defined(_WIN32)
170
171 DWORD NativeProt = getWindowsProtectionFlags(Segment.RAG.Prot);
172
173 if (!VirtualProtect(Segment.Addr.toPtr<void *>(), Segment.Size, NativeProt,
174 &NativeProt))
175 return errorCodeToError(mapWindowsError(GetLastError()));
176
177#endif
178
179 if ((Segment.RAG.Prot & MemProt::Exec) == MemProt::Exec)
180 sys::Memory::InvalidateInstructionCache(Segment.Addr.toPtr<void *>(),
181 Segment.Size);
182 }
183
184 // Run finalization actions and get deinitlization action list.
185 auto DeinitializeActions = shared::runFinalizeActions(FR.Actions);
186 if (!DeinitializeActions) {
187 return DeinitializeActions.takeError();
188 }
189
190 {
191 std::lock_guard<std::mutex> Lock(Mutex);
192 Allocations[MinAddr].DeinitializationActions =
193 std::move(*DeinitializeActions);
194 Reservations[Reservation.toPtr<void *>()].Allocations.push_back(MinAddr);
195 }
196
197 return MinAddr;
198
199#else
200 return make_error<StringError>(
201 "SharedMemoryMapper is not supported on this platform yet",
203#endif
204}
205
207 const std::vector<ExecutorAddr> &Bases) {
208 Error AllErr = Error::success();
209
210 {
211 std::lock_guard<std::mutex> Lock(Mutex);
212
213 for (auto Base : llvm::reverse(Bases)) {
215 Allocations[Base].DeinitializationActions)) {
216 AllErr = joinErrors(std::move(AllErr), std::move(Err));
217 }
218
219 // Remove the allocation from the allocation list of its reservation
220 for (auto &Reservation : Reservations) {
221 auto AllocationIt = llvm::find(Reservation.second.Allocations, Base);
222 if (AllocationIt != Reservation.second.Allocations.end()) {
223 Reservation.second.Allocations.erase(AllocationIt);
224 break;
225 }
226 }
227
228 Allocations.erase(Base);
229 }
230 }
231
232 return AllErr;
233}
234
236 const std::vector<ExecutorAddr> &Bases) {
237#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
238 Error Err = Error::success();
239
240 for (auto Base : Bases) {
241 std::vector<ExecutorAddr> AllocAddrs;
242 size_t Size;
243
244#if defined(_WIN32)
245 HANDLE SharedMemoryFile;
246#endif
247
248 {
249 std::lock_guard<std::mutex> Lock(Mutex);
250 auto &R = Reservations[Base.toPtr<void *>()];
251 Size = R.Size;
252
253#if defined(_WIN32)
254 SharedMemoryFile = R.SharedMemoryFile;
255#endif
256
257 AllocAddrs.swap(R.Allocations);
258 }
259
260 // deinitialize sub allocations
261 if (Error E = deinitialize(AllocAddrs))
262 Err = joinErrors(std::move(Err), std::move(E));
263
264#if defined(LLVM_ON_UNIX)
265
266#if defined(__MVS__)
267 (void)Size;
268
269 if (shmdt(Base.toPtr<void *>()) < 0)
270 Err = joinErrors(std::move(Err), errorCodeToError(errnoAsErrorCode()));
271#else
272 if (munmap(Base.toPtr<void *>(), Size) != 0)
273 Err = joinErrors(std::move(Err), errorCodeToError(errnoAsErrorCode()));
274#endif
275
276#elif defined(_WIN32)
277 (void)Size;
278
279 if (!UnmapViewOfFile(Base.toPtr<void *>()))
280 Err = joinErrors(std::move(Err),
281 errorCodeToError(mapWindowsError(GetLastError())));
282
283 CloseHandle(SharedMemoryFile);
284
285#endif
286
287 std::lock_guard<std::mutex> Lock(Mutex);
288 Reservations.erase(Base.toPtr<void *>());
289 }
290
291 return Err;
292#else
293 return make_error<StringError>(
294 "SharedMemoryMapper is not supported on this platform yet",
296#endif
297}
298
300 if (Reservations.empty())
301 return Error::success();
302
303 std::vector<ExecutorAddr> ReservationAddrs;
304 ReservationAddrs.reserve(Reservations.size());
305 for (const auto &R : Reservations)
306 ReservationAddrs.push_back(ExecutorAddr::fromPtr(R.getFirst()));
307
308 return release(std::move(ReservationAddrs));
309}
310
316 ExecutorAddr::fromPtr(&reserveWrapper);
318 ExecutorAddr::fromPtr(&initializeWrapper);
320 ExecutorAddr::fromPtr(&deinitializeWrapper);
322 ExecutorAddr::fromPtr(&releaseWrapper);
323}
324
326ExecutorSharedMemoryMapperService::reserveWrapper(const char *ArgData,
327 size_t ArgSize) {
330 handle(ArgData, ArgSize,
333 .release();
334}
335
337ExecutorSharedMemoryMapperService::initializeWrapper(const char *ArgData,
338 size_t ArgSize) {
341 handle(ArgData, ArgSize,
344 .release();
345}
346
348ExecutorSharedMemoryMapperService::deinitializeWrapper(const char *ArgData,
349 size_t ArgSize) {
350 return shared::WrapperFunction<
352 handle(ArgData, ArgSize,
355 .release();
356}
357
359ExecutorSharedMemoryMapperService::releaseWrapper(const char *ArgData,
360 size_t ArgSize) {
361 return shared::WrapperFunction<
363 handle(ArgData, ArgSize,
366 .release();
367}
368
369} // namespace rt_bootstrap
370} // end namespace orc
371} // end namespace llvm
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
uint64_t Addr
uint64_t Size
Provides a library for accessing information about this process and other processes on the operating ...
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
bool erase(const KeyT &Val)
Definition: DenseMap.h:329
unsigned size() const
Definition: DenseMap.h:99
bool empty() const
Definition: DenseMap.h:98
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:334
Tagged union holding either a T or a Error.
Definition: Error.h:474
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition: StringMap.h:127
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.
Expected< std::pair< ExecutorAddr, std::string > > reserve(uint64_t Size)
Expected< ExecutorAddr > initialize(ExecutorAddr Reservation, tpctypes::SharedMemoryFinalizeRequest &FR)
static 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 Pid getProcessId()
Get the process's identifier.
shared::SPSExpected< shared::SPSExecutorAddr >(shared::SPSExecutorAddr, shared::SPSExecutorAddr, shared::SPSSharedMemoryFinalizeRequest) SPSExecutorSharedMemoryMapperServiceInitializeSignature
Definition: OrcRTBridge.h:78
const char * ExecutorSharedMemoryMapperServiceInstanceName
Definition: OrcRTBridge.cpp:31
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSExecutorSharedMemoryMapperServiceReleaseSignature
Definition: OrcRTBridge.h:83
const char * ExecutorSharedMemoryMapperServiceReserveWrapperName
Definition: OrcRTBridge.cpp:33
shared::SPSExpected< shared::SPSTuple< shared::SPSExecutorAddr, shared::SPSString > >(shared::SPSExecutorAddr, uint64_t) SPSExecutorSharedMemoryMapperServiceReserveSignature
Definition: OrcRTBridge.h:74
const char * ExecutorSharedMemoryMapperServiceDeinitializeWrapperName
Definition: OrcRTBridge.cpp:37
const char * ExecutorSharedMemoryMapperServiceReleaseWrapperName
Definition: OrcRTBridge.cpp:39
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSExecutorSharedMemoryMapperServiceDeinitializeSignature
Definition: OrcRTBridge.h:81
const char * ExecutorSharedMemoryMapperServiceInitializeWrapperName
Definition: OrcRTBridge.cpp:35
MethodWrapperHandler< RetT, ClassT, ArgTs... > makeMethodWrapperHandler(RetT(ClassT::*Method)(ArgTs...))
Create a MethodWrapperHandler object from the given method pointer.
Error runDeallocActions(ArrayRef< WrapperFunctionCall > DAs)
Run deallocation actions.
Expected< std::vector< WrapperFunctionCall > > runFinalizeActions(AllocActions &AAs)
Run finalize actions.
MemProt
Describes Read/Write/Exec permissions for memory.
Definition: MemoryFlags.h:27
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
auto find(R &&Range, const T &Val)
Provide wrappers to std::find which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1742
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:90
auto reverse(ContainerTy &&C)
Definition: STLExtras.h:419
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition: Error.h:431
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition: Error.cpp:103
std::error_code errnoAsErrorCode()
Helper to get errno as an std::error_code.
Definition: Error.h:1193
std::error_code mapWindowsError(unsigned EV)
std::vector< SharedMemorySegFinalizeRequest > Segments