xref: /src/contrib/llvm-project/llvm/lib/ExecutionEngine/SectionMemoryManager.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
14a16efa3SDimitry Andric //===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++ -*-==//
24a16efa3SDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64a16efa3SDimitry Andric //
74a16efa3SDimitry Andric //===----------------------------------------------------------------------===//
84a16efa3SDimitry Andric //
94a16efa3SDimitry Andric // This file implements the section-based memory manager used by the MCJIT
104a16efa3SDimitry Andric // execution engine and RuntimeDyld
114a16efa3SDimitry Andric //
124a16efa3SDimitry Andric //===----------------------------------------------------------------------===//
134a16efa3SDimitry Andric 
144a16efa3SDimitry Andric #include "llvm/ExecutionEngine/SectionMemoryManager.h"
157ab83427SDimitry Andric #include "llvm/Config/config.h"
164a16efa3SDimitry Andric #include "llvm/Support/MathExtras.h"
17dd58ef01SDimitry Andric #include "llvm/Support/Process.h"
184a16efa3SDimitry Andric 
194a16efa3SDimitry Andric namespace llvm {
204a16efa3SDimitry Andric 
allocateDataSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName,bool IsReadOnly)214a16efa3SDimitry Andric uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size,
224a16efa3SDimitry Andric                                                    unsigned Alignment,
234a16efa3SDimitry Andric                                                    unsigned SectionID,
24f8af5cf6SDimitry Andric                                                    StringRef SectionName,
254a16efa3SDimitry Andric                                                    bool IsReadOnly) {
264a16efa3SDimitry Andric   if (IsReadOnly)
27044eb2f6SDimitry Andric     return allocateSection(SectionMemoryManager::AllocationPurpose::ROData,
28044eb2f6SDimitry Andric                            Size, Alignment);
29044eb2f6SDimitry Andric   return allocateSection(SectionMemoryManager::AllocationPurpose::RWData, Size,
30044eb2f6SDimitry Andric                          Alignment);
314a16efa3SDimitry Andric }
324a16efa3SDimitry Andric 
allocateCodeSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName)334a16efa3SDimitry Andric uint8_t *SectionMemoryManager::allocateCodeSection(uintptr_t Size,
344a16efa3SDimitry Andric                                                    unsigned Alignment,
35f8af5cf6SDimitry Andric                                                    unsigned SectionID,
36f8af5cf6SDimitry Andric                                                    StringRef SectionName) {
37044eb2f6SDimitry Andric   return allocateSection(SectionMemoryManager::AllocationPurpose::Code, Size,
38044eb2f6SDimitry Andric                          Alignment);
394a16efa3SDimitry Andric }
404a16efa3SDimitry Andric 
allocateSection(SectionMemoryManager::AllocationPurpose Purpose,uintptr_t Size,unsigned Alignment)41044eb2f6SDimitry Andric uint8_t *SectionMemoryManager::allocateSection(
42044eb2f6SDimitry Andric     SectionMemoryManager::AllocationPurpose Purpose, uintptr_t Size,
434a16efa3SDimitry Andric     unsigned Alignment) {
444a16efa3SDimitry Andric   if (!Alignment)
454a16efa3SDimitry Andric     Alignment = 16;
464a16efa3SDimitry Andric 
474a16efa3SDimitry Andric   assert(!(Alignment & (Alignment - 1)) && "Alignment must be a power of two.");
484a16efa3SDimitry Andric 
494a16efa3SDimitry Andric   uintptr_t RequiredSize = Alignment * ((Size + Alignment - 1) / Alignment + 1);
504a16efa3SDimitry Andric   uintptr_t Addr = 0;
514a16efa3SDimitry Andric 
52044eb2f6SDimitry Andric   MemoryGroup &MemGroup = [&]() -> MemoryGroup & {
53044eb2f6SDimitry Andric     switch (Purpose) {
54044eb2f6SDimitry Andric     case AllocationPurpose::Code:
55044eb2f6SDimitry Andric       return CodeMem;
56044eb2f6SDimitry Andric     case AllocationPurpose::ROData:
57044eb2f6SDimitry Andric       return RODataMem;
58044eb2f6SDimitry Andric     case AllocationPurpose::RWData:
59044eb2f6SDimitry Andric       return RWDataMem;
60044eb2f6SDimitry Andric     }
61044eb2f6SDimitry Andric     llvm_unreachable("Unknown SectionMemoryManager::AllocationPurpose");
62044eb2f6SDimitry Andric   }();
63044eb2f6SDimitry Andric 
644a16efa3SDimitry Andric   // Look in the list of free memory regions and use a block there if one
654a16efa3SDimitry Andric   // is available.
66dd58ef01SDimitry Andric   for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {
67e6d15924SDimitry Andric     if (FreeMB.Free.allocatedSize() >= RequiredSize) {
68dd58ef01SDimitry Andric       Addr = (uintptr_t)FreeMB.Free.base();
69e6d15924SDimitry Andric       uintptr_t EndOfBlock = Addr + FreeMB.Free.allocatedSize();
704a16efa3SDimitry Andric       // Align the address.
714a16efa3SDimitry Andric       Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);
72dd58ef01SDimitry Andric 
73dd58ef01SDimitry Andric       if (FreeMB.PendingPrefixIndex == (unsigned)-1) {
74dd58ef01SDimitry Andric         // The part of the block we're giving out to the user is now pending
75dd58ef01SDimitry Andric         MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));
76dd58ef01SDimitry Andric 
77dd58ef01SDimitry Andric         // Remember this pending block, such that future allocations can just
78dd58ef01SDimitry Andric         // modify it rather than creating a new one
79dd58ef01SDimitry Andric         FreeMB.PendingPrefixIndex = MemGroup.PendingMem.size() - 1;
80dd58ef01SDimitry Andric       } else {
81044eb2f6SDimitry Andric         sys::MemoryBlock &PendingMB =
82044eb2f6SDimitry Andric             MemGroup.PendingMem[FreeMB.PendingPrefixIndex];
83044eb2f6SDimitry Andric         PendingMB = sys::MemoryBlock(PendingMB.base(),
84044eb2f6SDimitry Andric                                      Addr + Size - (uintptr_t)PendingMB.base());
85dd58ef01SDimitry Andric       }
86dd58ef01SDimitry Andric 
87dd58ef01SDimitry Andric       // Remember how much free space is now left in this block
88044eb2f6SDimitry Andric       FreeMB.Free =
89044eb2f6SDimitry Andric           sys::MemoryBlock((void *)(Addr + Size), EndOfBlock - Addr - Size);
904a16efa3SDimitry Andric       return (uint8_t *)Addr;
914a16efa3SDimitry Andric     }
924a16efa3SDimitry Andric   }
934a16efa3SDimitry Andric 
944a16efa3SDimitry Andric   // No pre-allocated free block was large enough. Allocate a new memory region.
954a16efa3SDimitry Andric   // Note that all sections get allocated as read-write.  The permissions will
964a16efa3SDimitry Andric   // be updated later based on memory group.
974a16efa3SDimitry Andric   //
984a16efa3SDimitry Andric   // FIXME: It would be useful to define a default allocation size (or add
994a16efa3SDimitry Andric   // it as a constructor parameter) to minimize the number of allocations.
1004a16efa3SDimitry Andric   //
1014a16efa3SDimitry Andric   // FIXME: Initialize the Near member for each memory group to avoid
1024a16efa3SDimitry Andric   // interleaving.
1035ca98fd9SDimitry Andric   std::error_code ec;
1047fa27ce4SDimitry Andric   sys::MemoryBlock MB = MMapper->allocateMappedMemory(
105044eb2f6SDimitry Andric       Purpose, RequiredSize, &MemGroup.Near,
106044eb2f6SDimitry Andric       sys::Memory::MF_READ | sys::Memory::MF_WRITE, ec);
1074a16efa3SDimitry Andric   if (ec) {
1085ca98fd9SDimitry Andric     // FIXME: Add error propagation to the interface.
1095ca98fd9SDimitry Andric     return nullptr;
1104a16efa3SDimitry Andric   }
1114a16efa3SDimitry Andric 
1124a16efa3SDimitry Andric   // Save this address as the basis for our next request
1134a16efa3SDimitry Andric   MemGroup.Near = MB;
1144a16efa3SDimitry Andric 
115b60736ecSDimitry Andric   // Copy the address to all the other groups, if they have not
116b60736ecSDimitry Andric   // been initialized.
1176f8fc217SDimitry Andric   if (CodeMem.Near.base() == nullptr)
118b60736ecSDimitry Andric     CodeMem.Near = MB;
1196f8fc217SDimitry Andric   if (RODataMem.Near.base() == nullptr)
120b60736ecSDimitry Andric     RODataMem.Near = MB;
1216f8fc217SDimitry Andric   if (RWDataMem.Near.base() == nullptr)
122b60736ecSDimitry Andric     RWDataMem.Near = MB;
123b60736ecSDimitry Andric 
124dd58ef01SDimitry Andric   // Remember that we allocated this memory
1254a16efa3SDimitry Andric   MemGroup.AllocatedMem.push_back(MB);
1264a16efa3SDimitry Andric   Addr = (uintptr_t)MB.base();
127e6d15924SDimitry Andric   uintptr_t EndOfBlock = Addr + MB.allocatedSize();
1284a16efa3SDimitry Andric 
1294a16efa3SDimitry Andric   // Align the address.
1304a16efa3SDimitry Andric   Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);
1314a16efa3SDimitry Andric 
132dd58ef01SDimitry Andric   // The part of the block we're giving out to the user is now pending
133dd58ef01SDimitry Andric   MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));
134dd58ef01SDimitry Andric 
1354a16efa3SDimitry Andric   // The allocateMappedMemory may allocate much more memory than we need. In
1364a16efa3SDimitry Andric   // this case, we store the unused memory as a free memory block.
1374a16efa3SDimitry Andric   unsigned FreeSize = EndOfBlock - Addr - Size;
138dd58ef01SDimitry Andric   if (FreeSize > 16) {
139dd58ef01SDimitry Andric     FreeMemBlock FreeMB;
140dd58ef01SDimitry Andric     FreeMB.Free = sys::MemoryBlock((void *)(Addr + Size), FreeSize);
141dd58ef01SDimitry Andric     FreeMB.PendingPrefixIndex = (unsigned)-1;
142dd58ef01SDimitry Andric     MemGroup.FreeMem.push_back(FreeMB);
143dd58ef01SDimitry Andric   }
1444a16efa3SDimitry Andric 
1454a16efa3SDimitry Andric   // Return aligned address
1464a16efa3SDimitry Andric   return (uint8_t *)Addr;
1474a16efa3SDimitry Andric }
1484a16efa3SDimitry Andric 
finalizeMemory(std::string * ErrMsg)149044eb2f6SDimitry Andric bool SectionMemoryManager::finalizeMemory(std::string *ErrMsg) {
1504a16efa3SDimitry Andric   // FIXME: Should in-progress permissions be reverted if an error occurs?
1515ca98fd9SDimitry Andric   std::error_code ec;
1524a16efa3SDimitry Andric 
1534a16efa3SDimitry Andric   // Make code memory executable.
1544a16efa3SDimitry Andric   ec = applyMemoryGroupPermissions(CodeMem,
1554a16efa3SDimitry Andric                                    sys::Memory::MF_READ | sys::Memory::MF_EXEC);
1564a16efa3SDimitry Andric   if (ec) {
1574a16efa3SDimitry Andric     if (ErrMsg) {
1584a16efa3SDimitry Andric       *ErrMsg = ec.message();
1594a16efa3SDimitry Andric     }
1604a16efa3SDimitry Andric     return true;
1614a16efa3SDimitry Andric   }
1624a16efa3SDimitry Andric 
1634a16efa3SDimitry Andric   // Make read-only data memory read-only.
164b60736ecSDimitry Andric   ec = applyMemoryGroupPermissions(RODataMem, sys::Memory::MF_READ);
1654a16efa3SDimitry Andric   if (ec) {
1664a16efa3SDimitry Andric     if (ErrMsg) {
1674a16efa3SDimitry Andric       *ErrMsg = ec.message();
1684a16efa3SDimitry Andric     }
1694a16efa3SDimitry Andric     return true;
1704a16efa3SDimitry Andric   }
1714a16efa3SDimitry Andric 
1724a16efa3SDimitry Andric   // Read-write data memory already has the correct permissions
1734a16efa3SDimitry Andric 
17459d6cff9SDimitry Andric   // Some platforms with separate data cache and instruction cache require
17559d6cff9SDimitry Andric   // explicit cache flush, otherwise JIT code manipulations (like resolved
17659d6cff9SDimitry Andric   // relocations) will get to the data cache but not to the instruction cache.
17759d6cff9SDimitry Andric   invalidateInstructionCache();
17859d6cff9SDimitry Andric 
1794a16efa3SDimitry Andric   return false;
1804a16efa3SDimitry Andric }
1814a16efa3SDimitry Andric 
trimBlockToPageSize(sys::MemoryBlock M)182dd58ef01SDimitry Andric static sys::MemoryBlock trimBlockToPageSize(sys::MemoryBlock M) {
183e6d15924SDimitry Andric   static const size_t PageSize = sys::Process::getPageSizeEstimate();
184dd58ef01SDimitry Andric 
185dd58ef01SDimitry Andric   size_t StartOverlap =
186dd58ef01SDimitry Andric       (PageSize - ((uintptr_t)M.base() % PageSize)) % PageSize;
187dd58ef01SDimitry Andric 
188e6d15924SDimitry Andric   size_t TrimmedSize = M.allocatedSize();
189dd58ef01SDimitry Andric   TrimmedSize -= StartOverlap;
190dd58ef01SDimitry Andric   TrimmedSize -= TrimmedSize % PageSize;
191dd58ef01SDimitry Andric 
192044eb2f6SDimitry Andric   sys::MemoryBlock Trimmed((void *)((uintptr_t)M.base() + StartOverlap),
193044eb2f6SDimitry Andric                            TrimmedSize);
194dd58ef01SDimitry Andric 
195dd58ef01SDimitry Andric   assert(((uintptr_t)Trimmed.base() % PageSize) == 0);
196e6d15924SDimitry Andric   assert((Trimmed.allocatedSize() % PageSize) == 0);
197e6d15924SDimitry Andric   assert(M.base() <= Trimmed.base() &&
198e6d15924SDimitry Andric          Trimmed.allocatedSize() <= M.allocatedSize());
199dd58ef01SDimitry Andric 
200dd58ef01SDimitry Andric   return Trimmed;
201dd58ef01SDimitry Andric }
202dd58ef01SDimitry Andric 
2035ca98fd9SDimitry Andric std::error_code
applyMemoryGroupPermissions(MemoryGroup & MemGroup,unsigned Permissions)2045ca98fd9SDimitry Andric SectionMemoryManager::applyMemoryGroupPermissions(MemoryGroup &MemGroup,
2054a16efa3SDimitry Andric                                                   unsigned Permissions) {
206dd58ef01SDimitry Andric   for (sys::MemoryBlock &MB : MemGroup.PendingMem)
2077fa27ce4SDimitry Andric     if (std::error_code EC = MMapper->protectMappedMemory(MB, Permissions))
208dd58ef01SDimitry Andric       return EC;
2094a16efa3SDimitry Andric 
210dd58ef01SDimitry Andric   MemGroup.PendingMem.clear();
211dd58ef01SDimitry Andric 
212dd58ef01SDimitry Andric   // Now go through free blocks and trim any of them that don't span the entire
213dd58ef01SDimitry Andric   // page because one of the pending blocks may have overlapped it.
214dd58ef01SDimitry Andric   for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {
215dd58ef01SDimitry Andric     FreeMB.Free = trimBlockToPageSize(FreeMB.Free);
216dd58ef01SDimitry Andric     // We cleared the PendingMem list, so all these pointers are now invalid
217dd58ef01SDimitry Andric     FreeMB.PendingPrefixIndex = (unsigned)-1;
2184a16efa3SDimitry Andric   }
219dd58ef01SDimitry Andric 
220dd58ef01SDimitry Andric   // Remove all blocks which are now empty
221b60736ecSDimitry Andric   erase_if(MemGroup.FreeMem, [](FreeMemBlock &FreeMB) {
222e6d15924SDimitry Andric     return FreeMB.Free.allocatedSize() == 0;
223b60736ecSDimitry Andric   });
2244a16efa3SDimitry Andric 
2255ca98fd9SDimitry Andric   return std::error_code();
2264a16efa3SDimitry Andric }
2274a16efa3SDimitry Andric 
invalidateInstructionCache()2284a16efa3SDimitry Andric void SectionMemoryManager::invalidateInstructionCache() {
229dd58ef01SDimitry Andric   for (sys::MemoryBlock &Block : CodeMem.PendingMem)
230e6d15924SDimitry Andric     sys::Memory::InvalidateInstructionCache(Block.base(),
231e6d15924SDimitry Andric                                             Block.allocatedSize());
2324a16efa3SDimitry Andric }
2334a16efa3SDimitry Andric 
~SectionMemoryManager()2344a16efa3SDimitry Andric SectionMemoryManager::~SectionMemoryManager() {
235dd58ef01SDimitry Andric   for (MemoryGroup *Group : {&CodeMem, &RWDataMem, &RODataMem}) {
236dd58ef01SDimitry Andric     for (sys::MemoryBlock &Block : Group->AllocatedMem)
2377fa27ce4SDimitry Andric       MMapper->releaseMappedMemory(Block);
238dd58ef01SDimitry Andric   }
2394a16efa3SDimitry Andric }
2404a16efa3SDimitry Andric 
241145449b1SDimitry Andric SectionMemoryManager::MemoryMapper::~MemoryMapper() = default;
242044eb2f6SDimitry Andric 
anchor()243eb11fae6SDimitry Andric void SectionMemoryManager::anchor() {}
244eb11fae6SDimitry Andric 
245044eb2f6SDimitry Andric namespace {
246044eb2f6SDimitry Andric // Trivial implementation of SectionMemoryManager::MemoryMapper that just calls
247044eb2f6SDimitry Andric // into sys::Memory.
248044eb2f6SDimitry Andric class DefaultMMapper final : public SectionMemoryManager::MemoryMapper {
249044eb2f6SDimitry Andric public:
250044eb2f6SDimitry Andric   sys::MemoryBlock
allocateMappedMemory(SectionMemoryManager::AllocationPurpose Purpose,size_t NumBytes,const sys::MemoryBlock * const NearBlock,unsigned Flags,std::error_code & EC)251044eb2f6SDimitry Andric   allocateMappedMemory(SectionMemoryManager::AllocationPurpose Purpose,
252044eb2f6SDimitry Andric                        size_t NumBytes, const sys::MemoryBlock *const NearBlock,
253044eb2f6SDimitry Andric                        unsigned Flags, std::error_code &EC) override {
254044eb2f6SDimitry Andric     return sys::Memory::allocateMappedMemory(NumBytes, NearBlock, Flags, EC);
255044eb2f6SDimitry Andric   }
256044eb2f6SDimitry Andric 
protectMappedMemory(const sys::MemoryBlock & Block,unsigned Flags)257044eb2f6SDimitry Andric   std::error_code protectMappedMemory(const sys::MemoryBlock &Block,
258044eb2f6SDimitry Andric                                       unsigned Flags) override {
259044eb2f6SDimitry Andric     return sys::Memory::protectMappedMemory(Block, Flags);
260044eb2f6SDimitry Andric   }
261044eb2f6SDimitry Andric 
releaseMappedMemory(sys::MemoryBlock & M)262044eb2f6SDimitry Andric   std::error_code releaseMappedMemory(sys::MemoryBlock &M) override {
263044eb2f6SDimitry Andric     return sys::Memory::releaseMappedMemory(M);
264044eb2f6SDimitry Andric   }
265044eb2f6SDimitry Andric };
266044eb2f6SDimitry Andric } // namespace
267044eb2f6SDimitry Andric 
SectionMemoryManager(MemoryMapper * UnownedMM)2687fa27ce4SDimitry Andric SectionMemoryManager::SectionMemoryManager(MemoryMapper *UnownedMM)
2697fa27ce4SDimitry Andric     : MMapper(UnownedMM), OwnedMMapper(nullptr) {
2707fa27ce4SDimitry Andric   if (!MMapper) {
2717fa27ce4SDimitry Andric     OwnedMMapper = std::make_unique<DefaultMMapper>();
2727fa27ce4SDimitry Andric     MMapper = OwnedMMapper.get();
2737fa27ce4SDimitry Andric   }
2747fa27ce4SDimitry Andric }
275044eb2f6SDimitry Andric 
2764a16efa3SDimitry Andric } // namespace llvm
277