1/* 2 Title: osomem.cpp - Interface to OS memory management - Windows version 3 4 Copyright (c) 2006, 2017-18, 2020 David C.J. Matthews 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License version 2.1 as published by the Free Software Foundation. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19*/ 20 21#ifdef HAVE_CONFIG_H 22#include "config.h" 23#elif defined(_WIN32) 24#include "winconfig.h" 25#else 26#error "No configuration file" 27#endif 28 29#ifdef HAVE_ASSERT_H 30#include <assert.h> 31#define ASSERT(x) assert(x) 32#else 33#define ASSERT(x) 34#endif 35 36#include "osmem.h" 37#include "bitmap.h" 38#include "locking.h" 39 40// Use Windows memory management. 41#include <windows.h> 42 43#ifdef POLYML32IN64 44OSMem::OSMem() 45{ 46 memBase = 0; 47} 48 49OSMem::~OSMem() 50{ 51} 52 53bool OSMem::Initialise(enum _MemUsage usage, size_t space /* = 0 */, void** pBase /* = 0 */) 54{ 55 memUsage = usage; 56 // Get the page size and round up to that multiple. 57 SYSTEM_INFO sysInfo; 58 GetSystemInfo(&sysInfo); 59 // Get the page size. Put it in a size_t variable otherwise the rounding 60 // up of "space" may go wrong on 64-bits. 61 pageSize = sysInfo.dwPageSize; 62 63 memBase = (char*)VirtualAlloc(0, space, MEM_RESERVE, PAGE_NOACCESS); 64 if (memBase == 0) return 0; 65 // We need the heap to be such that the top 32-bits are non-zero. 66 if ((uintptr_t)memBase < ((uintptr_t)1 << 32)) 67 { 68 // Allocate again. 69 void* newSpace = VirtualAlloc(0, space, MEM_RESERVE, PAGE_NOACCESS); 70 VirtualFree(memBase, 0, MEM_RELEASE); // Free the old area that isn't suitable. 71 // Return what we got, or zero if it failed. 72 memBase = (char*)newSpace; 73 } 74 75 if (pBase != 0) *pBase = memBase; 76 77 // Create a bitmap with a bit for each page. 78 if (!pageMap.Create(space / pageSize)) 79 return false; 80 lastAllocated = space / pageSize; // Beyond the last page in the area 81 // Set the last bit in the area so that we don't use it. 82 // This is effectively a work-around for a problem with the heap. 83 // If we have a zero-sized cell at the end of the memory its address is 84 // going to be zero. This causes problems with forwarding pointers. 85 // There may be better ways of doing this. 86 pageMap.SetBit(space / pageSize - 1); 87 return true; 88} 89 90void* OSMem::AllocateDataArea(size_t& space) 91{ 92 char* baseAddr; 93 { 94 PLocker l(&bitmapLock); 95 uintptr_t pages = (space + pageSize - 1) / pageSize; 96 // Round up to an integral number of pages. 97 space = pages * pageSize; 98 // Find some space 99 while (pageMap.TestBit(lastAllocated - 1)) // Skip the wholly allocated area. 100 lastAllocated--; 101 uintptr_t free = pageMap.FindFree(0, lastAllocated, pages); 102 if (free == lastAllocated) 103 return 0; // Can't find the space. 104 pageMap.SetBits(free, pages); 105 // TODO: Do we need to zero this? It may have previously been set. 106 baseAddr = memBase + free * pageSize; 107 } 108 return VirtualAlloc(baseAddr, space, MEM_COMMIT, PAGE_READWRITE); 109} 110 111bool OSMem::FreeDataArea(void* p, size_t space) 112{ 113 char* addr = (char*)p; 114 uintptr_t offset = (addr - memBase) / pageSize; 115 if (!VirtualFree(p, space, MEM_DECOMMIT)) 116 return false; 117 uintptr_t pages = space / pageSize; 118 { 119 PLocker l(&bitmapLock); 120 pageMap.ClearBits(offset, pages); 121 if (offset + pages > lastAllocated) // We allocate from the top down. 122 lastAllocated = offset + pages; 123 } 124 return true; 125} 126 127void* OSMem::AllocateCodeArea(size_t& space, void*& shadowArea) 128{ 129 char* baseAddr; 130 { 131 PLocker l(&bitmapLock); 132 uintptr_t pages = (space + pageSize - 1) / pageSize; 133 // Round up to an integral number of pages. 134 space = pages * pageSize; 135 // Find some space 136 while (pageMap.TestBit(lastAllocated - 1)) // Skip the wholly allocated area. 137 lastAllocated--; 138 uintptr_t free = pageMap.FindFree(0, lastAllocated, pages); 139 if (free == lastAllocated) 140 return 0; // Can't find the space. 141 pageMap.SetBits(free, pages); 142 // TODO: Do we need to zero this? It may have previously been set. 143 baseAddr = memBase + free * pageSize; 144 } 145 146 void* dataArea = 147 VirtualAlloc(baseAddr, space, MEM_COMMIT, memUsage == UsageExecutableCode ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); 148 shadowArea = dataArea; 149 return dataArea; 150} 151 152bool OSMem::FreeCodeArea(void* codeAddr, void* dataAddr, size_t space) 153{ 154 ASSERT(codeAddr == dataAddr); 155 char* addr = (char*)codeAddr; 156 uintptr_t offset = (addr - memBase) / pageSize; 157 if (! VirtualFree(codeAddr, space, MEM_DECOMMIT)) 158 return false; 159 uintptr_t pages = space / pageSize; 160 { 161 PLocker l(&bitmapLock); 162 pageMap.ClearBits(offset, pages); 163 if (offset + pages > lastAllocated) // We allocate from the top down. 164 lastAllocated = offset + pages; 165 } 166 return true; 167} 168 169bool OSMem::EnableWrite(bool enable, void* p, size_t space) 170{ 171 DWORD oldProtect; 172 return VirtualProtect(p, space, enable ? PAGE_READWRITE : PAGE_READONLY, &oldProtect) == TRUE; 173} 174 175bool OSMem::DisableWriteForCode(void* codeAddr, void* dataAddr, size_t space) 176{ 177 ASSERT(codeAddr == dataAddr); 178 DWORD oldProtect; 179 return VirtualProtect(codeAddr, space, 180 memUsage == UsageExecutableCode ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldProtect) == TRUE; 181} 182 183#else 184 185// Native address versions 186OSMem::OSMem() 187{ 188} 189 190OSMem::~OSMem() 191{ 192} 193 194bool OSMem::Initialise(enum _MemUsage usage, size_t space /* = 0 */, void **pBase /* = 0 */) 195{ 196 memUsage = usage; 197 // Get the page size and round up to that multiple. 198 SYSTEM_INFO sysInfo; 199 GetSystemInfo(&sysInfo); 200 // Get the page size. Put it in a size_t variable otherwise the rounding 201 // up of "space" may go wrong on 64-bits. 202 pageSize = sysInfo.dwPageSize; 203 return true; 204} 205 206// Allocate space and return a pointer to it. The size is the minimum 207// size requested and it is updated with the actual space allocated. 208// Returns NULL if it cannot allocate the space. 209void *OSMem::AllocateDataArea(size_t &space) 210{ 211 space = (space + pageSize - 1) & ~(pageSize - 1); 212 DWORD options = MEM_RESERVE | MEM_COMMIT; 213 return VirtualAlloc(0, space, options, PAGE_READWRITE); 214} 215 216// Release the space previously allocated. This must free the whole of 217// the segment. The space must be the size actually allocated. 218bool OSMem::FreeDataArea(void *p, size_t space) 219{ 220 return VirtualFree(p, 0, MEM_RELEASE) == TRUE; 221} 222 223// Adjust the permissions on a segment. This must apply to the 224// whole of a segment. 225bool OSMem::EnableWrite(bool enable, void* p, size_t space) 226{ 227 DWORD oldProtect; 228 return VirtualProtect(p, space, enable ? PAGE_READWRITE: PAGE_READONLY, &oldProtect) == TRUE; 229} 230 231void* OSMem::AllocateCodeArea(size_t& space, void*& shadowArea) 232{ 233 space = (space + pageSize - 1) & ~(pageSize - 1); 234 DWORD options = MEM_RESERVE | MEM_COMMIT; 235 void * dataAddr = VirtualAlloc(0, space, options, 236 memUsage == UsageExecutableCode ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); 237 shadowArea = dataAddr; 238 return dataAddr; 239} 240 241bool OSMem::FreeCodeArea(void* codeAddr, void* dataAddr, size_t space) 242{ 243 ASSERT(codeAddr == dataAddr); 244 return VirtualFree(codeAddr, 0, MEM_RELEASE) == TRUE; 245} 246 247bool OSMem::DisableWriteForCode(void* codeAddr, void* dataAddr, size_t space) 248{ 249 ASSERT(codeAddr == dataAddr); 250 DWORD oldProtect; 251 return VirtualProtect(codeAddr, space, 252 memUsage == UsageExecutableCode ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldProtect) == TRUE; 253} 254 255#endif 256 257