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