0ad/source/lib/sysdep/win/wposix/wmman.cpp
janwas 4be0fe18a0 # SwEng
. major compat fixes for interoperation with wxw.
. add crashlog_sender (modified version of debgrpt wxw sample). compiles
but untested, will be changed heavily
further improvements:
. wposix no longer declares CRT functions. instead, use CRT headers..
but don't allow their (incorrect/non-POSIX) declarations to propagate to
users.
. split up win.cpp into wstartup, wutil, winit
. extern "C" -> EXTERN_C
. move sdl.* to lib/external_libraries and add png.h+zlib.h (fixes for
and include of the library)

This was SVN commit r5028.
2007-05-04 17:30:32 +00:00

209 lines
6.4 KiB
C++

#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;
}