/*
- * Copyright 2016 Facebook, Inc.
+ * 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.
#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) {
+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) {
+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.
}
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;
+};
+}
+
void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off) {
// Make sure it's something we support first.
}
DWORD newProt;
- if (!mmap_to_page_protection(prot, newProt)) {
+ DWORD accessFlags;
+ if (!mmap_to_page_protection(prot, newProt, accessFlags)) {
return MAP_FAILED;
}
HANDLE fmh = CreateFileMapping(
h,
nullptr,
- newProt | SEC_COMMIT | SEC_RESERVE,
+ newProt,
(DWORD)((length >> 32) & 0xFFFFFFFF),
(DWORD)(length & 0xFFFFFFFF),
nullptr);
+ if (fmh == nullptr) {
+ return MAP_FAILED;
+ }
ret = MapViewOfFileEx(
fmh,
- FILE_MAP_ALL_ACCESS,
- (DWORD)((off >> 32) & 0xFFFFFFFF),
+ accessFlags,
+ (DWORD)(0), // off_t is only 32-bit :(
(DWORD)(off & 0xFFFFFFFF),
0,
addr);
}
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
int mprotect(void* addr, size_t size, int prot) {
DWORD newProt;
- if (!mmap_to_page_protection(prot, newProt)) {
+ DWORD access;
+ if (!mmap_to_page_protection(prot, newProt, access)) {
return -1;
}
}
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;
}
int munmap(void* addr, size_t length) {
// Try to unmap it as a file, otherwise VirtualFree.
if (!UnmapViewOfFile(addr)) {
- if (!VirtualFree(addr, length, MEM_RELEASE)) {
+ 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