1 //===- SimpleExecuorMemoryManagare.cpp - Simple executor-side memory mgmt -===// 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/TargetProcess/SimpleExecutorMemoryManager.h" 10 11 #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" 12 #include "llvm/Support/FormatVariadic.h" 13 14 #define DEBUG_TYPE "orc" 15 16 namespace llvm { 17 namespace orc { 18 namespace rt_bootstrap { 19 20 SimpleExecutorMemoryManager::~SimpleExecutorMemoryManager() { 21 assert(Allocations.empty() && "shutdown not called?"); 22 } 23 24 Expected<ExecutorAddr> SimpleExecutorMemoryManager::allocate(uint64_t Size) { 25 std::error_code EC; 26 auto MB = sys::Memory::allocateMappedMemory( 27 Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC); 28 if (EC) 29 return errorCodeToError(EC); 30 std::lock_guard<std::mutex> Lock(M); 31 assert(!Allocations.count(MB.base()) && "Duplicate allocation addr"); 32 Allocations[MB.base()].Size = Size; 33 return ExecutorAddr::fromPtr(MB.base()); 34 } 35 36 Error SimpleExecutorMemoryManager::finalize(tpctypes::FinalizeRequest &FR) { 37 ExecutorAddr Base(~0ULL); 38 std::vector<shared::WrapperFunctionCall> DeallocationActions; 39 size_t SuccessfulFinalizationActions = 0; 40 41 if (FR.Segments.empty()) { 42 // NOTE: Finalizing nothing is currently a no-op. Should it be an error? 43 if (FR.Actions.empty()) 44 return Error::success(); 45 else 46 return make_error<StringError>("Finalization actions attached to empty " 47 "finalization request", 48 inconvertibleErrorCode()); 49 } 50 51 for (auto &Seg : FR.Segments) 52 Base = std::min(Base, Seg.Addr); 53 54 for (auto &ActPair : FR.Actions) 55 if (ActPair.Dealloc) 56 DeallocationActions.push_back(ActPair.Dealloc); 57 58 // Get the Allocation for this finalization. 59 size_t AllocSize = 0; 60 { 61 std::lock_guard<std::mutex> Lock(M); 62 auto I = Allocations.find(Base.toPtr<void *>()); 63 if (I == Allocations.end()) 64 return make_error<StringError>("Attempt to finalize unrecognized " 65 "allocation " + 66 formatv("{0:x}", Base.getValue()), 67 inconvertibleErrorCode()); 68 AllocSize = I->second.Size; 69 I->second.DeallocationActions = std::move(DeallocationActions); 70 } 71 ExecutorAddr AllocEnd = Base + ExecutorAddrDiff(AllocSize); 72 73 // Bail-out function: this will run deallocation actions corresponding to any 74 // completed finalization actions, then deallocate memory. 75 auto BailOut = [&](Error Err) { 76 std::pair<void *, Allocation> AllocToDestroy; 77 78 // Get allocation to destory. 79 { 80 std::lock_guard<std::mutex> Lock(M); 81 auto I = Allocations.find(Base.toPtr<void *>()); 82 83 // Check for missing allocation (effective a double free). 84 if (I == Allocations.end()) 85 return joinErrors( 86 std::move(Err), 87 make_error<StringError>("No allocation entry found " 88 "for " + 89 formatv("{0:x}", Base.getValue()), 90 inconvertibleErrorCode())); 91 AllocToDestroy = std::move(*I); 92 Allocations.erase(I); 93 } 94 95 // Run deallocation actions for all completed finalization actions. 96 while (SuccessfulFinalizationActions) 97 Err = 98 joinErrors(std::move(Err), FR.Actions[--SuccessfulFinalizationActions] 99 .Dealloc.runWithSPSRetErrorMerged()); 100 101 // Deallocate memory. 102 sys::MemoryBlock MB(AllocToDestroy.first, AllocToDestroy.second.Size); 103 if (auto EC = sys::Memory::releaseMappedMemory(MB)) 104 Err = joinErrors(std::move(Err), errorCodeToError(EC)); 105 106 return Err; 107 }; 108 109 // Copy content and apply permissions. 110 for (auto &Seg : FR.Segments) { 111 112 // Check segment ranges. 113 if (LLVM_UNLIKELY(Seg.Size < Seg.Content.size())) 114 return BailOut(make_error<StringError>( 115 formatv("Segment {0:x} content size ({1:x} bytes) " 116 "exceeds segment size ({2:x} bytes)", 117 Seg.Addr.getValue(), Seg.Content.size(), Seg.Size), 118 inconvertibleErrorCode())); 119 ExecutorAddr SegEnd = Seg.Addr + ExecutorAddrDiff(Seg.Size); 120 if (LLVM_UNLIKELY(Seg.Addr < Base || SegEnd > AllocEnd)) 121 return BailOut(make_error<StringError>( 122 formatv("Segment {0:x} -- {1:x} crosses boundary of " 123 "allocation {2:x} -- {3:x}", 124 Seg.Addr.getValue(), SegEnd.getValue(), Base.getValue(), 125 AllocEnd.getValue()), 126 inconvertibleErrorCode())); 127 128 char *Mem = Seg.Addr.toPtr<char *>(); 129 if (!Seg.Content.empty()) 130 memcpy(Mem, Seg.Content.data(), Seg.Content.size()); 131 memset(Mem + Seg.Content.size(), 0, Seg.Size - Seg.Content.size()); 132 assert(Seg.Size <= std::numeric_limits<size_t>::max()); 133 if (auto EC = sys::Memory::protectMappedMemory( 134 {Mem, static_cast<size_t>(Seg.Size)}, 135 toSysMemoryProtectionFlags(Seg.AG.getMemProt()))) 136 return BailOut(errorCodeToError(EC)); 137 if ((Seg.AG.getMemProt() & MemProt::Exec) == MemProt::Exec) 138 sys::Memory::InvalidateInstructionCache(Mem, Seg.Size); 139 } 140 141 // Run finalization actions. 142 for (auto &ActPair : FR.Actions) { 143 if (auto Err = ActPair.Finalize.runWithSPSRetErrorMerged()) 144 return BailOut(std::move(Err)); 145 ++SuccessfulFinalizationActions; 146 } 147 148 return Error::success(); 149 } 150 151 Error SimpleExecutorMemoryManager::deallocate( 152 const std::vector<ExecutorAddr> &Bases) { 153 std::vector<std::pair<void *, Allocation>> AllocPairs; 154 AllocPairs.reserve(Bases.size()); 155 156 // Get allocation to destory. 157 Error Err = Error::success(); 158 { 159 std::lock_guard<std::mutex> Lock(M); 160 for (auto &Base : Bases) { 161 auto I = Allocations.find(Base.toPtr<void *>()); 162 163 // Check for missing allocation (effective a double free). 164 if (I != Allocations.end()) { 165 AllocPairs.push_back(std::move(*I)); 166 Allocations.erase(I); 167 } else 168 Err = joinErrors( 169 std::move(Err), 170 make_error<StringError>("No allocation entry found " 171 "for " + 172 formatv("{0:x}", Base.getValue()), 173 inconvertibleErrorCode())); 174 } 175 } 176 177 while (!AllocPairs.empty()) { 178 auto &P = AllocPairs.back(); 179 Err = joinErrors(std::move(Err), deallocateImpl(P.first, P.second)); 180 AllocPairs.pop_back(); 181 } 182 183 return Err; 184 } 185 186 Error SimpleExecutorMemoryManager::shutdown() { 187 188 AllocationsMap AM; 189 { 190 std::lock_guard<std::mutex> Lock(M); 191 AM = std::move(Allocations); 192 } 193 194 Error Err = Error::success(); 195 for (auto &KV : AM) 196 Err = joinErrors(std::move(Err), deallocateImpl(KV.first, KV.second)); 197 return Err; 198 } 199 200 void SimpleExecutorMemoryManager::addBootstrapSymbols( 201 StringMap<ExecutorAddr> &M) { 202 M[rt::SimpleExecutorMemoryManagerInstanceName] = ExecutorAddr::fromPtr(this); 203 M[rt::SimpleExecutorMemoryManagerReserveWrapperName] = 204 ExecutorAddr::fromPtr(&reserveWrapper); 205 M[rt::SimpleExecutorMemoryManagerFinalizeWrapperName] = 206 ExecutorAddr::fromPtr(&finalizeWrapper); 207 M[rt::SimpleExecutorMemoryManagerDeallocateWrapperName] = 208 ExecutorAddr::fromPtr(&deallocateWrapper); 209 } 210 211 Error SimpleExecutorMemoryManager::deallocateImpl(void *Base, Allocation &A) { 212 Error Err = Error::success(); 213 214 while (!A.DeallocationActions.empty()) { 215 Err = joinErrors(std::move(Err), 216 A.DeallocationActions.back().runWithSPSRetErrorMerged()); 217 A.DeallocationActions.pop_back(); 218 } 219 220 sys::MemoryBlock MB(Base, A.Size); 221 if (auto EC = sys::Memory::releaseMappedMemory(MB)) 222 Err = joinErrors(std::move(Err), errorCodeToError(EC)); 223 224 return Err; 225 } 226 227 llvm::orc::shared::CWrapperFunctionResult 228 SimpleExecutorMemoryManager::reserveWrapper(const char *ArgData, 229 size_t ArgSize) { 230 return shared::WrapperFunction< 231 rt::SPSSimpleExecutorMemoryManagerReserveSignature>:: 232 handle(ArgData, ArgSize, 233 shared::makeMethodWrapperHandler( 234 &SimpleExecutorMemoryManager::allocate)) 235 .release(); 236 } 237 238 llvm::orc::shared::CWrapperFunctionResult 239 SimpleExecutorMemoryManager::finalizeWrapper(const char *ArgData, 240 size_t ArgSize) { 241 return shared::WrapperFunction< 242 rt::SPSSimpleExecutorMemoryManagerFinalizeSignature>:: 243 handle(ArgData, ArgSize, 244 shared::makeMethodWrapperHandler( 245 &SimpleExecutorMemoryManager::finalize)) 246 .release(); 247 } 248 249 llvm::orc::shared::CWrapperFunctionResult 250 SimpleExecutorMemoryManager::deallocateWrapper(const char *ArgData, 251 size_t ArgSize) { 252 return shared::WrapperFunction< 253 rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>:: 254 handle(ArgData, ArgSize, 255 shared::makeMethodWrapperHandler( 256 &SimpleExecutorMemoryManager::deallocate)) 257 .release(); 258 } 259 260 } // namespace rt_bootstrap 261 } // end namespace orc 262 } // end namespace llvm 263