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