224 lines
5.9 KiB
C++
224 lines
5.9 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* 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 the map isn't writable, we can let it go through as
|
|
// whether changes to the underlying file are reflected in the map
|
|
// is defined to be unspecified by the standard.
|
|
if ((flags & MAP_PRIVATE) == MAP_PRIVATE &&
|
|
(prot & PROT_WRITE) == PROT_WRITE && 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
|