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