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