1/*
2 * Copyright 2016-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <folly/portability/SysMman.h>
18
19#ifdef _WIN32
20
21#include <cassert>
22
23#include <folly/Portability.h>
24#include <folly/portability/Windows.h>
25
26static bool mmap_to_page_protection(int prot, DWORD& ret, DWORD& acc) {
27 if (prot == PROT_NONE) {
28 ret = PAGE_NOACCESS;
29 acc = 0;
30 } else if (prot == PROT_READ) {
31 ret = PAGE_READONLY;
32 acc = FILE_MAP_READ;
33 } else if (prot == PROT_EXEC) {
34 ret = PAGE_EXECUTE;
35 acc = FILE_MAP_EXECUTE;
36 } else if (prot == (PROT_READ | PROT_EXEC)) {
37 ret = PAGE_EXECUTE_READ;
38 acc = FILE_MAP_READ | FILE_MAP_EXECUTE;
39 } else if (prot == (PROT_READ | PROT_WRITE)) {
40 ret = PAGE_READWRITE;
41 acc = FILE_MAP_READ | FILE_MAP_WRITE;
42 } else if (prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) {
43 ret = PAGE_EXECUTE_READWRITE;
44 acc = FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_EXECUTE;
45 } else {
46 return false;
47 }
48 return true;
49}
50
51static size_t alignToAllocationGranularity(size_t s) {
52 static size_t granularity = [] {
53 static SYSTEM_INFO inf;
54 GetSystemInfo(&inf);
55 return inf.dwAllocationGranularity;
56 }();
57 return (s + granularity - 1) / granularity * granularity;
58}
59
60extern "C" {
61int madvise(const void* /* addr */, size_t /* len */, int /* advise */) {
62 // We do nothing at all.
63 // Could probably implement dontneed via VirtualAlloc
64 // with the MEM_RESET and MEM_RESET_UNDO flags.
65 return 0;
66}
67
68int mlock(const void* addr, size_t len) {
69 // For some strange reason, it's allowed to
70 // lock a nullptr as long as length is zero.
71 // VirtualLock doesn't allow it, so handle
72 // it specially.
73 if (addr == nullptr && len == 0) {
74 return 0;
75 }
76 if (!VirtualLock((void*)addr, len)) {
77 return -1;
78 }
79 return 0;
80}
81
82namespace {
83constexpr uint32_t kMMapLengthMagic = 0xFACEB00C;
84struct MemMapDebugTrailer {
85 size_t length;
86 uint32_t magic;
87};
88} // namespace
89
90void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off) {
91 // Make sure it's something we support first.
92
93 // No Anon shared.
94 if ((flags & (MAP_ANONYMOUS | MAP_SHARED)) == (MAP_ANONYMOUS | MAP_SHARED)) {
95 return MAP_FAILED;
96 }
97 // No private copy on write.
98 // If the map isn't writable, we can let it go through as
99 // whether changes to the underlying file are reflected in the map
100 // is defined to be unspecified by the standard.
101 if ((flags & MAP_PRIVATE) == MAP_PRIVATE &&
102 (prot & PROT_WRITE) == PROT_WRITE && fd != -1) {
103 return MAP_FAILED;
104 }
105 // Map isn't anon, must be file backed.
106 if (!(flags & MAP_ANONYMOUS) && fd == -1) {
107 return MAP_FAILED;
108 }
109
110 DWORD newProt;
111 DWORD accessFlags;
112 if (!mmap_to_page_protection(prot, newProt, accessFlags)) {
113 return MAP_FAILED;
114 }
115
116 void* ret;
117 if (!(flags & MAP_ANONYMOUS) || (flags & MAP_SHARED)) {
118 HANDLE h = INVALID_HANDLE_VALUE;
119 if (!(flags & MAP_ANONYMOUS)) {
120 h = (HANDLE)_get_osfhandle(fd);
121 }
122
123 HANDLE fmh = CreateFileMapping(
124 h,
125 nullptr,
126 newProt,
127 (DWORD)((length >> 32) & 0xFFFFFFFF),
128 (DWORD)(length & 0xFFFFFFFF),
129 nullptr);
130 if (fmh == nullptr) {
131 return MAP_FAILED;
132 }
133 ret = MapViewOfFileEx(
134 fmh,
135 accessFlags,
136 (DWORD)(0), // off_t is only 32-bit :(
137 (DWORD)(off & 0xFFFFFFFF),
138 0,
139 addr);
140 if (ret == nullptr) {
141 ret = MAP_FAILED;
142 }
143 CloseHandle(fmh);
144 } else {
145 auto baseLength = length;
146 if (folly::kIsDebug) {
147 // In debug mode we keep track of the length to make
148 // sure you're only munmapping the entire thing if
149 // we're using VirtualAlloc.
150 length += sizeof(MemMapDebugTrailer);
151 }
152
153 // VirtualAlloc rounds size down to a multiple
154 // of the system allocation granularity :(
155 length = alignToAllocationGranularity(length);
156 ret = VirtualAlloc(addr, length, MEM_COMMIT | MEM_RESERVE, newProt);
157 if (ret == nullptr) {
158 return MAP_FAILED;
159 }
160
161 if (folly::kIsDebug) {
162 auto deb = (MemMapDebugTrailer*)((char*)ret + baseLength);
163 deb->length = baseLength;
164 deb->magic = kMMapLengthMagic;
165 }
166 }
167
168 // TODO: Could technically implement MAP_POPULATE via PrefetchVirtualMemory
169 // Should also see about implementing MAP_NORESERVE
170 return ret;
171}
172
173int mprotect(void* addr, size_t size, int prot) {
174 DWORD newProt;
175 DWORD access;
176 if (!mmap_to_page_protection(prot, newProt, access)) {
177 return -1;
178 }
179
180 DWORD oldProt;
181 BOOL res = VirtualProtect(addr, size, newProt, &oldProt);
182 if (!res) {
183 return -1;
184 }
185 return 0;
186}
187
188int munlock(const void* addr, size_t length) {
189 // See comment in mlock
190 if (addr == nullptr && length == 0) {
191 return 0;
192 }
193 if (!VirtualUnlock((void*)addr, length)) {
194 return -1;
195 }
196 return 0;
197}
198
199int munmap(void* addr, size_t length) {
200 // Try to unmap it as a file, otherwise VirtualFree.
201 if (!UnmapViewOfFile(addr)) {
202 if (folly::kIsDebug) {
203 // We can't do partial unmapping with Windows, so
204 // assert that we aren't trying to do that if we're
205 // in debug mode.
206 MEMORY_BASIC_INFORMATION inf;
207 VirtualQuery(addr, &inf, sizeof(inf));
208 assert(inf.AllocationBase == addr);
209
210 auto deb = (MemMapDebugTrailer*)((char*)addr + length);
211 assert(deb->length == length);
212 assert(deb->magic == kMMapLengthMagic);
213 }
214 if (!VirtualFree(addr, 0, MEM_RELEASE)) {
215 return -1;
216 }
217 return 0;
218 }
219 return 0;
220}
221}
222
223#endif
224