1 //===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++ -*-==//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file implements the section-based memory manager used by the MCJIT
10 // execution engine and RuntimeDyld
12 //===----------------------------------------------------------------------===//
14 #include "llvm/ExecutionEngine/SectionMemoryManager.h"
15 #include "llvm/Config/config.h"
16 #include "llvm/Support/Process.h"
20 uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size
,
23 StringRef SectionName
,
26 return allocateSection(SectionMemoryManager::AllocationPurpose::ROData
,
28 return allocateSection(SectionMemoryManager::AllocationPurpose::RWData
, Size
,
32 uint8_t *SectionMemoryManager::allocateCodeSection(uintptr_t Size
,
35 StringRef SectionName
) {
36 return allocateSection(SectionMemoryManager::AllocationPurpose::Code
, Size
,
40 uint8_t *SectionMemoryManager::allocateSection(
41 SectionMemoryManager::AllocationPurpose Purpose
, uintptr_t Size
,
46 assert(!(Alignment
& (Alignment
- 1)) && "Alignment must be a power of two.");
48 uintptr_t RequiredSize
= Alignment
* ((Size
+ Alignment
- 1) / Alignment
+ 1);
51 MemoryGroup
&MemGroup
= [&]() -> MemoryGroup
& {
53 case AllocationPurpose::Code
:
55 case AllocationPurpose::ROData
:
57 case AllocationPurpose::RWData
:
60 llvm_unreachable("Unknown SectionMemoryManager::AllocationPurpose");
63 // Look in the list of free memory regions and use a block there if one
65 for (FreeMemBlock
&FreeMB
: MemGroup
.FreeMem
) {
66 if (FreeMB
.Free
.allocatedSize() >= RequiredSize
) {
67 Addr
= (uintptr_t)FreeMB
.Free
.base();
68 uintptr_t EndOfBlock
= Addr
+ FreeMB
.Free
.allocatedSize();
70 Addr
= (Addr
+ Alignment
- 1) & ~(uintptr_t)(Alignment
- 1);
72 if (FreeMB
.PendingPrefixIndex
== (unsigned)-1) {
73 // The part of the block we're giving out to the user is now pending
74 MemGroup
.PendingMem
.push_back(sys::MemoryBlock((void *)Addr
, Size
));
76 // Remember this pending block, such that future allocations can just
77 // modify it rather than creating a new one
78 FreeMB
.PendingPrefixIndex
= MemGroup
.PendingMem
.size() - 1;
80 sys::MemoryBlock
&PendingMB
=
81 MemGroup
.PendingMem
[FreeMB
.PendingPrefixIndex
];
82 PendingMB
= sys::MemoryBlock(PendingMB
.base(),
83 Addr
+ Size
- (uintptr_t)PendingMB
.base());
86 // Remember how much free space is now left in this block
88 sys::MemoryBlock((void *)(Addr
+ Size
), EndOfBlock
- Addr
- Size
);
89 return (uint8_t *)Addr
;
93 // 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 will
95 // be updated later based on memory group.
97 // FIXME: It would be useful to define a default allocation size (or add
98 // it as a constructor parameter) to minimize the number of allocations.
100 // FIXME: Initialize the Near member for each memory group to avoid
103 sys::MemoryBlock MB
= MMapper
->allocateMappedMemory(
104 Purpose
, RequiredSize
, &MemGroup
.Near
,
105 sys::Memory::MF_READ
| sys::Memory::MF_WRITE
, ec
);
107 // FIXME: Add error propagation to the interface.
111 // Save this address as the basis for our next request
114 // Copy the address to all the other groups, if they have not
116 if (CodeMem
.Near
.base() == nullptr)
118 if (RODataMem
.Near
.base() == nullptr)
120 if (RWDataMem
.Near
.base() == nullptr)
123 // Remember that we allocated this memory
124 MemGroup
.AllocatedMem
.push_back(MB
);
125 Addr
= (uintptr_t)MB
.base();
126 uintptr_t EndOfBlock
= Addr
+ MB
.allocatedSize();
128 // Align the address.
129 Addr
= (Addr
+ Alignment
- 1) & ~(uintptr_t)(Alignment
- 1);
131 // The part of the block we're giving out to the user is now pending
132 MemGroup
.PendingMem
.push_back(sys::MemoryBlock((void *)Addr
, Size
));
134 // The allocateMappedMemory may allocate much more memory than we need. In
135 // this case, we store the unused memory as a free memory block.
136 unsigned FreeSize
= EndOfBlock
- Addr
- Size
;
139 FreeMB
.Free
= sys::MemoryBlock((void *)(Addr
+ Size
), FreeSize
);
140 FreeMB
.PendingPrefixIndex
= (unsigned)-1;
141 MemGroup
.FreeMem
.push_back(FreeMB
);
144 // Return aligned address
145 return (uint8_t *)Addr
;
148 bool SectionMemoryManager::finalizeMemory(std::string
*ErrMsg
) {
149 // FIXME: Should in-progress permissions be reverted if an error occurs?
152 // Make code memory executable.
153 ec
= applyMemoryGroupPermissions(CodeMem
,
154 sys::Memory::MF_READ
| sys::Memory::MF_EXEC
);
157 *ErrMsg
= ec
.message();
162 // Make read-only data memory read-only.
163 ec
= applyMemoryGroupPermissions(RODataMem
, sys::Memory::MF_READ
);
166 *ErrMsg
= ec
.message();
171 // Read-write data memory already has the correct permissions
173 // Some platforms with separate data cache and instruction cache require
174 // explicit cache flush, otherwise JIT code manipulations (like resolved
175 // relocations) will get to the data cache but not to the instruction cache.
176 invalidateInstructionCache();
181 static sys::MemoryBlock
trimBlockToPageSize(sys::MemoryBlock M
) {
182 static const size_t PageSize
= sys::Process::getPageSizeEstimate();
184 size_t StartOverlap
=
185 (PageSize
- ((uintptr_t)M
.base() % PageSize
)) % PageSize
;
187 size_t TrimmedSize
= M
.allocatedSize();
188 TrimmedSize
-= StartOverlap
;
189 TrimmedSize
-= TrimmedSize
% PageSize
;
191 sys::MemoryBlock
Trimmed((void *)((uintptr_t)M
.base() + StartOverlap
),
194 assert(((uintptr_t)Trimmed
.base() % PageSize
) == 0);
195 assert((Trimmed
.allocatedSize() % PageSize
) == 0);
196 assert(M
.base() <= Trimmed
.base() &&
197 Trimmed
.allocatedSize() <= M
.allocatedSize());
203 SectionMemoryManager::applyMemoryGroupPermissions(MemoryGroup
&MemGroup
,
204 unsigned Permissions
) {
205 for (sys::MemoryBlock
&MB
: MemGroup
.PendingMem
)
206 if (std::error_code EC
= MMapper
->protectMappedMemory(MB
, Permissions
))
209 MemGroup
.PendingMem
.clear();
211 // Now go through free blocks and trim any of them that don't span the entire
212 // page because one of the pending blocks may have overlapped it.
213 for (FreeMemBlock
&FreeMB
: MemGroup
.FreeMem
) {
214 FreeMB
.Free
= trimBlockToPageSize(FreeMB
.Free
);
215 // We cleared the PendingMem list, so all these pointers are now invalid
216 FreeMB
.PendingPrefixIndex
= (unsigned)-1;
219 // Remove all blocks which are now empty
220 erase_if(MemGroup
.FreeMem
, [](FreeMemBlock
&FreeMB
) {
221 return FreeMB
.Free
.allocatedSize() == 0;
224 return std::error_code();
227 void SectionMemoryManager::invalidateInstructionCache() {
228 for (sys::MemoryBlock
&Block
: CodeMem
.PendingMem
)
229 sys::Memory::InvalidateInstructionCache(Block
.base(),
230 Block
.allocatedSize());
233 SectionMemoryManager::~SectionMemoryManager() {
234 for (MemoryGroup
*Group
: {&CodeMem
, &RWDataMem
, &RODataMem
}) {
235 for (sys::MemoryBlock
&Block
: Group
->AllocatedMem
)
236 MMapper
->releaseMappedMemory(Block
);
240 SectionMemoryManager::MemoryMapper::~MemoryMapper() = default;
242 void SectionMemoryManager::anchor() {}
245 // Trivial implementation of SectionMemoryManager::MemoryMapper that just calls
247 class DefaultMMapper final
: public SectionMemoryManager::MemoryMapper
{
250 allocateMappedMemory(SectionMemoryManager::AllocationPurpose Purpose
,
251 size_t NumBytes
, const sys::MemoryBlock
*const NearBlock
,
252 unsigned Flags
, std::error_code
&EC
) override
{
253 return sys::Memory::allocateMappedMemory(NumBytes
, NearBlock
, Flags
, EC
);
256 std::error_code
protectMappedMemory(const sys::MemoryBlock
&Block
,
257 unsigned Flags
) override
{
258 return sys::Memory::protectMappedMemory(Block
, Flags
);
261 std::error_code
releaseMappedMemory(sys::MemoryBlock
&M
) override
{
262 return sys::Memory::releaseMappedMemory(M
);
267 SectionMemoryManager::SectionMemoryManager(MemoryMapper
*UnownedMM
)
268 : MMapper(UnownedMM
), OwnedMMapper(nullptr) {
270 OwnedMMapper
= std::make_unique
<DefaultMMapper
>();
271 MMapper
= OwnedMMapper
.get();