17330f729Sjoerg //===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++ -*-==//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This file implements the section-based memory manager used by the MCJIT
107330f729Sjoerg // execution engine and RuntimeDyld
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg
147330f729Sjoerg #include "llvm/ExecutionEngine/SectionMemoryManager.h"
157330f729Sjoerg #include "llvm/Config/config.h"
167330f729Sjoerg #include "llvm/Support/MathExtras.h"
177330f729Sjoerg #include "llvm/Support/Process.h"
187330f729Sjoerg
197330f729Sjoerg namespace llvm {
207330f729Sjoerg
allocateDataSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName,bool IsReadOnly)217330f729Sjoerg uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size,
227330f729Sjoerg unsigned Alignment,
237330f729Sjoerg unsigned SectionID,
247330f729Sjoerg StringRef SectionName,
257330f729Sjoerg bool IsReadOnly) {
267330f729Sjoerg if (IsReadOnly)
277330f729Sjoerg return allocateSection(SectionMemoryManager::AllocationPurpose::ROData,
287330f729Sjoerg Size, Alignment);
297330f729Sjoerg return allocateSection(SectionMemoryManager::AllocationPurpose::RWData, Size,
307330f729Sjoerg Alignment);
317330f729Sjoerg }
327330f729Sjoerg
allocateCodeSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName)337330f729Sjoerg uint8_t *SectionMemoryManager::allocateCodeSection(uintptr_t Size,
347330f729Sjoerg unsigned Alignment,
357330f729Sjoerg unsigned SectionID,
367330f729Sjoerg StringRef SectionName) {
377330f729Sjoerg return allocateSection(SectionMemoryManager::AllocationPurpose::Code, Size,
387330f729Sjoerg Alignment);
397330f729Sjoerg }
407330f729Sjoerg
allocateSection(SectionMemoryManager::AllocationPurpose Purpose,uintptr_t Size,unsigned Alignment)417330f729Sjoerg uint8_t *SectionMemoryManager::allocateSection(
427330f729Sjoerg SectionMemoryManager::AllocationPurpose Purpose, uintptr_t Size,
437330f729Sjoerg unsigned Alignment) {
447330f729Sjoerg if (!Alignment)
457330f729Sjoerg Alignment = 16;
467330f729Sjoerg
477330f729Sjoerg assert(!(Alignment & (Alignment - 1)) && "Alignment must be a power of two.");
487330f729Sjoerg
497330f729Sjoerg uintptr_t RequiredSize = Alignment * ((Size + Alignment - 1) / Alignment + 1);
507330f729Sjoerg uintptr_t Addr = 0;
517330f729Sjoerg
527330f729Sjoerg MemoryGroup &MemGroup = [&]() -> MemoryGroup & {
537330f729Sjoerg switch (Purpose) {
547330f729Sjoerg case AllocationPurpose::Code:
557330f729Sjoerg return CodeMem;
567330f729Sjoerg case AllocationPurpose::ROData:
577330f729Sjoerg return RODataMem;
587330f729Sjoerg case AllocationPurpose::RWData:
597330f729Sjoerg return RWDataMem;
607330f729Sjoerg }
617330f729Sjoerg llvm_unreachable("Unknown SectionMemoryManager::AllocationPurpose");
627330f729Sjoerg }();
637330f729Sjoerg
647330f729Sjoerg // Look in the list of free memory regions and use a block there if one
657330f729Sjoerg // is available.
667330f729Sjoerg for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {
677330f729Sjoerg if (FreeMB.Free.allocatedSize() >= RequiredSize) {
687330f729Sjoerg Addr = (uintptr_t)FreeMB.Free.base();
697330f729Sjoerg uintptr_t EndOfBlock = Addr + FreeMB.Free.allocatedSize();
707330f729Sjoerg // Align the address.
717330f729Sjoerg Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);
727330f729Sjoerg
737330f729Sjoerg if (FreeMB.PendingPrefixIndex == (unsigned)-1) {
747330f729Sjoerg // The part of the block we're giving out to the user is now pending
757330f729Sjoerg MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));
767330f729Sjoerg
777330f729Sjoerg // Remember this pending block, such that future allocations can just
787330f729Sjoerg // modify it rather than creating a new one
797330f729Sjoerg FreeMB.PendingPrefixIndex = MemGroup.PendingMem.size() - 1;
807330f729Sjoerg } else {
817330f729Sjoerg sys::MemoryBlock &PendingMB =
827330f729Sjoerg MemGroup.PendingMem[FreeMB.PendingPrefixIndex];
837330f729Sjoerg PendingMB = sys::MemoryBlock(PendingMB.base(),
847330f729Sjoerg Addr + Size - (uintptr_t)PendingMB.base());
857330f729Sjoerg }
867330f729Sjoerg
877330f729Sjoerg // Remember how much free space is now left in this block
887330f729Sjoerg FreeMB.Free =
897330f729Sjoerg sys::MemoryBlock((void *)(Addr + Size), EndOfBlock - Addr - Size);
907330f729Sjoerg return (uint8_t *)Addr;
917330f729Sjoerg }
927330f729Sjoerg }
937330f729Sjoerg
947330f729Sjoerg // No pre-allocated free block was large enough. Allocate a new memory region.
957330f729Sjoerg // Note that all sections get allocated as read-write. The permissions will
967330f729Sjoerg // be updated later based on memory group.
977330f729Sjoerg //
987330f729Sjoerg // FIXME: It would be useful to define a default allocation size (or add
997330f729Sjoerg // it as a constructor parameter) to minimize the number of allocations.
1007330f729Sjoerg //
1017330f729Sjoerg // FIXME: Initialize the Near member for each memory group to avoid
1027330f729Sjoerg // interleaving.
1037330f729Sjoerg std::error_code ec;
1047330f729Sjoerg sys::MemoryBlock MB = MMapper.allocateMappedMemory(
1057330f729Sjoerg Purpose, RequiredSize, &MemGroup.Near,
1067330f729Sjoerg sys::Memory::MF_READ | sys::Memory::MF_WRITE, ec);
1077330f729Sjoerg if (ec) {
1087330f729Sjoerg // FIXME: Add error propagation to the interface.
1097330f729Sjoerg return nullptr;
1107330f729Sjoerg }
1117330f729Sjoerg
1127330f729Sjoerg // Save this address as the basis for our next request
1137330f729Sjoerg MemGroup.Near = MB;
1147330f729Sjoerg
115*82d56013Sjoerg // Copy the address to all the other groups, if they have not
116*82d56013Sjoerg // been initialized.
117*82d56013Sjoerg if (CodeMem.Near.base() == 0)
118*82d56013Sjoerg CodeMem.Near = MB;
119*82d56013Sjoerg if (RODataMem.Near.base() == 0)
120*82d56013Sjoerg RODataMem.Near = MB;
121*82d56013Sjoerg if (RWDataMem.Near.base() == 0)
122*82d56013Sjoerg RWDataMem.Near = MB;
123*82d56013Sjoerg
1247330f729Sjoerg // Remember that we allocated this memory
1257330f729Sjoerg MemGroup.AllocatedMem.push_back(MB);
1267330f729Sjoerg Addr = (uintptr_t)MB.base();
1277330f729Sjoerg uintptr_t EndOfBlock = Addr + MB.allocatedSize();
1287330f729Sjoerg
1297330f729Sjoerg // Align the address.
1307330f729Sjoerg Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);
1317330f729Sjoerg
1327330f729Sjoerg // The part of the block we're giving out to the user is now pending
1337330f729Sjoerg MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));
1347330f729Sjoerg
1357330f729Sjoerg // The allocateMappedMemory may allocate much more memory than we need. In
1367330f729Sjoerg // this case, we store the unused memory as a free memory block.
1377330f729Sjoerg unsigned FreeSize = EndOfBlock - Addr - Size;
1387330f729Sjoerg if (FreeSize > 16) {
1397330f729Sjoerg FreeMemBlock FreeMB;
1407330f729Sjoerg FreeMB.Free = sys::MemoryBlock((void *)(Addr + Size), FreeSize);
1417330f729Sjoerg FreeMB.PendingPrefixIndex = (unsigned)-1;
1427330f729Sjoerg MemGroup.FreeMem.push_back(FreeMB);
1437330f729Sjoerg }
1447330f729Sjoerg
1457330f729Sjoerg // Return aligned address
1467330f729Sjoerg return (uint8_t *)Addr;
1477330f729Sjoerg }
1487330f729Sjoerg
finalizeMemory(std::string * ErrMsg)1497330f729Sjoerg bool SectionMemoryManager::finalizeMemory(std::string *ErrMsg) {
1507330f729Sjoerg // FIXME: Should in-progress permissions be reverted if an error occurs?
1517330f729Sjoerg std::error_code ec;
1527330f729Sjoerg
1537330f729Sjoerg // Make code memory executable.
1547330f729Sjoerg ec = applyMemoryGroupPermissions(CodeMem,
1557330f729Sjoerg sys::Memory::MF_READ | sys::Memory::MF_EXEC);
1567330f729Sjoerg if (ec) {
1577330f729Sjoerg if (ErrMsg) {
1587330f729Sjoerg *ErrMsg = ec.message();
1597330f729Sjoerg }
1607330f729Sjoerg return true;
1617330f729Sjoerg }
1627330f729Sjoerg
1637330f729Sjoerg // Make read-only data memory read-only.
164*82d56013Sjoerg ec = applyMemoryGroupPermissions(RODataMem, sys::Memory::MF_READ);
1657330f729Sjoerg if (ec) {
1667330f729Sjoerg if (ErrMsg) {
1677330f729Sjoerg *ErrMsg = ec.message();
1687330f729Sjoerg }
1697330f729Sjoerg return true;
1707330f729Sjoerg }
1717330f729Sjoerg
1727330f729Sjoerg // Read-write data memory already has the correct permissions
1737330f729Sjoerg
1747330f729Sjoerg // Some platforms with separate data cache and instruction cache require
1757330f729Sjoerg // explicit cache flush, otherwise JIT code manipulations (like resolved
1767330f729Sjoerg // relocations) will get to the data cache but not to the instruction cache.
1777330f729Sjoerg invalidateInstructionCache();
1787330f729Sjoerg
1797330f729Sjoerg return false;
1807330f729Sjoerg }
1817330f729Sjoerg
trimBlockToPageSize(sys::MemoryBlock M)1827330f729Sjoerg static sys::MemoryBlock trimBlockToPageSize(sys::MemoryBlock M) {
1837330f729Sjoerg static const size_t PageSize = sys::Process::getPageSizeEstimate();
1847330f729Sjoerg
1857330f729Sjoerg size_t StartOverlap =
1867330f729Sjoerg (PageSize - ((uintptr_t)M.base() % PageSize)) % PageSize;
1877330f729Sjoerg
1887330f729Sjoerg size_t TrimmedSize = M.allocatedSize();
1897330f729Sjoerg TrimmedSize -= StartOverlap;
1907330f729Sjoerg TrimmedSize -= TrimmedSize % PageSize;
1917330f729Sjoerg
1927330f729Sjoerg sys::MemoryBlock Trimmed((void *)((uintptr_t)M.base() + StartOverlap),
1937330f729Sjoerg TrimmedSize);
1947330f729Sjoerg
1957330f729Sjoerg assert(((uintptr_t)Trimmed.base() % PageSize) == 0);
1967330f729Sjoerg assert((Trimmed.allocatedSize() % PageSize) == 0);
1977330f729Sjoerg assert(M.base() <= Trimmed.base() &&
1987330f729Sjoerg Trimmed.allocatedSize() <= M.allocatedSize());
1997330f729Sjoerg
2007330f729Sjoerg return Trimmed;
2017330f729Sjoerg }
2027330f729Sjoerg
2037330f729Sjoerg std::error_code
applyMemoryGroupPermissions(MemoryGroup & MemGroup,unsigned Permissions)2047330f729Sjoerg SectionMemoryManager::applyMemoryGroupPermissions(MemoryGroup &MemGroup,
2057330f729Sjoerg unsigned Permissions) {
2067330f729Sjoerg for (sys::MemoryBlock &MB : MemGroup.PendingMem)
2077330f729Sjoerg if (std::error_code EC = MMapper.protectMappedMemory(MB, Permissions))
2087330f729Sjoerg return EC;
2097330f729Sjoerg
2107330f729Sjoerg MemGroup.PendingMem.clear();
2117330f729Sjoerg
2127330f729Sjoerg // Now go through free blocks and trim any of them that don't span the entire
2137330f729Sjoerg // page because one of the pending blocks may have overlapped it.
2147330f729Sjoerg for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {
2157330f729Sjoerg FreeMB.Free = trimBlockToPageSize(FreeMB.Free);
2167330f729Sjoerg // We cleared the PendingMem list, so all these pointers are now invalid
2177330f729Sjoerg FreeMB.PendingPrefixIndex = (unsigned)-1;
2187330f729Sjoerg }
2197330f729Sjoerg
2207330f729Sjoerg // Remove all blocks which are now empty
221*82d56013Sjoerg erase_if(MemGroup.FreeMem, [](FreeMemBlock &FreeMB) {
2227330f729Sjoerg return FreeMB.Free.allocatedSize() == 0;
223*82d56013Sjoerg });
2247330f729Sjoerg
2257330f729Sjoerg return std::error_code();
2267330f729Sjoerg }
2277330f729Sjoerg
invalidateInstructionCache()2287330f729Sjoerg void SectionMemoryManager::invalidateInstructionCache() {
2297330f729Sjoerg for (sys::MemoryBlock &Block : CodeMem.PendingMem)
2307330f729Sjoerg sys::Memory::InvalidateInstructionCache(Block.base(),
2317330f729Sjoerg Block.allocatedSize());
2327330f729Sjoerg }
2337330f729Sjoerg
~SectionMemoryManager()2347330f729Sjoerg SectionMemoryManager::~SectionMemoryManager() {
2357330f729Sjoerg for (MemoryGroup *Group : {&CodeMem, &RWDataMem, &RODataMem}) {
2367330f729Sjoerg for (sys::MemoryBlock &Block : Group->AllocatedMem)
2377330f729Sjoerg MMapper.releaseMappedMemory(Block);
2387330f729Sjoerg }
2397330f729Sjoerg }
2407330f729Sjoerg
~MemoryMapper()2417330f729Sjoerg SectionMemoryManager::MemoryMapper::~MemoryMapper() {}
2427330f729Sjoerg
anchor()2437330f729Sjoerg void SectionMemoryManager::anchor() {}
2447330f729Sjoerg
2457330f729Sjoerg namespace {
2467330f729Sjoerg // Trivial implementation of SectionMemoryManager::MemoryMapper that just calls
2477330f729Sjoerg // into sys::Memory.
2487330f729Sjoerg class DefaultMMapper final : public SectionMemoryManager::MemoryMapper {
2497330f729Sjoerg public:
2507330f729Sjoerg sys::MemoryBlock
allocateMappedMemory(SectionMemoryManager::AllocationPurpose Purpose,size_t NumBytes,const sys::MemoryBlock * const NearBlock,unsigned Flags,std::error_code & EC)2517330f729Sjoerg allocateMappedMemory(SectionMemoryManager::AllocationPurpose Purpose,
2527330f729Sjoerg size_t NumBytes, const sys::MemoryBlock *const NearBlock,
2537330f729Sjoerg unsigned Flags, std::error_code &EC) override {
2547330f729Sjoerg return sys::Memory::allocateMappedMemory(NumBytes, NearBlock, Flags, EC);
2557330f729Sjoerg }
2567330f729Sjoerg
protectMappedMemory(const sys::MemoryBlock & Block,unsigned Flags)2577330f729Sjoerg std::error_code protectMappedMemory(const sys::MemoryBlock &Block,
2587330f729Sjoerg unsigned Flags) override {
2597330f729Sjoerg return sys::Memory::protectMappedMemory(Block, Flags);
2607330f729Sjoerg }
2617330f729Sjoerg
releaseMappedMemory(sys::MemoryBlock & M)2627330f729Sjoerg std::error_code releaseMappedMemory(sys::MemoryBlock &M) override {
2637330f729Sjoerg return sys::Memory::releaseMappedMemory(M);
2647330f729Sjoerg }
2657330f729Sjoerg };
2667330f729Sjoerg
2677330f729Sjoerg DefaultMMapper DefaultMMapperInstance;
2687330f729Sjoerg } // namespace
2697330f729Sjoerg
SectionMemoryManager(MemoryMapper * MM)2707330f729Sjoerg SectionMemoryManager::SectionMemoryManager(MemoryMapper *MM)
2717330f729Sjoerg : MMapper(MM ? *MM : DefaultMMapperInstance) {}
2727330f729Sjoerg
2737330f729Sjoerg } // namespace llvm
274