SysMman.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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. #include <folly/portability/SysMman.h>
  17. #ifdef _WIN32
  18. #include <cassert>
  19. #include <folly/Portability.h>
  20. #include <folly/portability/Windows.h>
  21. static bool mmap_to_page_protection(int prot, DWORD& ret, DWORD& acc) {
  22. if (prot == PROT_NONE) {
  23. ret = PAGE_NOACCESS;
  24. acc = 0;
  25. } else if (prot == PROT_READ) {
  26. ret = PAGE_READONLY;
  27. acc = FILE_MAP_READ;
  28. } else if (prot == PROT_EXEC) {
  29. ret = PAGE_EXECUTE;
  30. acc = FILE_MAP_EXECUTE;
  31. } else if (prot == (PROT_READ | PROT_EXEC)) {
  32. ret = PAGE_EXECUTE_READ;
  33. acc = FILE_MAP_READ | FILE_MAP_EXECUTE;
  34. } else if (prot == (PROT_READ | PROT_WRITE)) {
  35. ret = PAGE_READWRITE;
  36. acc = FILE_MAP_READ | FILE_MAP_WRITE;
  37. } else if (prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) {
  38. ret = PAGE_EXECUTE_READWRITE;
  39. acc = FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_EXECUTE;
  40. } else {
  41. return false;
  42. }
  43. return true;
  44. }
  45. static size_t alignToAllocationGranularity(size_t s) {
  46. static size_t granularity = [] {
  47. static SYSTEM_INFO inf;
  48. GetSystemInfo(&inf);
  49. return inf.dwAllocationGranularity;
  50. }();
  51. return (s + granularity - 1) / granularity * granularity;
  52. }
  53. extern "C" {
  54. int madvise(const void* /* addr */, size_t /* len */, int /* advise */) {
  55. // We do nothing at all.
  56. // Could probably implement dontneed via VirtualAlloc
  57. // with the MEM_RESET and MEM_RESET_UNDO flags.
  58. return 0;
  59. }
  60. int mlock(const void* addr, size_t len) {
  61. // For some strange reason, it's allowed to
  62. // lock a nullptr as long as length is zero.
  63. // VirtualLock doesn't allow it, so handle
  64. // it specially.
  65. if (addr == nullptr && len == 0) {
  66. return 0;
  67. }
  68. if (!VirtualLock((void*)addr, len)) {
  69. return -1;
  70. }
  71. return 0;
  72. }
  73. namespace {
  74. constexpr uint32_t kMMapLengthMagic = 0xFACEB00C;
  75. struct MemMapDebugTrailer {
  76. size_t length;
  77. uint32_t magic;
  78. };
  79. } // namespace
  80. void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off) {
  81. // Make sure it's something we support first.
  82. // No Anon shared.
  83. if ((flags & (MAP_ANONYMOUS | MAP_SHARED)) == (MAP_ANONYMOUS | MAP_SHARED)) {
  84. return MAP_FAILED;
  85. }
  86. // No private copy on write.
  87. if ((flags & MAP_PRIVATE) == MAP_PRIVATE && fd != -1) {
  88. return MAP_FAILED;
  89. }
  90. // Map isn't anon, must be file backed.
  91. if (!(flags & MAP_ANONYMOUS) && fd == -1) {
  92. return MAP_FAILED;
  93. }
  94. DWORD newProt;
  95. DWORD accessFlags;
  96. if (!mmap_to_page_protection(prot, newProt, accessFlags)) {
  97. return MAP_FAILED;
  98. }
  99. void* ret;
  100. if (!(flags & MAP_ANONYMOUS) || (flags & MAP_SHARED)) {
  101. HANDLE h = INVALID_HANDLE_VALUE;
  102. if (!(flags & MAP_ANONYMOUS)) {
  103. h = (HANDLE)_get_osfhandle(fd);
  104. }
  105. HANDLE fmh = CreateFileMapping(
  106. h,
  107. nullptr,
  108. newProt,
  109. (DWORD)((length >> 32) & 0xFFFFFFFF),
  110. (DWORD)(length & 0xFFFFFFFF),
  111. nullptr);
  112. if (fmh == nullptr) {
  113. return MAP_FAILED;
  114. }
  115. ret = MapViewOfFileEx(
  116. fmh,
  117. accessFlags,
  118. (DWORD)(0), // off_t is only 32-bit :(
  119. (DWORD)(off & 0xFFFFFFFF),
  120. 0,
  121. addr);
  122. if (ret == nullptr) {
  123. ret = MAP_FAILED;
  124. }
  125. CloseHandle(fmh);
  126. } else {
  127. auto baseLength = length;
  128. if (folly::kIsDebug) {
  129. // In debug mode we keep track of the length to make
  130. // sure you're only munmapping the entire thing if
  131. // we're using VirtualAlloc.
  132. length += sizeof(MemMapDebugTrailer);
  133. }
  134. // VirtualAlloc rounds size down to a multiple
  135. // of the system allocation granularity :(
  136. length = alignToAllocationGranularity(length);
  137. ret = VirtualAlloc(addr, length, MEM_COMMIT | MEM_RESERVE, newProt);
  138. if (ret == nullptr) {
  139. return MAP_FAILED;
  140. }
  141. if (folly::kIsDebug) {
  142. auto deb = (MemMapDebugTrailer*)((char*)ret + baseLength);
  143. deb->length = baseLength;
  144. deb->magic = kMMapLengthMagic;
  145. }
  146. }
  147. // TODO: Could technically implement MAP_POPULATE via PrefetchVirtualMemory
  148. // Should also see about implementing MAP_NORESERVE
  149. return ret;
  150. }
  151. int mprotect(void* addr, size_t size, int prot) {
  152. DWORD newProt;
  153. DWORD access;
  154. if (!mmap_to_page_protection(prot, newProt, access)) {
  155. return -1;
  156. }
  157. DWORD oldProt;
  158. BOOL res = VirtualProtect(addr, size, newProt, &oldProt);
  159. if (!res) {
  160. return -1;
  161. }
  162. return 0;
  163. }
  164. int munlock(const void* addr, size_t length) {
  165. // See comment in mlock
  166. if (addr == nullptr && length == 0) {
  167. return 0;
  168. }
  169. if (!VirtualUnlock((void*)addr, length)) {
  170. return -1;
  171. }
  172. return 0;
  173. }
  174. int munmap(void* addr, size_t length) {
  175. // Try to unmap it as a file, otherwise VirtualFree.
  176. if (!UnmapViewOfFile(addr)) {
  177. if (folly::kIsDebug) {
  178. // We can't do partial unmapping with Windows, so
  179. // assert that we aren't trying to do that if we're
  180. // in debug mode.
  181. MEMORY_BASIC_INFORMATION inf;
  182. VirtualQuery(addr, &inf, sizeof(inf));
  183. assert(inf.AllocationBase == addr);
  184. auto deb = (MemMapDebugTrailer*)((char*)addr + length);
  185. assert(deb->length == length);
  186. assert(deb->magic == kMMapLengthMagic);
  187. }
  188. if (!VirtualFree(addr, 0, MEM_RELEASE)) {
  189. return -1;
  190. }
  191. return 0;
  192. }
  193. return 0;
  194. }
  195. }
  196. #endif