Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/SectionMemoryManager.cpp
35232 views
//===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++ -*-==//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the section-based memory manager used by the MCJIT9// execution engine and RuntimeDyld10//11//===----------------------------------------------------------------------===//1213#include "llvm/ExecutionEngine/SectionMemoryManager.h"14#include "llvm/Config/config.h"15#include "llvm/Support/MathExtras.h"16#include "llvm/Support/Process.h"1718namespace llvm {1920uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size,21unsigned Alignment,22unsigned SectionID,23StringRef SectionName,24bool IsReadOnly) {25if (IsReadOnly)26return allocateSection(SectionMemoryManager::AllocationPurpose::ROData,27Size, Alignment);28return allocateSection(SectionMemoryManager::AllocationPurpose::RWData, Size,29Alignment);30}3132uint8_t *SectionMemoryManager::allocateCodeSection(uintptr_t Size,33unsigned Alignment,34unsigned SectionID,35StringRef SectionName) {36return allocateSection(SectionMemoryManager::AllocationPurpose::Code, Size,37Alignment);38}3940uint8_t *SectionMemoryManager::allocateSection(41SectionMemoryManager::AllocationPurpose Purpose, uintptr_t Size,42unsigned Alignment) {43if (!Alignment)44Alignment = 16;4546assert(!(Alignment & (Alignment - 1)) && "Alignment must be a power of two.");4748uintptr_t RequiredSize = Alignment * ((Size + Alignment - 1) / Alignment + 1);49uintptr_t Addr = 0;5051MemoryGroup &MemGroup = [&]() -> MemoryGroup & {52switch (Purpose) {53case AllocationPurpose::Code:54return CodeMem;55case AllocationPurpose::ROData:56return RODataMem;57case AllocationPurpose::RWData:58return RWDataMem;59}60llvm_unreachable("Unknown SectionMemoryManager::AllocationPurpose");61}();6263// Look in the list of free memory regions and use a block there if one64// is available.65for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {66if (FreeMB.Free.allocatedSize() >= RequiredSize) {67Addr = (uintptr_t)FreeMB.Free.base();68uintptr_t EndOfBlock = Addr + FreeMB.Free.allocatedSize();69// Align the address.70Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);7172if (FreeMB.PendingPrefixIndex == (unsigned)-1) {73// The part of the block we're giving out to the user is now pending74MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));7576// Remember this pending block, such that future allocations can just77// modify it rather than creating a new one78FreeMB.PendingPrefixIndex = MemGroup.PendingMem.size() - 1;79} else {80sys::MemoryBlock &PendingMB =81MemGroup.PendingMem[FreeMB.PendingPrefixIndex];82PendingMB = sys::MemoryBlock(PendingMB.base(),83Addr + Size - (uintptr_t)PendingMB.base());84}8586// Remember how much free space is now left in this block87FreeMB.Free =88sys::MemoryBlock((void *)(Addr + Size), EndOfBlock - Addr - Size);89return (uint8_t *)Addr;90}91}9293// No pre-allocated free block was large enough. Allocate a new memory region.94// Note that all sections get allocated as read-write. The permissions will95// be updated later based on memory group.96//97// FIXME: It would be useful to define a default allocation size (or add98// it as a constructor parameter) to minimize the number of allocations.99//100// FIXME: Initialize the Near member for each memory group to avoid101// interleaving.102std::error_code ec;103sys::MemoryBlock MB = MMapper->allocateMappedMemory(104Purpose, RequiredSize, &MemGroup.Near,105sys::Memory::MF_READ | sys::Memory::MF_WRITE, ec);106if (ec) {107// FIXME: Add error propagation to the interface.108return nullptr;109}110111// Save this address as the basis for our next request112MemGroup.Near = MB;113114// Copy the address to all the other groups, if they have not115// been initialized.116if (CodeMem.Near.base() == nullptr)117CodeMem.Near = MB;118if (RODataMem.Near.base() == nullptr)119RODataMem.Near = MB;120if (RWDataMem.Near.base() == nullptr)121RWDataMem.Near = MB;122123// Remember that we allocated this memory124MemGroup.AllocatedMem.push_back(MB);125Addr = (uintptr_t)MB.base();126uintptr_t EndOfBlock = Addr + MB.allocatedSize();127128// Align the address.129Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);130131// The part of the block we're giving out to the user is now pending132MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size));133134// The allocateMappedMemory may allocate much more memory than we need. In135// this case, we store the unused memory as a free memory block.136unsigned FreeSize = EndOfBlock - Addr - Size;137if (FreeSize > 16) {138FreeMemBlock FreeMB;139FreeMB.Free = sys::MemoryBlock((void *)(Addr + Size), FreeSize);140FreeMB.PendingPrefixIndex = (unsigned)-1;141MemGroup.FreeMem.push_back(FreeMB);142}143144// Return aligned address145return (uint8_t *)Addr;146}147148bool SectionMemoryManager::finalizeMemory(std::string *ErrMsg) {149// FIXME: Should in-progress permissions be reverted if an error occurs?150std::error_code ec;151152// Make code memory executable.153ec = applyMemoryGroupPermissions(CodeMem,154sys::Memory::MF_READ | sys::Memory::MF_EXEC);155if (ec) {156if (ErrMsg) {157*ErrMsg = ec.message();158}159return true;160}161162// Make read-only data memory read-only.163ec = applyMemoryGroupPermissions(RODataMem, sys::Memory::MF_READ);164if (ec) {165if (ErrMsg) {166*ErrMsg = ec.message();167}168return true;169}170171// Read-write data memory already has the correct permissions172173// Some platforms with separate data cache and instruction cache require174// explicit cache flush, otherwise JIT code manipulations (like resolved175// relocations) will get to the data cache but not to the instruction cache.176invalidateInstructionCache();177178return false;179}180181static sys::MemoryBlock trimBlockToPageSize(sys::MemoryBlock M) {182static const size_t PageSize = sys::Process::getPageSizeEstimate();183184size_t StartOverlap =185(PageSize - ((uintptr_t)M.base() % PageSize)) % PageSize;186187size_t TrimmedSize = M.allocatedSize();188TrimmedSize -= StartOverlap;189TrimmedSize -= TrimmedSize % PageSize;190191sys::MemoryBlock Trimmed((void *)((uintptr_t)M.base() + StartOverlap),192TrimmedSize);193194assert(((uintptr_t)Trimmed.base() % PageSize) == 0);195assert((Trimmed.allocatedSize() % PageSize) == 0);196assert(M.base() <= Trimmed.base() &&197Trimmed.allocatedSize() <= M.allocatedSize());198199return Trimmed;200}201202std::error_code203SectionMemoryManager::applyMemoryGroupPermissions(MemoryGroup &MemGroup,204unsigned Permissions) {205for (sys::MemoryBlock &MB : MemGroup.PendingMem)206if (std::error_code EC = MMapper->protectMappedMemory(MB, Permissions))207return EC;208209MemGroup.PendingMem.clear();210211// Now go through free blocks and trim any of them that don't span the entire212// page because one of the pending blocks may have overlapped it.213for (FreeMemBlock &FreeMB : MemGroup.FreeMem) {214FreeMB.Free = trimBlockToPageSize(FreeMB.Free);215// We cleared the PendingMem list, so all these pointers are now invalid216FreeMB.PendingPrefixIndex = (unsigned)-1;217}218219// Remove all blocks which are now empty220erase_if(MemGroup.FreeMem, [](FreeMemBlock &FreeMB) {221return FreeMB.Free.allocatedSize() == 0;222});223224return std::error_code();225}226227void SectionMemoryManager::invalidateInstructionCache() {228for (sys::MemoryBlock &Block : CodeMem.PendingMem)229sys::Memory::InvalidateInstructionCache(Block.base(),230Block.allocatedSize());231}232233SectionMemoryManager::~SectionMemoryManager() {234for (MemoryGroup *Group : {&CodeMem, &RWDataMem, &RODataMem}) {235for (sys::MemoryBlock &Block : Group->AllocatedMem)236MMapper->releaseMappedMemory(Block);237}238}239240SectionMemoryManager::MemoryMapper::~MemoryMapper() = default;241242void SectionMemoryManager::anchor() {}243244namespace {245// Trivial implementation of SectionMemoryManager::MemoryMapper that just calls246// into sys::Memory.247class DefaultMMapper final : public SectionMemoryManager::MemoryMapper {248public:249sys::MemoryBlock250allocateMappedMemory(SectionMemoryManager::AllocationPurpose Purpose,251size_t NumBytes, const sys::MemoryBlock *const NearBlock,252unsigned Flags, std::error_code &EC) override {253return sys::Memory::allocateMappedMemory(NumBytes, NearBlock, Flags, EC);254}255256std::error_code protectMappedMemory(const sys::MemoryBlock &Block,257unsigned Flags) override {258return sys::Memory::protectMappedMemory(Block, Flags);259}260261std::error_code releaseMappedMemory(sys::MemoryBlock &M) override {262return sys::Memory::releaseMappedMemory(M);263}264};265} // namespace266267SectionMemoryManager::SectionMemoryManager(MemoryMapper *UnownedMM)268: MMapper(UnownedMM), OwnedMMapper(nullptr) {269if (!MMapper) {270OwnedMMapper = std::make_unique<DefaultMMapper>();271MMapper = OwnedMMapper.get();272}273}274275} // namespace llvm276277278