123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- /*
- * Copyright 2016-present Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <folly/portability/SysMman.h>
- #ifdef _WIN32
- #include <cassert>
- #include <folly/Portability.h>
- #include <folly/portability/Windows.h>
- static bool mmap_to_page_protection(int prot, DWORD& ret, DWORD& acc) {
- if (prot == PROT_NONE) {
- ret = PAGE_NOACCESS;
- acc = 0;
- } else if (prot == PROT_READ) {
- ret = PAGE_READONLY;
- acc = FILE_MAP_READ;
- } else if (prot == PROT_EXEC) {
- ret = PAGE_EXECUTE;
- acc = FILE_MAP_EXECUTE;
- } else if (prot == (PROT_READ | PROT_EXEC)) {
- ret = PAGE_EXECUTE_READ;
- acc = FILE_MAP_READ | FILE_MAP_EXECUTE;
- } else if (prot == (PROT_READ | PROT_WRITE)) {
- ret = PAGE_READWRITE;
- acc = FILE_MAP_READ | FILE_MAP_WRITE;
- } else if (prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) {
- ret = PAGE_EXECUTE_READWRITE;
- acc = FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_EXECUTE;
- } else {
- return false;
- }
- return true;
- }
- static size_t alignToAllocationGranularity(size_t s) {
- static size_t granularity = [] {
- static SYSTEM_INFO inf;
- GetSystemInfo(&inf);
- return inf.dwAllocationGranularity;
- }();
- return (s + granularity - 1) / granularity * granularity;
- }
- extern "C" {
- int madvise(const void* /* addr */, size_t /* len */, int /* advise */) {
- // We do nothing at all.
- // Could probably implement dontneed via VirtualAlloc
- // with the MEM_RESET and MEM_RESET_UNDO flags.
- return 0;
- }
- int mlock(const void* addr, size_t len) {
- // For some strange reason, it's allowed to
- // lock a nullptr as long as length is zero.
- // VirtualLock doesn't allow it, so handle
- // it specially.
- if (addr == nullptr && len == 0) {
- return 0;
- }
- if (!VirtualLock((void*)addr, len)) {
- return -1;
- }
- return 0;
- }
- namespace {
- constexpr uint32_t kMMapLengthMagic = 0xFACEB00C;
- struct MemMapDebugTrailer {
- size_t length;
- uint32_t magic;
- };
- } // namespace
- void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off) {
- // Make sure it's something we support first.
- // No Anon shared.
- if ((flags & (MAP_ANONYMOUS | MAP_SHARED)) == (MAP_ANONYMOUS | MAP_SHARED)) {
- return MAP_FAILED;
- }
- // No private copy on write.
- if ((flags & MAP_PRIVATE) == MAP_PRIVATE && fd != -1) {
- return MAP_FAILED;
- }
- // Map isn't anon, must be file backed.
- if (!(flags & MAP_ANONYMOUS) && fd == -1) {
- return MAP_FAILED;
- }
- DWORD newProt;
- DWORD accessFlags;
- if (!mmap_to_page_protection(prot, newProt, accessFlags)) {
- return MAP_FAILED;
- }
- void* ret;
- if (!(flags & MAP_ANONYMOUS) || (flags & MAP_SHARED)) {
- HANDLE h = INVALID_HANDLE_VALUE;
- if (!(flags & MAP_ANONYMOUS)) {
- h = (HANDLE)_get_osfhandle(fd);
- }
- HANDLE fmh = CreateFileMapping(
- h,
- nullptr,
- newProt,
- (DWORD)((length >> 32) & 0xFFFFFFFF),
- (DWORD)(length & 0xFFFFFFFF),
- nullptr);
- if (fmh == nullptr) {
- return MAP_FAILED;
- }
- ret = MapViewOfFileEx(
- fmh,
- accessFlags,
- (DWORD)(0), // off_t is only 32-bit :(
- (DWORD)(off & 0xFFFFFFFF),
- 0,
- addr);
- if (ret == nullptr) {
- ret = MAP_FAILED;
- }
- CloseHandle(fmh);
- } else {
- auto baseLength = length;
- if (folly::kIsDebug) {
- // In debug mode we keep track of the length to make
- // sure you're only munmapping the entire thing if
- // we're using VirtualAlloc.
- length += sizeof(MemMapDebugTrailer);
- }
- // VirtualAlloc rounds size down to a multiple
- // of the system allocation granularity :(
- length = alignToAllocationGranularity(length);
- ret = VirtualAlloc(addr, length, MEM_COMMIT | MEM_RESERVE, newProt);
- if (ret == nullptr) {
- return MAP_FAILED;
- }
- if (folly::kIsDebug) {
- auto deb = (MemMapDebugTrailer*)((char*)ret + baseLength);
- deb->length = baseLength;
- deb->magic = kMMapLengthMagic;
- }
- }
- // TODO: Could technically implement MAP_POPULATE via PrefetchVirtualMemory
- // Should also see about implementing MAP_NORESERVE
- return ret;
- }
- int mprotect(void* addr, size_t size, int prot) {
- DWORD newProt;
- DWORD access;
- if (!mmap_to_page_protection(prot, newProt, access)) {
- return -1;
- }
- DWORD oldProt;
- BOOL res = VirtualProtect(addr, size, newProt, &oldProt);
- if (!res) {
- return -1;
- }
- return 0;
- }
- int munlock(const void* addr, size_t length) {
- // See comment in mlock
- if (addr == nullptr && length == 0) {
- return 0;
- }
- if (!VirtualUnlock((void*)addr, length)) {
- return -1;
- }
- return 0;
- }
- int munmap(void* addr, size_t length) {
- // Try to unmap it as a file, otherwise VirtualFree.
- if (!UnmapViewOfFile(addr)) {
- if (folly::kIsDebug) {
- // We can't do partial unmapping with Windows, so
- // assert that we aren't trying to do that if we're
- // in debug mode.
- MEMORY_BASIC_INFORMATION inf;
- VirtualQuery(addr, &inf, sizeof(inf));
- assert(inf.AllocationBase == addr);
- auto deb = (MemMapDebugTrailer*)((char*)addr + length);
- assert(deb->length == length);
- assert(deb->magic == kMMapLengthMagic);
- }
- if (!VirtualFree(addr, 0, MEM_RELEASE)) {
- return -1;
- }
- return 0;
- }
- return 0;
- }
- }
- #endif
|