#include "precompiled.h" #include "wmman.h" #include "wposix_internal.h" #include "crt_posix.h" // _get_osfhandle //----------------------------------------------------------------------------- // memory mapping //----------------------------------------------------------------------------- // convert POSIX PROT_* flags to their Win32 PAGE_* enumeration equivalents. // used by mprotect. static DWORD win32_prot(int prot) { // this covers all 8 combinations of read|write|exec // (note that "none" means all flags are 0). switch(prot & (PROT_READ|PROT_WRITE|PROT_EXEC)) { case PROT_NONE: return PAGE_NOACCESS; case PROT_READ: return PAGE_READONLY; case PROT_WRITE: // not supported by Win32; POSIX allows us to also grant read access. return PAGE_READWRITE; case PROT_EXEC: return PAGE_EXECUTE; case PROT_READ|PROT_WRITE: return PAGE_READWRITE; case PROT_READ|PROT_EXEC: return PAGE_EXECUTE_READ; case PROT_WRITE|PROT_EXEC: // not supported by Win32; POSIX allows us to also grant read access. return PAGE_EXECUTE_READWRITE; case PROT_READ|PROT_WRITE|PROT_EXEC: return PAGE_EXECUTE_READWRITE; NODEFAULT; } } int mprotect(void* addr, size_t len, int prot) { const DWORD flNewProtect = win32_prot(prot); DWORD flOldProtect; // required by VirtualProtect BOOL ok = VirtualProtect(addr, len, flNewProtect, &flOldProtect); WARN_RETURN_IF_FALSE(ok); return 0; } // called when flags & MAP_ANONYMOUS static LibError mmap_mem(void* start, size_t len, int prot, int flags, int fd, void** pp) { // sanity checks. we don't care about these but enforce them to // ensure callers are compatible with mmap. // .. MAP_ANONYMOUS is documented to require this. debug_assert(fd == -1); // .. if MAP_SHARED, writes are to change "the underlying [mapped] // object", but there is none here (we're backed by the page file). debug_assert(flags & MAP_PRIVATE); // see explanation at MAP_NORESERVE definition. bool want_commit = (prot != PROT_NONE && !(flags & MAP_NORESERVE)); // decommit a given area (leaves its address space reserved) if(!want_commit && start != 0 && flags & MAP_FIXED) { MEMORY_BASIC_INFORMATION mbi; WARN_RETURN_IF_FALSE(VirtualQuery(start, &mbi, sizeof(mbi))); if(mbi.State == MEM_COMMIT) { WARN_IF_FALSE(VirtualFree(start, len, MEM_DECOMMIT)); *pp = 0; // make sure *pp won't be misinterpreted as an error cassert(MAP_FAILED != 0); return INFO::OK; } } DWORD flAllocationType = want_commit? MEM_COMMIT : MEM_RESERVE; DWORD flProtect = win32_prot(prot); void* p = VirtualAlloc(start, len, flAllocationType, flProtect); if(!p) WARN_RETURN(ERR::NO_MEM); *pp = p; return INFO::OK; } // given mmap prot and flags, output protection/access values for use with // CreateFileMapping / MapViewOfFile. they only support read-only, // read/write and copy-on-write, so we dumb it down to that and later // set the correct (and more restrictive) permission via mprotect. static LibError mmap_file_access(int prot, int flags, DWORD& flProtect, DWORD& dwAccess) { // assume read-only; other cases handled below. flProtect = PAGE_READONLY; dwAccess = FILE_MAP_READ; if(prot & PROT_WRITE) { // determine write behavior: (whether they change the underlying file) switch(flags & (MAP_SHARED|MAP_PRIVATE)) { // .. changes are written to file. case MAP_SHARED: flProtect = PAGE_READWRITE; dwAccess = FILE_MAP_WRITE; // read and write break; // .. copy-on-write mapping; writes do not affect the file. case MAP_PRIVATE: flProtect = PAGE_WRITECOPY; dwAccess = FILE_MAP_COPY; break; // .. either none or both of the flags are set. the latter is // definitely illegal according to POSIX and some man pages // say exactly one must be set, so abort. default: WARN_RETURN(ERR::INVALID_PARAM); } } return INFO::OK; } static LibError mmap_file(void* start, size_t len, int prot, int flags, int fd, off_t ofs, void** pp) { debug_assert(fd != -1); // handled by mmap_mem WIN_SAVE_LAST_ERROR; HANDLE hFile = HANDLE_from_intptr(_get_osfhandle(fd)); if(hFile == INVALID_HANDLE_VALUE) WARN_RETURN(ERR::INVALID_PARAM); // MapViewOfFileEx will fail if the "suggested" base address is // nonzero but cannot be honored, so wipe out <start> unless MAP_FIXED. if(!(flags & MAP_FIXED)) start = 0; // choose protection and access rights for CreateFileMapping / // MapViewOfFile. these are weaker than what PROT_* allows and // are augmented below by subsequently mprotect-ing. DWORD flProtect; DWORD dwAccess; RETURN_ERR(mmap_file_access(prot, flags, flProtect, dwAccess)); // enough foreplay; now actually map. const HANDLE hMap = CreateFileMapping(hFile, 0, flProtect, 0, 0, (LPCSTR)0); // .. create failed; bail now to avoid overwriting the last error value. if(!hMap) WARN_RETURN(ERR::NO_MEM); const DWORD ofs_hi = u64_hi(ofs), ofs_lo = u64_lo(ofs); void* p = MapViewOfFileEx(hMap, dwAccess, ofs_hi, ofs_lo, (SIZE_T)len, start); // .. make sure we got the requested address if MAP_FIXED was passed. debug_assert(!(flags & MAP_FIXED) || (p == start)); // .. free the mapping object now, so that we don't have to hold on to its // handle until munmap(). it's not actually released yet due to the // reference held by MapViewOfFileEx (if it succeeded). CloseHandle(hMap); // .. map failed; bail now to avoid "restoring" the last error value. if(!p) WARN_RETURN(ERR::NO_MEM); // slap on correct (more restrictive) permissions. (void)mprotect(p, len, prot); WIN_RESTORE_LAST_ERROR; *pp = p; return INFO::OK; } void* mmap(void* start, size_t len, int prot, int flags, int fd, off_t ofs) { void* p; LibError err; if(flags & MAP_ANONYMOUS) err = mmap_mem(start, len, prot, flags, fd, &p); else err = mmap_file(start, len, prot, flags, fd, ofs, &p); if(err < 0) { WARN_ERR(err); LibError_set_errno(err); return MAP_FAILED; } return p; } int munmap(void* start, size_t UNUSED(len)) { // UnmapViewOfFile checks if start was returned by MapViewOfFile*; // if not, it will fail. BOOL ok = UnmapViewOfFile(start); if(!ok) // VirtualFree requires dwSize to be 0 (entire region is released). ok = VirtualFree(start, 0, MEM_RELEASE); WARN_RETURN_IF_FALSE(ok); // both failed return 0; }