Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
wmman.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2011 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "precompiled.h"
25 
27 #include "lib/sysdep/os/win/wposix/crt_posix.h" // _get_osfhandle
28 
29 
30 unsigned MemoryProtectionFromPosix(int prot)
31 {
32  if(prot == PROT_NONE)
33  return PAGE_NOACCESS;
34 
35  // this covers all 8 combinations of read|write|exec
36  switch(prot & (PROT_READ|PROT_WRITE|PROT_EXEC))
37  {
38  case PROT_READ:
39  return PAGE_READONLY;
40  case PROT_WRITE:
41  // not supported by Win32; POSIX allows us to also grant read access.
42  return PAGE_READWRITE;
43  case PROT_EXEC:
44  return PAGE_EXECUTE;
45  case PROT_READ|PROT_WRITE:
46  return PAGE_READWRITE;
47  case PROT_READ|PROT_EXEC:
48  return PAGE_EXECUTE_READ;
49  case PROT_WRITE|PROT_EXEC:
50  // not supported by Win32; POSIX allows us to also grant read access.
51  return PAGE_EXECUTE_READWRITE;
53  return PAGE_EXECUTE_READWRITE;
54  default: // none set
56  return PAGE_NOACCESS;
57  }
58 
59  // UNREACHABLE
60 }
61 
62 
63 //-----------------------------------------------------------------------------
64 // memory mapping
65 //-----------------------------------------------------------------------------
66 
67 int mprotect(void* addr, size_t len, int prot)
68 {
69  const DWORD newProtect = (DWORD)MemoryProtectionFromPosix(prot);
70  DWORD oldProtect; // required by VirtualProtect
71  const BOOL ok = VirtualProtect(addr, len, newProtect, &oldProtect);
72  WARN_IF_FALSE(ok);
73  return ok? 0 : -1;
74 }
75 
76 
77 // called when flags & MAP_ANONYMOUS
78 static Status mmap_mem(void* start, size_t len, int prot, int flags, int fd, void** pp)
79 {
80  // sanity checks. we don't care about these but enforce them to
81  // ensure callers are compatible with mmap.
82  // .. MAP_ANONYMOUS is documented to require this.
83  ENSURE(fd == -1);
84  // .. if MAP_SHARED, writes are to change "the underlying [mapped]
85  // object", but there is none here (we're backed by the page file).
86  ENSURE(!(flags & MAP_SHARED));
87 
88  // see explanation at MAP_NORESERVE definition.
89  bool want_commit = (prot != PROT_NONE && !(flags & MAP_NORESERVE));
90 
91  // decommit a given area (leaves its address space reserved)
92  if(!want_commit && start != 0 && flags & MAP_FIXED)
93  {
94  MEMORY_BASIC_INFORMATION mbi;
95  if(!VirtualQuery(start, &mbi, sizeof(mbi)))
97  if(mbi.State == MEM_COMMIT)
98  {
99  WARN_IF_FALSE(VirtualFree(start, len, MEM_DECOMMIT));
100  *pp = 0;
101  // make sure *pp won't be misinterpreted as an error
103  return INFO::OK;
104  }
105  }
106 
107  const DWORD allocationType = want_commit? MEM_COMMIT : MEM_RESERVE;
108  const DWORD protect = (DWORD)MemoryProtectionFromPosix(prot);
109  void* p = VirtualAlloc(start, len, allocationType, protect);
110  if(!p)
111  {
112  debug_printf(L"wmman: VirtualAlloc(%p, 0x%I64X) failed\n", start, len);
114  }
115  *pp = p;
116  return INFO::OK;
117 }
118 
119 
120 // given mmap prot and flags, output protection/access values for use with
121 // CreateFileMapping / MapViewOfFile. they only support read-only,
122 // read/write and copy-on-write, so we dumb it down to that and later
123 // set the correct (and more restrictive) permission via mprotect.
124 static Status DecodeFlags(int prot, int flags, DWORD& protect, DWORD& access)
125 {
126  // ensure exactly one of (MAP_SHARED, MAP_PRIVATE) is specified
127  switch(flags & (MAP_SHARED|MAP_PRIVATE))
128  {
129  case 0:
130  case MAP_SHARED|MAP_PRIVATE:
132  default:;
133  }
134 
135  if(prot & PROT_WRITE)
136  {
137  // determine write behavior: (whether they change the underlying file)
138  if(flags & MAP_SHARED) // writes affect the file
139  {
140  protect = PAGE_READWRITE;
141  access = FILE_MAP_WRITE; // read and write
142  }
143  else // copy on write (file remains unchanged)
144  {
145  protect = PAGE_WRITECOPY;
146  access = FILE_MAP_COPY;
147  }
148  }
149  else
150  {
151  protect = PAGE_READONLY;
152  access = FILE_MAP_READ;
153  }
154 
155  return INFO::OK;
156 }
157 
158 
159 static Status mmap_file(void* start, size_t len, int prot, int flags, int fd, off_t ofs, void** pp)
160 {
162 
163  ENSURE(fd != -1); // handled by mmap_mem
164 
165  HANDLE hFile = HANDLE_from_intptr(_get_osfhandle(fd));
166  if(hFile == INVALID_HANDLE_VALUE)
168 
169  // MapViewOfFileEx will fail if the "suggested" base address is
170  // nonzero but cannot be honored, so wipe out <start> unless MAP_FIXED.
171  if(!(flags & MAP_FIXED))
172  start = 0;
173 
174  // choose protection and access rights for CreateFileMapping /
175  // MapViewOfFile. these are weaker than what PROT_* allows and
176  // are augmented below by subsequently mprotect-ing.
177  DWORD protect; DWORD access;
178  RETURN_STATUS_IF_ERR(DecodeFlags(prot, flags, protect, access));
179 
180  const HANDLE hMap = CreateFileMapping(hFile, 0, protect, 0, 0, 0);
181  if(!hMap)
183  void* p = MapViewOfFileEx(hMap, access, u64_hi(ofs), u64_lo(ofs), (SIZE_T)len, start);
184  // ensure we got the requested address if MAP_FIXED was passed.
185  ENSURE(!(flags & MAP_FIXED) || (p == start));
186  // free the mapping object now, so that we don't have to hold on to its
187  // handle until munmap(). it's not actually released yet due to the
188  // reference held by MapViewOfFileEx (if it succeeded).
189  CloseHandle(hMap);
190  // map failed; bail now to avoid "restoring" the last error value.
191  if(!p)
193 
194  // enforce the desired (more restrictive) protection.
195  (void)mprotect(p, len, prot);
196 
197  *pp = p;
198  return INFO::OK;
199 }
200 
201 
202 void* mmap(void* start, size_t len, int prot, int flags, int fd, off_t ofs)
203 {
204  ASSERT(len != 0);
205 
206  void* p;
207  Status status;
208  if(flags & MAP_ANONYMOUS)
209  status = mmap_mem(start, len, prot, flags, fd, &p);
210  else
211  status = mmap_file(start, len, prot, flags, fd, ofs, &p);
212  if(status < 0)
213  {
214  errno = ErrnoFromStatus(status);
215  return MAP_FAILED; // NOWARN - already done
216  }
217 
218  return p;
219 }
220 
221 
222 int munmap(void* start, size_t UNUSED(len))
223 {
224  // UnmapViewOfFile checks if start was returned by MapViewOfFile*;
225  // if not, it will fail.
226  BOOL ok = UnmapViewOfFile(start);
227  if(!ok)
228  // VirtualFree requires dwSize to be 0 (entire region is released).
229  ok = VirtualFree(start, 0, MEM_RELEASE);
230  WARN_IF_FALSE(ok);
231  return ok? 0 : -1;
232 }
#define PROT_WRITE
Definition: wmman.h:33
#define MAP_SHARED
Definition: wmman.h:37
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
static Status mmap_file(void *start, size_t len, int prot, int flags, int fd, off_t ofs, void **pp)
Definition: wmman.cpp:159
const Status OK
Definition: status.h:386
some WinAPI functions SetLastError(0) on success, which is bad because it can hide previous errors...
Definition: wutil.h:119
int mprotect(void *addr, size_t len, int prot)
Definition: wmman.cpp:67
#define ASSERT(expr)
same as ENSURE in debug mode, does nothing in release mode.
Definition: debug.h:310
u32 u64_lo(u64 x)
return upper 32-bits
Definition: lib.cpp:49
const Status INVALID_HANDLE
Definition: status.h:419
int BOOL
Definition: wgl.h:51
static Status DecodeFlags(int prot, int flags, DWORD &protect, DWORD &access)
Definition: wmman.cpp:124
int ErrnoFromStatus(Status status)
Definition: status.cpp:93
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
void * mmap(void *start, size_t len, int prot, int flags, int fd, off_t ofs)
Definition: wmman.cpp:202
__int64 off_t
Definition: wposix_types.h:91
void * HANDLE
Definition: wgl.h:62
#define MAP_FAILED
Definition: wmman.h:52
unsigned long DWORD
Definition: wgl.h:56
#define MAP_FIXED
Definition: wmman.h:39
Status StatusFromWin()
Definition: wutil.cpp:125
const Status INVALID_PARAM
Definition: status.h:423
i64 Status
Error handling system.
Definition: status.h:171
#define DEBUG_WARN_ERR(status)
display the error dialog with text corresponding to the given error code.
Definition: debug.h:331
#define PROT_READ
Definition: wmman.h:32
#define MAP_PRIVATE
Definition: wmman.h:38
#define WARN_IF_FALSE(expression)
Definition: status.h:360
const Status INVALID_FLAG
Definition: status.h:422
#define MAP_NORESERVE
Definition: wmman.h:42
unsigned MemoryProtectionFromPosix(int prot)
Definition: wmman.cpp:30
u32 u64_hi(u64 x)
Definition: lib.cpp:44
#define WARN_RETURN(status)
Definition: status.h:255
static Status mmap_mem(void *start, size_t len, int prot, int flags, int fd, void **pp)
Definition: wmman.cpp:78
HANDLE HANDLE_from_intptr(intptr_t i)
int munmap(void *start, size_t len)
Definition: wmman.cpp:222
#define cassert(expr)
Compile-time assertion.
const Status NO_MEM
Definition: status.h:430
#define MAP_ANONYMOUS
void debug_printf(const wchar_t *fmt,...)
write a formatted string to the debug channel, subject to filtering (see below).
Definition: debug.cpp:142
#define PROT_EXEC
Definition: wmman.h:34
#define RETURN_STATUS_IF_ERR(expression)
Definition: status.h:276
#define PROT_NONE
Definition: wmman.h:31