xref: /freebsd/contrib/llvm-project/llvm/lib/ExecutionEngine/SectionMemoryManager.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
10b57cec5SDimitry Andric //===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++ -*-==//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file implements the section-based memory manager used by the MCJIT
100b57cec5SDimitry Andric // execution engine and RuntimeDyld
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "llvm/ExecutionEngine/SectionMemoryManager.h"
150b57cec5SDimitry Andric #include "llvm/Config/config.h"
160b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h"
170b57cec5SDimitry Andric #include "llvm/Support/Process.h"
180b57cec5SDimitry Andric 
190b57cec5SDimitry Andric namespace llvm {
200b57cec5SDimitry Andric 
allocateDataSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName,bool IsReadOnly)210b57cec5SDimitry Andric uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size,
220b57cec5SDimitry Andric                                                    unsigned Alignment,
230b57cec5SDimitry Andric                                                    unsigned SectionID,
240b57cec5SDimitry Andric                                                    StringRef SectionName,
250b57cec5SDimitry Andric                                                    bool IsReadOnly) {
260b57cec5SDimitry Andric   if (IsReadOnly)
270b57cec5SDimitry Andric     return allocateSection(SectionMemoryManager::AllocationPurpose::ROData,
280b57cec5SDimitry Andric                            Size, Alignment);
290b57cec5SDimitry Andric   return allocateSection(SectionMemoryManager::AllocationPurpose::RWData, Size,
300b57cec5SDimitry Andric                          Alignment);
310b57cec5SDimitry Andric }
320b57cec5SDimitry Andric 
allocateCodeSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName)330b57cec5SDimitry Andric uint8_t *SectionMemoryManager::allocateCodeSection(uintptr_t Size,
340b57cec5SDimitry Andric                                                    unsigned Alignment,
350b57cec5SDimitry Andric                                                    unsigned SectionID,
360b57cec5SDimitry Andric                                                    StringRef SectionName) {
370b57cec5SDimitry Andric   return allocateSection(SectionMemoryManager::AllocationPurpose::Code, Size,
380b57cec5SDimitry Andric                          Alignment);
390b57cec5SDimitry Andric }
400b57cec5SDimitry Andric 
allocateSection(SectionMemoryManager::AllocationPurpose Purpose,uintptr_t Size,unsigned Alignment)410b57cec5SDimitry Andric uint8_t *SectionMemoryManager::allocateSection(
420b57cec5SDimitry Andric     SectionMemoryManager::AllocationPurpose Purpose, uintptr_t Size,
430b57cec5SDimitry Andric     unsigned Alignment) {
440b57cec5SDimitry Andric   if (!Alignment)
450b57cec5SDimitry Andric     Alignment = 16;
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric   assert(!(Alignment & (Alignment - 1)) && "Alignment must be a power of two.");
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric   uintptr_t RequiredSize = Alignment * ((Size + Alignment - 1) / Alignment + 1);
500b57cec5SDimitry Andric   uintptr_t Addr = 0;
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric   MemoryGroup &MemGroup = [&]() -> MemoryGroup & {
530b57cec5SDimitry Andric     switch (Purpose) {
540b57cec5SDimitry Andric     case AllocationPurpose::Code:
550b57cec5SDimitry Andric       return CodeMem;
560b57cec5SDimitry Andric     case AllocationPurpose::ROData:
570b57cec5SDimitry Andric       return RODataMem;
580b57cec5SDimitry Andric     case AllocationPurpose::RWData:
590b57cec5SDimitry Andric       return RWDataMem;
600b57cec5SDimitry Andric     }
610b57cec5SDimitry Andric     llvm_unreachable("Unknown SectionMemoryManager::AllocationPurpose");
620b57cec5SDimitry Andric   }();
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric   // Look in the list of free memory regions and use a block there if one
650b57cec5SDimitry Andric   // is available.
660b57cec5SDimitry Andric   for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {
670b57cec5SDimitry Andric     if (FreeMB.Free.allocatedSize() >= RequiredSize) {
680b57cec5SDimitry Andric       Addr = (uintptr_t)FreeMB.Free.base();
690b57cec5SDimitry Andric       uintptr_t EndOfBlock = Addr + FreeMB.Free.allocatedSize();
700b57cec5SDimitry Andric       // Align the address.
710b57cec5SDimitry Andric       Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric       if (FreeMB.PendingPrefixIndex == (unsigned)-1) {
740b57cec5SDimitry Andric         // The part of the block we're giving out to the user is now pending
750b57cec5SDimitry Andric         MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric         // Remember this pending block, such that future allocations can just
780b57cec5SDimitry Andric         // modify it rather than creating a new one
790b57cec5SDimitry Andric         FreeMB.PendingPrefixIndex = MemGroup.PendingMem.size() - 1;
800b57cec5SDimitry Andric       } else {
810b57cec5SDimitry Andric         sys::MemoryBlock &PendingMB =
820b57cec5SDimitry Andric             MemGroup.PendingMem[FreeMB.PendingPrefixIndex];
830b57cec5SDimitry Andric         PendingMB = sys::MemoryBlock(PendingMB.base(),
840b57cec5SDimitry Andric                                      Addr + Size - (uintptr_t)PendingMB.base());
850b57cec5SDimitry Andric       }
860b57cec5SDimitry Andric 
870b57cec5SDimitry Andric       // Remember how much free space is now left in this block
880b57cec5SDimitry Andric       FreeMB.Free =
890b57cec5SDimitry Andric           sys::MemoryBlock((void *)(Addr + Size), EndOfBlock - Addr - Size);
900b57cec5SDimitry Andric       return (uint8_t *)Addr;
910b57cec5SDimitry Andric     }
920b57cec5SDimitry Andric   }
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric   // No pre-allocated free block was large enough. Allocate a new memory region.
950b57cec5SDimitry Andric   // Note that all sections get allocated as read-write.  The permissions will
960b57cec5SDimitry Andric   // be updated later based on memory group.
970b57cec5SDimitry Andric   //
980b57cec5SDimitry Andric   // FIXME: It would be useful to define a default allocation size (or add
990b57cec5SDimitry Andric   // it as a constructor parameter) to minimize the number of allocations.
1000b57cec5SDimitry Andric   //
1010b57cec5SDimitry Andric   // FIXME: Initialize the Near member for each memory group to avoid
1020b57cec5SDimitry Andric   // interleaving.
1030b57cec5SDimitry Andric   std::error_code ec;
104*06c3fb27SDimitry Andric   sys::MemoryBlock MB = MMapper->allocateMappedMemory(
1050b57cec5SDimitry Andric       Purpose, RequiredSize, &MemGroup.Near,
1060b57cec5SDimitry Andric       sys::Memory::MF_READ | sys::Memory::MF_WRITE, ec);
1070b57cec5SDimitry Andric   if (ec) {
1080b57cec5SDimitry Andric     // FIXME: Add error propagation to the interface.
1090b57cec5SDimitry Andric     return nullptr;
1100b57cec5SDimitry Andric   }
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric   // Save this address as the basis for our next request
1130b57cec5SDimitry Andric   MemGroup.Near = MB;
1140b57cec5SDimitry Andric 
115e8d8bef9SDimitry Andric   // Copy the address to all the other groups, if they have not
116e8d8bef9SDimitry Andric   // been initialized.
11704eeddc0SDimitry Andric   if (CodeMem.Near.base() == nullptr)
118e8d8bef9SDimitry Andric     CodeMem.Near = MB;
11904eeddc0SDimitry Andric   if (RODataMem.Near.base() == nullptr)
120e8d8bef9SDimitry Andric     RODataMem.Near = MB;
12104eeddc0SDimitry Andric   if (RWDataMem.Near.base() == nullptr)
122e8d8bef9SDimitry Andric     RWDataMem.Near = MB;
123e8d8bef9SDimitry Andric 
1240b57cec5SDimitry Andric   // Remember that we allocated this memory
1250b57cec5SDimitry Andric   MemGroup.AllocatedMem.push_back(MB);
1260b57cec5SDimitry Andric   Addr = (uintptr_t)MB.base();
1270b57cec5SDimitry Andric   uintptr_t EndOfBlock = Addr + MB.allocatedSize();
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric   // Align the address.
1300b57cec5SDimitry Andric   Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric   // The part of the block we're giving out to the user is now pending
1330b57cec5SDimitry Andric   MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));
1340b57cec5SDimitry Andric 
1350b57cec5SDimitry Andric   // The allocateMappedMemory may allocate much more memory than we need. In
1360b57cec5SDimitry Andric   // this case, we store the unused memory as a free memory block.
1370b57cec5SDimitry Andric   unsigned FreeSize = EndOfBlock - Addr - Size;
1380b57cec5SDimitry Andric   if (FreeSize > 16) {
1390b57cec5SDimitry Andric     FreeMemBlock FreeMB;
1400b57cec5SDimitry Andric     FreeMB.Free = sys::MemoryBlock((void *)(Addr + Size), FreeSize);
1410b57cec5SDimitry Andric     FreeMB.PendingPrefixIndex = (unsigned)-1;
1420b57cec5SDimitry Andric     MemGroup.FreeMem.push_back(FreeMB);
1430b57cec5SDimitry Andric   }
1440b57cec5SDimitry Andric 
1450b57cec5SDimitry Andric   // Return aligned address
1460b57cec5SDimitry Andric   return (uint8_t *)Addr;
1470b57cec5SDimitry Andric }
1480b57cec5SDimitry Andric 
finalizeMemory(std::string * ErrMsg)1490b57cec5SDimitry Andric bool SectionMemoryManager::finalizeMemory(std::string *ErrMsg) {
1500b57cec5SDimitry Andric   // FIXME: Should in-progress permissions be reverted if an error occurs?
1510b57cec5SDimitry Andric   std::error_code ec;
1520b57cec5SDimitry Andric 
1530b57cec5SDimitry Andric   // Make code memory executable.
1540b57cec5SDimitry Andric   ec = applyMemoryGroupPermissions(CodeMem,
1550b57cec5SDimitry Andric                                    sys::Memory::MF_READ | sys::Memory::MF_EXEC);
1560b57cec5SDimitry Andric   if (ec) {
1570b57cec5SDimitry Andric     if (ErrMsg) {
1580b57cec5SDimitry Andric       *ErrMsg = ec.message();
1590b57cec5SDimitry Andric     }
1600b57cec5SDimitry Andric     return true;
1610b57cec5SDimitry Andric   }
1620b57cec5SDimitry Andric 
1630b57cec5SDimitry Andric   // Make read-only data memory read-only.
164e8d8bef9SDimitry Andric   ec = applyMemoryGroupPermissions(RODataMem, sys::Memory::MF_READ);
1650b57cec5SDimitry Andric   if (ec) {
1660b57cec5SDimitry Andric     if (ErrMsg) {
1670b57cec5SDimitry Andric       *ErrMsg = ec.message();
1680b57cec5SDimitry Andric     }
1690b57cec5SDimitry Andric     return true;
1700b57cec5SDimitry Andric   }
1710b57cec5SDimitry Andric 
1720b57cec5SDimitry Andric   // Read-write data memory already has the correct permissions
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric   // Some platforms with separate data cache and instruction cache require
1750b57cec5SDimitry Andric   // explicit cache flush, otherwise JIT code manipulations (like resolved
1760b57cec5SDimitry Andric   // relocations) will get to the data cache but not to the instruction cache.
1770b57cec5SDimitry Andric   invalidateInstructionCache();
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric   return false;
1800b57cec5SDimitry Andric }
1810b57cec5SDimitry Andric 
trimBlockToPageSize(sys::MemoryBlock M)1820b57cec5SDimitry Andric static sys::MemoryBlock trimBlockToPageSize(sys::MemoryBlock M) {
1830b57cec5SDimitry Andric   static const size_t PageSize = sys::Process::getPageSizeEstimate();
1840b57cec5SDimitry Andric 
1850b57cec5SDimitry Andric   size_t StartOverlap =
1860b57cec5SDimitry Andric       (PageSize - ((uintptr_t)M.base() % PageSize)) % PageSize;
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric   size_t TrimmedSize = M.allocatedSize();
1890b57cec5SDimitry Andric   TrimmedSize -= StartOverlap;
1900b57cec5SDimitry Andric   TrimmedSize -= TrimmedSize % PageSize;
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric   sys::MemoryBlock Trimmed((void *)((uintptr_t)M.base() + StartOverlap),
1930b57cec5SDimitry Andric                            TrimmedSize);
1940b57cec5SDimitry Andric 
1950b57cec5SDimitry Andric   assert(((uintptr_t)Trimmed.base() % PageSize) == 0);
1960b57cec5SDimitry Andric   assert((Trimmed.allocatedSize() % PageSize) == 0);
1970b57cec5SDimitry Andric   assert(M.base() <= Trimmed.base() &&
1980b57cec5SDimitry Andric          Trimmed.allocatedSize() <= M.allocatedSize());
1990b57cec5SDimitry Andric 
2000b57cec5SDimitry Andric   return Trimmed;
2010b57cec5SDimitry Andric }
2020b57cec5SDimitry Andric 
2030b57cec5SDimitry Andric std::error_code
applyMemoryGroupPermissions(MemoryGroup & MemGroup,unsigned Permissions)2040b57cec5SDimitry Andric SectionMemoryManager::applyMemoryGroupPermissions(MemoryGroup &MemGroup,
2050b57cec5SDimitry Andric                                                   unsigned Permissions) {
2060b57cec5SDimitry Andric   for (sys::MemoryBlock &MB : MemGroup.PendingMem)
207*06c3fb27SDimitry Andric     if (std::error_code EC = MMapper->protectMappedMemory(MB, Permissions))
2080b57cec5SDimitry Andric       return EC;
2090b57cec5SDimitry Andric 
2100b57cec5SDimitry Andric   MemGroup.PendingMem.clear();
2110b57cec5SDimitry Andric 
2120b57cec5SDimitry Andric   // Now go through free blocks and trim any of them that don't span the entire
2130b57cec5SDimitry Andric   // page because one of the pending blocks may have overlapped it.
2140b57cec5SDimitry Andric   for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {
2150b57cec5SDimitry Andric     FreeMB.Free = trimBlockToPageSize(FreeMB.Free);
2160b57cec5SDimitry Andric     // We cleared the PendingMem list, so all these pointers are now invalid
2170b57cec5SDimitry Andric     FreeMB.PendingPrefixIndex = (unsigned)-1;
2180b57cec5SDimitry Andric   }
2190b57cec5SDimitry Andric 
2200b57cec5SDimitry Andric   // Remove all blocks which are now empty
221e8d8bef9SDimitry Andric   erase_if(MemGroup.FreeMem, [](FreeMemBlock &FreeMB) {
2220b57cec5SDimitry Andric     return FreeMB.Free.allocatedSize() == 0;
223e8d8bef9SDimitry Andric   });
2240b57cec5SDimitry Andric 
2250b57cec5SDimitry Andric   return std::error_code();
2260b57cec5SDimitry Andric }
2270b57cec5SDimitry Andric 
invalidateInstructionCache()2280b57cec5SDimitry Andric void SectionMemoryManager::invalidateInstructionCache() {
2290b57cec5SDimitry Andric   for (sys::MemoryBlock &Block : CodeMem.PendingMem)
2300b57cec5SDimitry Andric     sys::Memory::InvalidateInstructionCache(Block.base(),
2310b57cec5SDimitry Andric                                             Block.allocatedSize());
2320b57cec5SDimitry Andric }
2330b57cec5SDimitry Andric 
~SectionMemoryManager()2340b57cec5SDimitry Andric SectionMemoryManager::~SectionMemoryManager() {
2350b57cec5SDimitry Andric   for (MemoryGroup *Group : {&CodeMem, &RWDataMem, &RODataMem}) {
2360b57cec5SDimitry Andric     for (sys::MemoryBlock &Block : Group->AllocatedMem)
237*06c3fb27SDimitry Andric       MMapper->releaseMappedMemory(Block);
2380b57cec5SDimitry Andric   }
2390b57cec5SDimitry Andric }
2400b57cec5SDimitry Andric 
24181ad6265SDimitry Andric SectionMemoryManager::MemoryMapper::~MemoryMapper() = default;
2420b57cec5SDimitry Andric 
anchor()2430b57cec5SDimitry Andric void SectionMemoryManager::anchor() {}
2440b57cec5SDimitry Andric 
2450b57cec5SDimitry Andric namespace {
2460b57cec5SDimitry Andric // Trivial implementation of SectionMemoryManager::MemoryMapper that just calls
2470b57cec5SDimitry Andric // into sys::Memory.
2480b57cec5SDimitry Andric class DefaultMMapper final : public SectionMemoryManager::MemoryMapper {
2490b57cec5SDimitry Andric public:
2500b57cec5SDimitry Andric   sys::MemoryBlock
allocateMappedMemory(SectionMemoryManager::AllocationPurpose Purpose,size_t NumBytes,const sys::MemoryBlock * const NearBlock,unsigned Flags,std::error_code & EC)2510b57cec5SDimitry Andric   allocateMappedMemory(SectionMemoryManager::AllocationPurpose Purpose,
2520b57cec5SDimitry Andric                        size_t NumBytes, const sys::MemoryBlock *const NearBlock,
2530b57cec5SDimitry Andric                        unsigned Flags, std::error_code &EC) override {
2540b57cec5SDimitry Andric     return sys::Memory::allocateMappedMemory(NumBytes, NearBlock, Flags, EC);
2550b57cec5SDimitry Andric   }
2560b57cec5SDimitry Andric 
protectMappedMemory(const sys::MemoryBlock & Block,unsigned Flags)2570b57cec5SDimitry Andric   std::error_code protectMappedMemory(const sys::MemoryBlock &Block,
2580b57cec5SDimitry Andric                                       unsigned Flags) override {
2590b57cec5SDimitry Andric     return sys::Memory::protectMappedMemory(Block, Flags);
2600b57cec5SDimitry Andric   }
2610b57cec5SDimitry Andric 
releaseMappedMemory(sys::MemoryBlock & M)2620b57cec5SDimitry Andric   std::error_code releaseMappedMemory(sys::MemoryBlock &M) override {
2630b57cec5SDimitry Andric     return sys::Memory::releaseMappedMemory(M);
2640b57cec5SDimitry Andric   }
2650b57cec5SDimitry Andric };
2660b57cec5SDimitry Andric } // namespace
2670b57cec5SDimitry Andric 
SectionMemoryManager(MemoryMapper * UnownedMM)268*06c3fb27SDimitry Andric SectionMemoryManager::SectionMemoryManager(MemoryMapper *UnownedMM)
269*06c3fb27SDimitry Andric     : MMapper(UnownedMM), OwnedMMapper(nullptr) {
270*06c3fb27SDimitry Andric   if (!MMapper) {
271*06c3fb27SDimitry Andric     OwnedMMapper = std::make_unique<DefaultMMapper>();
272*06c3fb27SDimitry Andric     MMapper = OwnedMMapper.get();
273*06c3fb27SDimitry Andric   }
274*06c3fb27SDimitry Andric }
2750b57cec5SDimitry Andric 
2760b57cec5SDimitry Andric } // namespace llvm
277