194 lines · plain
1//===- Win32/Memory.cpp - Win32 Memory Implementation -----------*- C++ -*-===//2//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-exception6//7//===----------------------------------------------------------------------===//8//9// This file provides the Win32 specific implementation of various Memory10// management utilities11//12//===----------------------------------------------------------------------===//13 14#include "llvm/Support/DataTypes.h"15#include "llvm/Support/ErrorHandling.h"16#include "llvm/Support/Process.h"17#include "llvm/Support/WindowsError.h"18 19// The Windows.h header must be the last one included.20#include "llvm/Support/Windows/WindowsSupport.h"21 22static DWORD getWindowsProtectionFlags(unsigned Flags) {23 switch (Flags & llvm::sys::Memory::MF_RWE_MASK) {24 // Contrary to what you might expect, the Windows page protection flags25 // are not a bitwise combination of RWX values26 case llvm::sys::Memory::MF_READ:27 return PAGE_READONLY;28 case llvm::sys::Memory::MF_WRITE:29 // Note: PAGE_WRITE is not supported by VirtualProtect30 return PAGE_READWRITE;31 case llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_WRITE:32 return PAGE_READWRITE;33 case llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_EXEC:34 return PAGE_EXECUTE_READ;35 case llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_WRITE |36 llvm::sys::Memory::MF_EXEC:37 return PAGE_EXECUTE_READWRITE;38 case llvm::sys::Memory::MF_EXEC:39 return PAGE_EXECUTE;40 default:41 llvm_unreachable("Illegal memory protection flag specified!");42 }43 // Provide a default return value as required by some compilers.44 return PAGE_NOACCESS;45}46 47// While we'd be happy to allocate single pages, the Windows allocation48// granularity may be larger than a single page (in practice, it is 64K)49// so mapping less than that will create an unreachable fragment of memory.50static size_t getAllocationGranularity() {51 SYSTEM_INFO Info;52 ::GetSystemInfo(&Info);53 if (Info.dwPageSize > Info.dwAllocationGranularity)54 return Info.dwPageSize;55 else56 return Info.dwAllocationGranularity;57}58 59// Large/huge memory pages need explicit process permissions in order to be60// used. See https://blogs.msdn.microsoft.com/oldnewthing/20110128-00/?p=1164361// Also large pages need to be manually enabled on your OS. If all this is62// sucessfull, we return the minimal large memory page size.63static size_t enableProcessLargePages() {64 HANDLE Token = 0;65 size_t LargePageMin = GetLargePageMinimum();66 if (LargePageMin)67 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,68 &Token);69 if (!Token)70 return 0;71 LUID Luid;72 if (!LookupPrivilegeValue(0, SE_LOCK_MEMORY_NAME, &Luid)) {73 CloseHandle(Token);74 return 0;75 }76 TOKEN_PRIVILEGES TP{};77 TP.PrivilegeCount = 1;78 TP.Privileges[0].Luid = Luid;79 TP.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;80 if (!AdjustTokenPrivileges(Token, FALSE, &TP, 0, 0, 0)) {81 CloseHandle(Token);82 return 0;83 }84 DWORD E = GetLastError();85 CloseHandle(Token);86 if (E == ERROR_SUCCESS)87 return LargePageMin;88 return 0;89}90 91namespace llvm {92namespace sys {93 94//===----------------------------------------------------------------------===//95//=== WARNING: Implementation here must contain only Win32 specific code96//=== and must not be UNIX code97//===----------------------------------------------------------------------===//98 99MemoryBlock Memory::allocateMappedMemory(size_t NumBytes,100 const MemoryBlock *const NearBlock,101 unsigned Flags, std::error_code &EC) {102 EC = std::error_code();103 if (NumBytes == 0)104 return MemoryBlock();105 106 static size_t DefaultGranularity = getAllocationGranularity();107 static size_t LargePageGranularity = enableProcessLargePages();108 109 DWORD AllocType = MEM_RESERVE | MEM_COMMIT;110 bool HugePages = false;111 size_t Granularity = DefaultGranularity;112 113 if ((Flags & MF_HUGE_HINT) && LargePageGranularity > 0) {114 AllocType |= MEM_LARGE_PAGES;115 HugePages = true;116 Granularity = LargePageGranularity;117 }118 119 size_t NumBlocks = (NumBytes + Granularity - 1) / Granularity;120 121 uintptr_t Start = NearBlock ? reinterpret_cast<uintptr_t>(NearBlock->base()) +122 NearBlock->allocatedSize()123 : 0;124 125 // If the requested address is not aligned to the allocation granularity,126 // round up to get beyond NearBlock. VirtualAlloc would have rounded down.127 if (Start && Start % Granularity != 0)128 Start += Granularity - Start % Granularity;129 130 DWORD Protect = getWindowsProtectionFlags(Flags);131 132 size_t AllocSize = NumBlocks * Granularity;133 void *PA = ::VirtualAlloc(reinterpret_cast<void *>(Start), AllocSize,134 AllocType, Protect);135 if (PA == NULL) {136 if (NearBlock || HugePages) {137 // Try again without the NearBlock hint and without large memory pages138 return allocateMappedMemory(NumBytes, NULL, Flags & ~MF_HUGE_HINT, EC);139 }140 EC = mapWindowsError(::GetLastError());141 return MemoryBlock();142 }143 144 MemoryBlock Result;145 Result.Address = PA;146 Result.AllocatedSize = AllocSize;147 Result.Flags = (Flags & ~MF_HUGE_HINT) | (HugePages ? MF_HUGE_HINT : 0);148 149 if (Flags & MF_EXEC)150 Memory::InvalidateInstructionCache(Result.Address, AllocSize);151 152 return Result;153}154 155std::error_code Memory::releaseMappedMemory(MemoryBlock &M) {156 if (M.Address == 0 || M.AllocatedSize == 0)157 return std::error_code();158 159 if (!VirtualFree(M.Address, 0, MEM_RELEASE))160 return mapWindowsError(::GetLastError());161 162 M.Address = 0;163 M.AllocatedSize = 0;164 165 return std::error_code();166}167 168std::error_code Memory::protectMappedMemory(const MemoryBlock &M,169 unsigned Flags) {170 if (M.Address == 0 || M.AllocatedSize == 0)171 return std::error_code();172 173 DWORD Protect = getWindowsProtectionFlags(Flags);174 175 DWORD OldFlags;176 if (!VirtualProtect(M.Address, M.AllocatedSize, Protect, &OldFlags))177 return mapWindowsError(::GetLastError());178 179 if (Flags & MF_EXEC)180 Memory::InvalidateInstructionCache(M.Address, M.AllocatedSize);181 182 return std::error_code();183}184 185/// InvalidateInstructionCache - Before the JIT can run a block of code186/// that has been emitted it must invalidate the instruction cache on some187/// platforms.188void Memory::InvalidateInstructionCache(const void *Addr, size_t Len) {189 FlushInstructionCache(GetCurrentProcess(), Addr, Len);190}191 192} // namespace sys193} // namespace llvm194