1218885Sdim//===- Win32/Memory.cpp - Win32 Memory Implementation -----------*- C++ -*-===//
2218885Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6218885Sdim//
7218885Sdim//===----------------------------------------------------------------------===//
8218885Sdim//
9218885Sdim// This file provides the Win32 specific implementation of various Memory
10218885Sdim// management utilities
11218885Sdim//
12218885Sdim//===----------------------------------------------------------------------===//
13218885Sdim
14218885Sdim#include "llvm/Support/DataTypes.h"
15243830Sdim#include "llvm/Support/ErrorHandling.h"
16218885Sdim#include "llvm/Support/Process.h"
17276479Sdim#include "llvm/Support/WindowsError.h"
18249423Sdim
19249423Sdim// The Windows.h header must be the last one included.
20360784Sdim#include "llvm/Support/Windows/WindowsSupport.h"
21218885Sdim
22360784Sdimstatic DWORD getWindowsProtectionFlags(unsigned Flags) {
23353358Sdim  switch (Flags & llvm::sys::Memory::MF_RWE_MASK) {
24243830Sdim  // Contrary to what you might expect, the Windows page protection flags
25243830Sdim  // are not a bitwise combination of RWX values
26243830Sdim  case llvm::sys::Memory::MF_READ:
27243830Sdim    return PAGE_READONLY;
28243830Sdim  case llvm::sys::Memory::MF_WRITE:
29243830Sdim    // Note: PAGE_WRITE is not supported by VirtualProtect
30243830Sdim    return PAGE_READWRITE;
31243830Sdim  case llvm::sys::Memory::MF_READ|llvm::sys::Memory::MF_WRITE:
32243830Sdim    return PAGE_READWRITE;
33243830Sdim  case llvm::sys::Memory::MF_READ|llvm::sys::Memory::MF_EXEC:
34243830Sdim    return PAGE_EXECUTE_READ;
35243830Sdim  case llvm::sys::Memory::MF_READ |
36243830Sdim         llvm::sys::Memory::MF_WRITE |
37243830Sdim         llvm::sys::Memory::MF_EXEC:
38243830Sdim    return PAGE_EXECUTE_READWRITE;
39243830Sdim  case llvm::sys::Memory::MF_EXEC:
40243830Sdim    return PAGE_EXECUTE;
41243830Sdim  default:
42243830Sdim    llvm_unreachable("Illegal memory protection flag specified!");
43243830Sdim  }
44243830Sdim  // Provide a default return value as required by some compilers.
45243830Sdim  return PAGE_NOACCESS;
46243830Sdim}
47243830Sdim
48353358Sdim// While we'd be happy to allocate single pages, the Windows allocation
49353358Sdim// granularity may be larger than a single page (in practice, it is 64K)
50353358Sdim// so mapping less than that will create an unreachable fragment of memory.
51360784Sdimstatic size_t getAllocationGranularity() {
52243830Sdim  SYSTEM_INFO  Info;
53243830Sdim  ::GetSystemInfo(&Info);
54243830Sdim  if (Info.dwPageSize > Info.dwAllocationGranularity)
55243830Sdim    return Info.dwPageSize;
56243830Sdim  else
57243830Sdim    return Info.dwAllocationGranularity;
58243830Sdim}
59243830Sdim
60353358Sdim// Large/huge memory pages need explicit process permissions in order to be
61353358Sdim// used. See https://blogs.msdn.microsoft.com/oldnewthing/20110128-00/?p=11643
62353358Sdim// Also large pages need to be manually enabled on your OS. If all this is
63353358Sdim// sucessfull, we return the minimal large memory page size.
64353358Sdimstatic size_t enableProcessLargePages() {
65353358Sdim  HANDLE Token = 0;
66353358Sdim  size_t LargePageMin = GetLargePageMinimum();
67353358Sdim  if (LargePageMin)
68353358Sdim    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
69353358Sdim                     &Token);
70353358Sdim  if (!Token)
71353358Sdim    return 0;
72353358Sdim  LUID Luid;
73353358Sdim  if (!LookupPrivilegeValue(0, SE_LOCK_MEMORY_NAME, &Luid)) {
74353358Sdim    CloseHandle(Token);
75353358Sdim    return 0;
76353358Sdim  }
77353358Sdim  TOKEN_PRIVILEGES TP{};
78353358Sdim  TP.PrivilegeCount = 1;
79353358Sdim  TP.Privileges[0].Luid = Luid;
80353358Sdim  TP.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
81353358Sdim  if (!AdjustTokenPrivileges(Token, FALSE, &TP, 0, 0, 0)) {
82353358Sdim    CloseHandle(Token);
83353358Sdim    return 0;
84353358Sdim  }
85353358Sdim  DWORD E = GetLastError();
86353358Sdim  CloseHandle(Token);
87353358Sdim  if (E == ERROR_SUCCESS)
88353358Sdim    return LargePageMin;
89353358Sdim  return 0;
90353358Sdim}
91353358Sdim
92218885Sdimnamespace llvm {
93243830Sdimnamespace sys {
94218885Sdim
95218885Sdim//===----------------------------------------------------------------------===//
96218885Sdim//=== WARNING: Implementation here must contain only Win32 specific code
97218885Sdim//===          and must not be UNIX code
98218885Sdim//===----------------------------------------------------------------------===//
99218885Sdim
100243830SdimMemoryBlock Memory::allocateMappedMemory(size_t NumBytes,
101243830Sdim                                         const MemoryBlock *const NearBlock,
102243830Sdim                                         unsigned Flags,
103276479Sdim                                         std::error_code &EC) {
104276479Sdim  EC = std::error_code();
105243830Sdim  if (NumBytes == 0)
106243830Sdim    return MemoryBlock();
107218885Sdim
108353358Sdim  static size_t DefaultGranularity = getAllocationGranularity();
109353358Sdim  static size_t LargePageGranularity = enableProcessLargePages();
110353358Sdim
111353358Sdim  DWORD AllocType = MEM_RESERVE | MEM_COMMIT;
112353358Sdim  bool HugePages = false;
113353358Sdim  size_t Granularity = DefaultGranularity;
114353358Sdim
115353358Sdim  if ((Flags & MF_HUGE_HINT) && LargePageGranularity > 0) {
116353358Sdim    AllocType |= MEM_LARGE_PAGES;
117353358Sdim    HugePages = true;
118353358Sdim    Granularity = LargePageGranularity;
119288943Sdim  }
120288943Sdim
121353358Sdim  size_t NumBlocks = (NumBytes + Granularity - 1) / Granularity;
122218885Sdim
123243830Sdim  uintptr_t Start = NearBlock ? reinterpret_cast<uintptr_t>(NearBlock->base()) +
124353358Sdim                                NearBlock->allocatedSize()
125261991Sdim                           : 0;
126218885Sdim
127243830Sdim  // If the requested address is not aligned to the allocation granularity,
128243830Sdim  // round up to get beyond NearBlock. VirtualAlloc would have rounded down.
129243830Sdim  if (Start && Start % Granularity != 0)
130243830Sdim    Start += Granularity - Start % Granularity;
131243830Sdim
132243830Sdim  DWORD Protect = getWindowsProtectionFlags(Flags);
133243830Sdim
134353358Sdim  size_t AllocSize = NumBlocks * Granularity;
135353358Sdim  void *PA = ::VirtualAlloc(reinterpret_cast<void *>(Start),
136353358Sdim                            AllocSize, AllocType, Protect);
137243830Sdim  if (PA == NULL) {
138353358Sdim    if (NearBlock || HugePages) {
139353358Sdim      // Try again without the NearBlock hint and without large memory pages
140353358Sdim      return allocateMappedMemory(NumBytes, NULL, Flags & ~MF_HUGE_HINT, EC);
141226633Sdim    }
142276479Sdim    EC = mapWindowsError(::GetLastError());
143218885Sdim    return MemoryBlock();
144218885Sdim  }
145218885Sdim
146243830Sdim  MemoryBlock Result;
147243830Sdim  Result.Address = PA;
148353358Sdim  Result.AllocatedSize = AllocSize;
149353358Sdim  Result.Flags = (Flags & ~MF_HUGE_HINT) | (HugePages ? MF_HUGE_HINT : 0);
150261991Sdim
151243830Sdim  if (Flags & MF_EXEC)
152353358Sdim    Memory::InvalidateInstructionCache(Result.Address, AllocSize);
153243830Sdim
154243830Sdim  return Result;
155218885Sdim}
156218885Sdim
157276479Sdim  std::error_code Memory::releaseMappedMemory(MemoryBlock &M) {
158353358Sdim  if (M.Address == 0 || M.AllocatedSize == 0)
159276479Sdim    return std::error_code();
160243830Sdim
161218885Sdim  if (!VirtualFree(M.Address, 0, MEM_RELEASE))
162276479Sdim    return mapWindowsError(::GetLastError());
163243830Sdim
164243830Sdim  M.Address = 0;
165353358Sdim  M.AllocatedSize = 0;
166243830Sdim
167276479Sdim  return std::error_code();
168218885Sdim}
169218885Sdim
170276479Sdim  std::error_code Memory::protectMappedMemory(const MemoryBlock &M,
171243830Sdim                                       unsigned Flags) {
172353358Sdim  if (M.Address == 0 || M.AllocatedSize == 0)
173276479Sdim    return std::error_code();
174243830Sdim
175243830Sdim  DWORD Protect = getWindowsProtectionFlags(Flags);
176243830Sdim
177243830Sdim  DWORD OldFlags;
178353358Sdim  if (!VirtualProtect(M.Address, M.AllocatedSize, Protect, &OldFlags))
179276479Sdim    return mapWindowsError(::GetLastError());
180243830Sdim
181243830Sdim  if (Flags & MF_EXEC)
182353358Sdim    Memory::InvalidateInstructionCache(M.Address, M.AllocatedSize);
183243830Sdim
184276479Sdim  return std::error_code();
185243830Sdim}
186243830Sdim
187243830Sdim/// InvalidateInstructionCache - Before the JIT can run a block of code
188243830Sdim/// that has been emitted it must invalidate the instruction cache on some
189243830Sdim/// platforms.
190243830Sdimvoid Memory::InvalidateInstructionCache(
191243830Sdim    const void *Addr, size_t Len) {
192243830Sdim  FlushInstructionCache(GetCurrentProcess(), Addr, Len);
193243830Sdim}
194243830Sdim
195243830Sdim} // namespace sys
196243830Sdim} // namespace llvm
197