1 //===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++ -*-==// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file implements the section-based memory manager used by the MCJIT 11 // execution engine and RuntimeDyld 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "llvm/Config/config.h" 16 #include "llvm/ExecutionEngine/SectionMemoryManager.h" 17 #include "llvm/Support/MathExtras.h" 18 #include "llvm/Support/Process.h" 19 20 namespace llvm { 21 22 uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size, 23 unsigned Alignment, 24 unsigned SectionID, 25 StringRef SectionName, 26 bool IsReadOnly) { 27 if (IsReadOnly) 28 return allocateSection(RODataMem, Size, Alignment); 29 return allocateSection(RWDataMem, Size, Alignment); 30 } 31 32 uint8_t *SectionMemoryManager::allocateCodeSection(uintptr_t Size, 33 unsigned Alignment, 34 unsigned SectionID, 35 StringRef SectionName) { 36 return allocateSection(CodeMem, Size, Alignment); 37 } 38 39 uint8_t *SectionMemoryManager::allocateSection(MemoryGroup &MemGroup, 40 uintptr_t Size, 41 unsigned Alignment) { 42 if (!Alignment) 43 Alignment = 16; 44 45 assert(!(Alignment & (Alignment - 1)) && "Alignment must be a power of two."); 46 47 uintptr_t RequiredSize = Alignment * ((Size + Alignment - 1)/Alignment + 1); 48 uintptr_t Addr = 0; 49 50 // Look in the list of free memory regions and use a block there if one 51 // is available. 52 for (FreeMemBlock &FreeMB : MemGroup.FreeMem) { 53 if (FreeMB.Free.size() >= RequiredSize) { 54 Addr = (uintptr_t)FreeMB.Free.base(); 55 uintptr_t EndOfBlock = Addr + FreeMB.Free.size(); 56 // Align the address. 57 Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1); 58 59 if (FreeMB.PendingPrefixIndex == (unsigned)-1) { 60 // The part of the block we're giving out to the user is now pending 61 MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size)); 62 63 // Remember this pending block, such that future allocations can just 64 // modify it rather than creating a new one 65 FreeMB.PendingPrefixIndex = MemGroup.PendingMem.size() - 1; 66 } else { 67 sys::MemoryBlock &PendingMB = MemGroup.PendingMem[FreeMB.PendingPrefixIndex]; 68 PendingMB = sys::MemoryBlock(PendingMB.base(), Addr + Size - (uintptr_t)PendingMB.base()); 69 } 70 71 // Remember how much free space is now left in this block 72 FreeMB.Free = sys::MemoryBlock((void *)(Addr + Size), EndOfBlock - Addr - Size); 73 return (uint8_t*)Addr; 74 } 75 } 76 77 // No pre-allocated free block was large enough. Allocate a new memory region. 78 // Note that all sections get allocated as read-write. The permissions will 79 // be updated later based on memory group. 80 // 81 // FIXME: It would be useful to define a default allocation size (or add 82 // it as a constructor parameter) to minimize the number of allocations. 83 // 84 // FIXME: Initialize the Near member for each memory group to avoid 85 // interleaving. 86 std::error_code ec; 87 sys::MemoryBlock MB = sys::Memory::allocateMappedMemory(RequiredSize, 88 &MemGroup.Near, 89 sys::Memory::MF_READ | 90 sys::Memory::MF_WRITE, 91 ec); 92 if (ec) { 93 // FIXME: Add error propagation to the interface. 94 return nullptr; 95 } 96 97 // Save this address as the basis for our next request 98 MemGroup.Near = MB; 99 100 // Remember that we allocated this memory 101 MemGroup.AllocatedMem.push_back(MB); 102 Addr = (uintptr_t)MB.base(); 103 uintptr_t EndOfBlock = Addr + MB.size(); 104 105 // Align the address. 106 Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1); 107 108 // The part of the block we're giving out to the user is now pending 109 MemGroup.PendingMem.push_back(sys::MemoryBlock((void *)Addr, Size)); 110 111 // The allocateMappedMemory may allocate much more memory than we need. In 112 // this case, we store the unused memory as a free memory block. 113 unsigned FreeSize = EndOfBlock-Addr-Size; 114 if (FreeSize > 16) { 115 FreeMemBlock FreeMB; 116 FreeMB.Free = sys::MemoryBlock((void*)(Addr + Size), FreeSize); 117 FreeMB.PendingPrefixIndex = (unsigned)-1; 118 MemGroup.FreeMem.push_back(FreeMB); 119 } 120 121 // Return aligned address 122 return (uint8_t*)Addr; 123 } 124 125 bool SectionMemoryManager::finalizeMemory(std::string *ErrMsg) 126 { 127 // FIXME: Should in-progress permissions be reverted if an error occurs? 128 std::error_code ec; 129 130 // Make code memory executable. 131 ec = applyMemoryGroupPermissions(CodeMem, 132 sys::Memory::MF_READ | sys::Memory::MF_EXEC); 133 if (ec) { 134 if (ErrMsg) { 135 *ErrMsg = ec.message(); 136 } 137 return true; 138 } 139 140 // Make read-only data memory read-only. 141 ec = applyMemoryGroupPermissions(RODataMem, 142 sys::Memory::MF_READ | sys::Memory::MF_EXEC); 143 if (ec) { 144 if (ErrMsg) { 145 *ErrMsg = ec.message(); 146 } 147 return true; 148 } 149 150 // Read-write data memory already has the correct permissions 151 152 // Some platforms with separate data cache and instruction cache require 153 // explicit cache flush, otherwise JIT code manipulations (like resolved 154 // relocations) will get to the data cache but not to the instruction cache. 155 invalidateInstructionCache(); 156 157 return false; 158 } 159 160 static sys::MemoryBlock trimBlockToPageSize(sys::MemoryBlock M) { 161 static const size_t PageSize = sys::Process::getPageSize(); 162 163 size_t StartOverlap = 164 (PageSize - ((uintptr_t)M.base() % PageSize)) % PageSize; 165 166 size_t TrimmedSize = M.size(); 167 TrimmedSize -= StartOverlap; 168 TrimmedSize -= TrimmedSize % PageSize; 169 170 sys::MemoryBlock Trimmed((void *)((uintptr_t)M.base() + StartOverlap), TrimmedSize); 171 172 assert(((uintptr_t)Trimmed.base() % PageSize) == 0); 173 assert((Trimmed.size() % PageSize) == 0); 174 assert(M.base() <= Trimmed.base() && Trimmed.size() <= M.size()); 175 176 return Trimmed; 177 } 178 179 180 std::error_code 181 SectionMemoryManager::applyMemoryGroupPermissions(MemoryGroup &MemGroup, 182 unsigned Permissions) { 183 for (sys::MemoryBlock &MB : MemGroup.PendingMem) 184 if (std::error_code EC = sys::Memory::protectMappedMemory(MB, Permissions)) 185 return EC; 186 187 MemGroup.PendingMem.clear(); 188 189 // Now go through free blocks and trim any of them that don't span the entire 190 // page because one of the pending blocks may have overlapped it. 191 for (FreeMemBlock &FreeMB : MemGroup.FreeMem) { 192 FreeMB.Free = trimBlockToPageSize(FreeMB.Free); 193 // We cleared the PendingMem list, so all these pointers are now invalid 194 FreeMB.PendingPrefixIndex = (unsigned)-1; 195 } 196 197 // Remove all blocks which are now empty 198 MemGroup.FreeMem.erase( 199 std::remove_if(MemGroup.FreeMem.begin(), MemGroup.FreeMem.end(), 200 [](FreeMemBlock &FreeMB) { return FreeMB.Free.size() == 0; }), 201 MemGroup.FreeMem.end()); 202 203 return std::error_code(); 204 } 205 206 void SectionMemoryManager::invalidateInstructionCache() { 207 for (sys::MemoryBlock &Block : CodeMem.PendingMem) 208 sys::Memory::InvalidateInstructionCache(Block.base(), Block.size()); 209 } 210 211 SectionMemoryManager::~SectionMemoryManager() { 212 for (MemoryGroup *Group : {&CodeMem, &RWDataMem, &RODataMem}) { 213 for (sys::MemoryBlock &Block : Group->AllocatedMem) 214 sys::Memory::releaseMappedMemory(Block); 215 } 216 } 217 218 } // namespace llvm 219