]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygwin/mmap.cc
Throughout, update copyrights to reflect dates which correspond to main-branch
[newlib-cygwin.git] / winsup / cygwin / mmap.cc
CommitLineData
1fd5e000
CF
1/* mmap.cc
2
bc837d22
CF
3 Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
4 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc.
1fd5e000
CF
5
6This file is part of Cygwin.
7
8This software is a copyrighted work licensed under the terms of the
9Cygwin license. Please consult the file "CYGWIN_LICENSE" for
10details. */
11
4c8d72de 12#include "winsup.h"
ade47a34 13#include "miscfuncs.h"
c6dd43f2 14#include <unistd.h>
1fd5e000 15#include <stdlib.h>
1fd5e000 16#include <sys/mman.h>
5a101414 17#include <sys/param.h>
169c465a 18#include "cygerrno.h"
6b91b8d5 19#include "security.h"
47063f00 20#include "path.h"
7ac61736 21#include "fhandler.h"
e2ebe117 22#include "dtable.h"
7ade56ca 23#include "cygheap.h"
6d11044c 24#include "ntdll.h"
230a3c86 25#include <sys/queue.h>
1fd5e000 26
5a101414
CV
27/* __PROT_ATTACH indicates an anonymous mapping which is supposed to be
28 attached to a file mapping for pages beyond the file's EOF. The idea
29 is to support mappings longer than the file, without the file growing
30 to mapping length (POSIX semantics). */
31#define __PROT_ATTACH 0x8000000
32/* Filler pages are the pages from the last file backed page to the next
33 64K boundary. These pages are created as anonymous pages, but with
34 the same page protection as the file's pages, since POSIX applications
35 expect to be able to access this part the same way as the file pages. */
36#define __PROT_FILLER 0x4000000
37
d5b45d2f
CV
38/* Stick with 4K pages for bookkeeping, otherwise we just get confused
39 when trying to do file mappings with trailing filler pages correctly. */
177dc6c7 40#define PAGE_CNT(bytes) howmany((bytes), wincap.page_size())
c6dd43f2 41
f0227ea3
CF
42#define PGBITS (sizeof (DWORD)*8)
43#define MAPSIZE(pages) howmany ((pages), PGBITS)
c6dd43f2 44
f90e23f2
CV
45#define MAP_SET(n) (page_map[(n)/PGBITS] |= (1L << ((n) % PGBITS)))
46#define MAP_CLR(n) (page_map[(n)/PGBITS] &= ~(1L << ((n) % PGBITS)))
47#define MAP_ISSET(n) (page_map[(n)/PGBITS] & (1L << ((n) % PGBITS)))
c6dd43f2 48
5a101414
CV
49/* Used for anonymous mappings. */
50static fhandler_dev_zero fh_anonymous;
a020c74a 51
a1591d3b
CV
52/* Used for thread synchronization while accessing mmap bookkeeping lists. */
53static NO_COPY muto mmap_guard;
54#define LIST_LOCK() (mmap_guard.init ("mmap_guard")->acquire ())
55#define LIST_UNLOCK() (mmap_guard.release ())
56
a020c74a
CV
57/* Small helpers to avoid having lots of flag bit tests in the code. */
58static inline bool
59priv (int flags)
60{
61 return (flags & MAP_PRIVATE) == MAP_PRIVATE;
62}
63
64static inline bool
65fixed (int flags)
66{
67 return (flags & MAP_FIXED) == MAP_FIXED;
68}
69
70static inline bool
71anonymous (int flags)
72{
73 return (flags & MAP_ANONYMOUS) == MAP_ANONYMOUS;
74}
75
76static inline bool
77noreserve (int flags)
78{
79 return (flags & MAP_NORESERVE) == MAP_NORESERVE;
80}
81
82static inline bool
83autogrow (int flags)
84{
85 return (flags & MAP_AUTOGROW) == MAP_AUTOGROW;
86}
4544f7f6 87
5a101414
CV
88static inline bool
89attached (int prot)
90{
91 return (prot & __PROT_ATTACH) == __PROT_ATTACH;
92}
93
94static inline bool
95filler (int prot)
96{
97 return (prot & __PROT_FILLER) == __PROT_FILLER;
98}
99
100static inline DWORD
101gen_create_protect (DWORD openflags, int flags)
102{
103 DWORD ret = PAGE_READONLY;
104
2193d02b
CV
105 if (priv (flags))
106 ret = PAGE_WRITECOPY;
107 else if (openflags & GENERIC_WRITE)
108 ret = PAGE_READWRITE;
5a101414 109
3af64048 110 if (openflags & GENERIC_EXECUTE)
5a101414
CV
111 ret <<= 4;
112
113 return ret;
114}
115
f90e23f2 116/* Generate Windows protection flags from mmap prot and flag values. */
b5342210 117static inline DWORD
5a101414 118gen_protect (int prot, int flags)
f90e23f2
CV
119{
120 DWORD ret = PAGE_NOACCESS;
5a101414
CV
121
122 /* Attached pages are only reserved, but the protection must be a
123 valid value, so we just return PAGE_READWRITE. */
124 if (attached (prot))
125 return PAGE_EXECUTE_READWRITE;
126
127 if (prot & PROT_WRITE)
128 ret = (priv (flags) && (!anonymous (flags) || filler (prot)))
129 ? PAGE_WRITECOPY : PAGE_READWRITE;
f90e23f2 130 else if (prot & PROT_READ)
5a101414
CV
131 ret = PAGE_READONLY;
132
3af64048 133 if (prot & PROT_EXEC)
f90e23f2 134 ret <<= 4;
5a101414 135
f90e23f2
CV
136 return ret;
137}
138
f90e23f2 139static HANDLE
570858c3 140CreateMapping (HANDLE fhdl, size_t len, _off64_t off, DWORD openflags,
7d2ade33 141 int prot, int flags)
f90e23f2
CV
142{
143 HANDLE h;
177dc6c7 144 NTSTATUS status;
f90e23f2
CV
145
146 LARGE_INTEGER sectionsize = { QuadPart: len };
5a101414
CV
147 ULONG protect = gen_create_protect (openflags, flags);
148 ULONG attributes = attached (prot) ? SEC_RESERVE : SEC_COMMIT;
f90e23f2
CV
149
150 OBJECT_ATTRIBUTES oa;
151 InitializeObjectAttributes (&oa, NULL, OBJ_INHERIT, NULL,
152 sec_none.lpSecurityDescriptor);
153
154 if (fhdl == INVALID_HANDLE_VALUE)
155 {
156 /* Standard anonymous mapping needs non-zero len. */
177dc6c7
CV
157 status = NtCreateSection (&h, SECTION_ALL_ACCESS, &oa, &sectionsize,
158 protect, attributes, NULL);
f90e23f2 159 }
a020c74a 160 else if (autogrow (flags))
f90e23f2
CV
161 {
162 /* Auto-grow only works if the protection is PAGE_READWRITE. So,
de935f6d 163 first we call NtCreateSection with PAGE_READWRITE, then, if the
f90e23f2
CV
164 requested protection is different, we close the mapping and
165 reopen it again with the correct protection, if auto-grow worked. */
166 sectionsize.QuadPart += off;
177dc6c7
CV
167 status = NtCreateSection (&h, SECTION_ALL_ACCESS, &oa, &sectionsize,
168 PAGE_READWRITE, attributes, fhdl);
169 if (NT_SUCCESS (status) && protect != PAGE_READWRITE)
de935f6d 170 {
7d2ade33 171 NtClose (h);
177dc6c7
CV
172 status = NtCreateSection (&h, SECTION_ALL_ACCESS, &oa, &sectionsize,
173 protect, attributes, fhdl);
f90e23f2
CV
174 }
175 }
176 else
177 {
178 /* Zero len creates mapping for whole file and allows
de935f6d 179 AT_EXTENDABLE_FILE mapping, if we ever use it... */
f90e23f2 180 sectionsize.QuadPart = 0;
177dc6c7
CV
181 status = NtCreateSection (&h, SECTION_ALL_ACCESS, &oa, &sectionsize,
182 protect, attributes, fhdl);
f90e23f2 183 }
177dc6c7 184 if (!NT_SUCCESS (status))
f90e23f2
CV
185 {
186 h = NULL;
177dc6c7 187 SetLastError (RtlNtStatusToDosError (status));
f90e23f2
CV
188 }
189 return h;
190}
191
abbde487 192static void *
570858c3
CV
193MapView (HANDLE h, void *addr, size_t len, DWORD openflags,
194 int prot, int flags, _off64_t off)
f90e23f2 195{
177dc6c7 196 NTSTATUS status;
f90e23f2 197 LARGE_INTEGER offset = { QuadPart:off };
5a101414 198 DWORD protect = gen_create_protect (openflags, flags);
f90e23f2 199 void *base = addr;
5a101414
CV
200 ULONG commitsize = attached (prot) ? 0 : len;
201 ULONG viewsize = len;
c2b10dc4
CV
202 ULONG alloc_type = (base && !wincap.is_wow64 () ? AT_ROUND_TO_PAGE : 0)
203 | MEM_TOP_DOWN;
f90e23f2
CV
204
205 /* Try mapping using the given address first, even if it's NULL.
206 If it failed, and addr was not NULL and flags is not MAP_FIXED,
9f3e3df9 207 try again with NULL address.
510a85cb 208
9f3e3df9
CV
209 Note: Retrying the mapping might be unnecessary, now that mmap64 checks
210 for a valid memory area first. */
177dc6c7
CV
211 status = NtMapViewOfSection (h, NtCurrentProcess (), &base, 0, commitsize,
212 &offset, &viewsize, ViewShare, alloc_type,
213 protect);
214 if (!NT_SUCCESS (status) && addr && !fixed (flags))
f90e23f2
CV
215 {
216 base = NULL;
177dc6c7
CV
217 status = NtMapViewOfSection (h, NtCurrentProcess (), &base, 0, commitsize,
218 &offset, &viewsize, ViewShare, 0, protect);
f90e23f2 219 }
177dc6c7 220 if (!NT_SUCCESS (status))
f90e23f2
CV
221 {
222 base = NULL;
177dc6c7 223 SetLastError (RtlNtStatusToDosError (status));
f90e23f2 224 }
b007725e 225 debug_printf ("%p (status %p) = NtMapViewOfSection (h:%x, addr:%x, len:%u,"
b86f999a 226 " off:%X, protect:%x, type:%x)",
177dc6c7 227 base, status, h, addr, len, off, protect, 0);
f90e23f2
CV
228 return base;
229}
230
4544f7f6
CV
231/* Class structure used to keep a record of all current mmap areas
232 in a process. Needed for bookkeeping all mmaps in a process and
233 for duplicating all mmaps after fork() since mmaps are not propagated
234 to child processes by Windows. All information must be duplicated
235 by hand, see fixup_mmaps_after_fork().
9a4d574b 236
4544f7f6 237 The class structure:
9a4d574b 238
4544f7f6 239 One member of class map per process, global variable mmapped_areas.
230a3c86
CV
240 Contains a singly-linked list of type class mmap_list. Each mmap_list
241 entry represents all mapping to a file, keyed by file descriptor and
242 file name hash.
243 Each list entry contains a singly-linked list of type class mmap_record.
244 Each mmap_record represents exactly one mapping. For each mapping, there's
4544f7f6
CV
245 an additional so called `page_map'. It's an array of bits, one bit
246 per mapped memory page. The bit is set if the page is accessible,
247 unset otherwise. */
1fd5e000 248
bf69faeb 249#pragma pack(push, 4)
1fd5e000
CF
250class mmap_record
251{
230a3c86
CV
252 public:
253 LIST_ENTRY (mmap_record) mr_next;
254
1fd5e000 255 private:
f90e23f2
CV
256 int fd;
257 HANDLE mapping_hdl;
5a101414 258 DWORD openflags;
f90e23f2
CV
259 int prot;
260 int flags;
261 _off64_t offset;
262 DWORD len;
263 caddr_t base_address;
bf69faeb 264 int dev;
ac706ac1 265 DWORD page_map[0];
1fd5e000
CF
266
267 public:
5a101414 268 mmap_record (int nfd, HANDLE h, DWORD of, int p, int f, _off64_t o, DWORD l,
de935f6d 269 caddr_t b) :
f90e23f2
CV
270 fd (nfd),
271 mapping_hdl (h),
5a101414 272 openflags (of),
f90e23f2
CV
273 prot (p),
274 flags (f),
275 offset (o),
276 len (l),
ac706ac1 277 base_address (b)
7ade56ca 278 {
bf69faeb 279 dev = 0;
9867ecfd 280 if (fd >= 0 && !cygheap->fdtab.not_open (fd))
7ac61736 281 dev = cygheap->fdtab[fd]->dev ();
a020c74a 282 else if (fd == -1)
bf69faeb 283 dev = FH_ZERO;
7ade56ca 284 }
1fd5e000 285
f90e23f2
CV
286 int get_fd () const { return fd; }
287 HANDLE get_handle () const { return mapping_hdl; }
bf69faeb 288 int get_device () { return dev; }
f90e23f2 289 int get_prot () const { return prot; }
5a101414 290 int get_openflags () const { return openflags; }
f90e23f2 291 int get_flags () const { return flags; }
a020c74a
CV
292 bool priv () const { return ::priv (flags); }
293 bool fixed () const { return ::fixed (flags); }
294 bool anonymous () const { return ::anonymous (flags); }
295 bool noreserve () const { return ::noreserve (flags); }
296 bool autogrow () const { return ::autogrow (flags); }
5a101414
CV
297 bool attached () const { return ::attached (prot); }
298 bool filler () const { return ::filler (prot); }
f90e23f2
CV
299 _off64_t get_offset () const { return offset; }
300 DWORD get_len () const { return len; }
301 caddr_t get_address () const { return base_address; }
4544f7f6 302
ac706ac1 303 void init_page_map (mmap_record &r);
4544f7f6 304
f90e23f2 305 DWORD find_unused_pages (DWORD pages) const;
230a3c86 306 bool match (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len);
4544f7f6 307 _off64_t map_pages (_off64_t off, DWORD len);
c68de3a2 308 bool map_pages (caddr_t addr, DWORD len);
2e008fb9 309 bool unmap_pages (caddr_t addr, DWORD len);
857b65dd 310 int access (caddr_t address);
7ade56ca
CV
311
312 fhandler_base *alloc_fh ();
313 void free_fh (fhandler_base *fh);
de935f6d 314
5a101414
CV
315 DWORD gen_create_protect () const
316 { return ::gen_create_protect (get_openflags (), get_flags ()); }
317 DWORD gen_protect () const
318 { return ::gen_protect (get_prot (), get_flags ()); }
f90e23f2 319 bool compatible_flags (int fl) const;
1fd5e000 320};
bf69faeb 321#pragma pack(pop)
1fd5e000 322
230a3c86 323class mmap_list
4544f7f6 324{
230a3c86
CV
325 public:
326 LIST_ENTRY (mmap_list) ml_next;
327 LIST_HEAD (, mmap_record) recs;
328
4544f7f6 329 private:
4544f7f6 330 int fd;
2397d888 331 __ino64_t hash;
4544f7f6
CV
332
333 public:
334 int get_fd () const { return fd; }
2397d888 335 __ino64_t get_hash () const { return hash; }
4544f7f6 336
a020c74a 337 bool anonymous () const { return fd == -1; }
2397d888 338 void set (int nfd, struct __stat64 *st);
ac706ac1 339 mmap_record *add_record (mmap_record &r);
230a3c86 340 bool del_record (mmap_record *rec);
b0c3293e 341 caddr_t try_map (void *addr, size_t len, int flags, _off64_t off);
4544f7f6
CV
342};
343
230a3c86 344class mmap_areas
4544f7f6 345{
4544f7f6 346 public:
230a3c86
CV
347 LIST_HEAD (, mmap_list) lists;
348
349 mmap_list *get_list_by_fd (int fd, struct __stat64 *st);
350 mmap_list *add_list (int fd, struct __stat64 *st);
351 void del_list (mmap_list *ml);
4544f7f6
CV
352};
353
b5342210 354/* This is the global map structure pointer. */
230a3c86 355static mmap_areas mmapped_areas;
f90e23f2
CV
356
357bool
358mmap_record::compatible_flags (int fl) const
359{
f90e23f2 360#define MAP_COMPATMASK (MAP_TYPE | MAP_NORESERVE)
f90e23f2
CV
361 return (get_flags () & MAP_COMPATMASK) == (fl & MAP_COMPATMASK);
362}
4544f7f6 363
c6dd43f2 364DWORD
f90e23f2 365mmap_record::find_unused_pages (DWORD pages) const
c6dd43f2 366{
f90e23f2 367 DWORD mapped_pages = PAGE_CNT (get_len ());
c6dd43f2
CV
368 DWORD start;
369
910e57ec
CV
370 if (pages > mapped_pages)
371 return (DWORD)-1;
f977222f 372 for (start = 0; start <= mapped_pages - pages; ++start)
c6dd43f2
CV
373 if (!MAP_ISSET (start))
374 {
9867ecfd
CF
375 DWORD cnt;
376 for (cnt = 0; cnt < pages; ++cnt)
c6dd43f2
CV
377 if (MAP_ISSET (start + cnt))
378 break;
9867ecfd 379 if (cnt >= pages)
c6dd43f2
CV
380 return start;
381 }
382 return (DWORD)-1;
383}
384
230a3c86
CV
385bool
386mmap_record::match (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len)
387{
388 caddr_t low = (addr >= get_address ()) ? addr : get_address ();
389 caddr_t high = get_address ();
390 if (filler ())
391 high += get_len ();
392 else
177dc6c7 393 high += (PAGE_CNT (get_len ()) * wincap.page_size ());
230a3c86
CV
394 high = (addr + len < high) ? addr + len : high;
395 if (low < high)
396 {
397 m_addr = low;
398 m_len = high - low;
399 return true;
400 }
401 return false;
402}
403
ac706ac1
CV
404void
405mmap_record::init_page_map (mmap_record &r)
4136fdb1 406{
ac706ac1 407 *this = r;
5a101414 408 DWORD start_protect = gen_create_protect ();
b5342210 409 DWORD real_protect = gen_protect ();
d44b9795 410 if (real_protect != start_protect && !noreserve ()
b5342210
CV
411 && !VirtualProtect (get_address (), get_len (),
412 real_protect, &start_protect))
413 system_printf ("Warning: VirtualProtect (addr: %p, len: 0x%x, "
414 "new_prot: 0x%x, old_prot: 0x%x), %E",
415 get_address (), get_len (),
416 real_protect, start_protect);
f90e23f2 417 DWORD len = PAGE_CNT (get_len ());
e348b39a 418 while (len-- > 0)
f90e23f2 419 MAP_SET (len);
4136fdb1
CV
420}
421
1727fba0 422_off64_t
4544f7f6 423mmap_record::map_pages (_off64_t off, DWORD len)
c6dd43f2 424{
4136fdb1
CV
425 /* Used ONLY if this mapping matches into the chunk of another already
426 performed mapping in a special case of MAP_ANON|MAP_PRIVATE.
a113a3c5 427
ac706ac1 428 Otherwise it's job is now done by init_page_map(). */
f90e23f2
CV
429 DWORD old_prot;
430 debug_printf ("map_pages (fd=%d, off=%D, len=%u)", get_fd (), off, len);
c6dd43f2 431 len = PAGE_CNT (len);
c6dd43f2 432
4544f7f6 433 if ((off = find_unused_pages (len)) == (DWORD)-1)
4136fdb1 434 return 0L;
a020c74a 435 if (!noreserve ()
177dc6c7
CV
436 && !VirtualProtect (get_address () + off * wincap.page_size (),
437 len * wincap.page_size (), gen_protect (),
d5b45d2f 438 &old_prot))
bbe09139 439 {
f4b098d9 440 __seterrno ();
1727fba0 441 return (_off64_t)-1;
bbe09139 442 }
c6dd43f2 443
4136fdb1
CV
444 while (len-- > 0)
445 MAP_SET (off + len);
177dc6c7 446 return off * wincap.page_size ();
c6dd43f2
CV
447}
448
c68de3a2
CV
449bool
450mmap_record::map_pages (caddr_t addr, DWORD len)
451{
452 debug_printf ("map_pages (addr=%x, len=%u)", addr, len);
f90e23f2
CV
453 DWORD old_prot;
454 DWORD off = addr - get_address ();
177dc6c7 455 off /= wincap.page_size ();
c68de3a2
CV
456 len = PAGE_CNT (len);
457 /* First check if the area is unused right now. */
458 for (DWORD l = 0; l < len; ++l)
459 if (MAP_ISSET (off + l))
460 {
05726ddd 461 set_errno (EINVAL);
c68de3a2
CV
462 return false;
463 }
a020c74a 464 if (!noreserve ()
177dc6c7
CV
465 && !VirtualProtect (get_address () + off * wincap.page_size (),
466 len * wincap.page_size (), gen_protect (),
d5b45d2f 467 &old_prot))
c68de3a2
CV
468 {
469 __seterrno ();
470 return false;
471 }
472 for (; len-- > 0; ++off)
473 MAP_SET (off);
474 return true;
475}
476
2e008fb9 477bool
4544f7f6 478mmap_record::unmap_pages (caddr_t addr, DWORD len)
c6dd43f2
CV
479{
480 DWORD old_prot;
f90e23f2 481 DWORD off = addr - get_address ();
2e19497a 482 if (noreserve ()
d5b45d2f 483 && !VirtualFree (get_address () + off, len, MEM_DECOMMIT))
b5342210 484 debug_printf ("VirtualFree in unmap_pages () failed, %E");
d5b45d2f
CV
485 else if (!VirtualProtect (get_address () + off, len, PAGE_NOACCESS,
486 &old_prot))
b5342210 487 debug_printf ("VirtualProtect in unmap_pages () failed, %E");
c6dd43f2 488
177dc6c7 489 off /= wincap.page_size ();
d5b45d2f 490 len = PAGE_CNT (len);
c6dd43f2
CV
491 for (; len-- > 0; ++off)
492 MAP_CLR (off);
493 /* Return TRUE if all pages are free'd which may result in unmapping
494 the whole chunk. */
f90e23f2
CV
495 for (len = MAPSIZE (PAGE_CNT (get_len ())); len > 0; )
496 if (page_map[--len])
2e008fb9
CF
497 return false;
498 return true;
c6dd43f2
CV
499}
500
713fb38b 501int
857b65dd 502mmap_record::access (caddr_t address)
713fb38b 503{
f90e23f2 504 if (address < get_address () || address >= get_address () + get_len ())
713fb38b 505 return 0;
177dc6c7 506 DWORD off = (address - get_address ()) / wincap.page_size ();
713fb38b
CV
507 return MAP_ISSET (off);
508}
509
7ade56ca
CV
510fhandler_base *
511mmap_record::alloc_fh ()
512{
a020c74a 513 if (anonymous ())
7ade56ca 514 {
5a101414
CV
515 fh_anonymous.set_io_handle (INVALID_HANDLE_VALUE);
516 fh_anonymous.set_access (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
517 return &fh_anonymous;
7ade56ca
CV
518 }
519
520 /* The file descriptor could have been closed or, even
521 worse, could have been reused for another file before
522 the call to fork(). This requires creating a fhandler
523 of the correct type to be sure to call the method of the
524 correct class. */
bf69faeb 525 device fdev;
ccbc7fca 526 fdev.name = fdev.native = "";
bf69faeb 527 fdev.parse (get_device ());
ccbc7fca 528 fhandler_base *fh = build_fh_dev (fdev);
e5b7e4d1
CF
529 if (fh)
530 fh->set_access (get_openflags ());
5a101414 531 return fh;
7ade56ca
CV
532}
533
534void
535mmap_record::free_fh (fhandler_base *fh)
536{
a020c74a 537 if (!anonymous ())
1c1b04b8 538 delete fh;
7ade56ca
CV
539}
540
c6dd43f2 541mmap_record *
ac706ac1 542mmap_list::add_record (mmap_record &r)
1fd5e000 543{
ac706ac1
CV
544 mmap_record *rec = (mmap_record *) ccalloc (HEAP_MMAP,
545 sizeof (mmap_record)
546 + MAPSIZE (PAGE_CNT (r.get_len ())) * sizeof (DWORD), 1);
230a3c86
CV
547 if (!rec)
548 return NULL;
ac706ac1
CV
549 rec->init_page_map (r);
550
230a3c86
CV
551 LIST_INSERT_HEAD (&recs, rec, mr_next);
552 return rec;
c6dd43f2
CV
553}
554
1fd5e000 555void
230a3c86 556mmap_list::set (int nfd, struct __stat64 *st)
1fd5e000 557{
a020c74a
CV
558 fd = nfd;
559 if (!anonymous ())
5a101414
CV
560 {
561 /* The fd isn't sufficient since it could already be the fd of another
562 file. So we use the inode number as evaluated by fstat to identify
563 the file. */
2397d888 564 hash = st ? st->st_ino : (__ino64_t) 0;
5a101414 565 }
230a3c86 566 LIST_INIT (&recs);
1fd5e000
CF
567}
568
4544f7f6 569bool
230a3c86 570mmap_list::del_record (mmap_record *rec)
1fd5e000 571{
230a3c86
CV
572 LIST_REMOVE (rec, mr_next);
573 cfree (rec);
4544f7f6 574 /* Return true if the list is empty which allows the caller to remove
230a3c86
CV
575 this list from the list of lists. */
576 return !LIST_FIRST(&recs);
1fd5e000
CF
577}
578
b0c3293e 579caddr_t
230a3c86 580mmap_list::try_map (void *addr, size_t len, int flags, _off64_t off)
b0c3293e
CV
581{
582 mmap_record *rec;
583
584 if (off == 0 && !fixed (flags))
585 {
586 /* If MAP_FIXED isn't given, check if this mapping matches into the
587 chunk of another already performed mapping. */
a1591d3b
CV
588 DWORD plen = PAGE_CNT (len);
589 LIST_FOREACH (rec, &recs, mr_next)
590 if (rec->find_unused_pages (plen) != (DWORD) -1)
591 break;
592 if (rec && rec->compatible_flags (flags))
b0c3293e 593 {
a1591d3b 594 if ((off = rec->map_pages (off, len)) == (_off64_t) -1)
b0c3293e
CV
595 return (caddr_t) MAP_FAILED;
596 return (caddr_t) rec->get_address () + off;
597 }
598 }
599 else if (fixed (flags))
600 {
601 /* If MAP_FIXED is given, test if the requested area is in an
602 unmapped part of an still active mapping. This can happen
603 if a memory region is unmapped and remapped with MAP_FIXED. */
604 caddr_t u_addr;
605 DWORD u_len;
230a3c86
CV
606
607 LIST_FOREACH (rec, &recs, mr_next)
608 if (rec->match ((caddr_t) addr, len, u_addr, u_len))
609 break;
610 if (rec)
b0c3293e 611 {
b0c3293e
CV
612 if (u_addr > (caddr_t) addr || u_addr + len < (caddr_t) addr + len
613 || !rec->compatible_flags (flags))
614 {
615 /* Partial match only, or access mode doesn't match. */
616 /* FIXME: Handle partial mappings gracefully if adjacent
617 memory is available. */
618 set_errno (EINVAL);
619 return (caddr_t) MAP_FAILED;
620 }
621 if (!rec->map_pages ((caddr_t) addr, len))
622 return (caddr_t) MAP_FAILED;
623 return (caddr_t) addr;
624 }
625 }
626 return NULL;
627}
628
230a3c86
CV
629mmap_list *
630mmap_areas::get_list_by_fd (int fd, struct __stat64 *st)
1fd5e000 631{
230a3c86
CV
632 mmap_list *ml;
633 LIST_FOREACH (ml, &lists, ml_next)
5a101414 634 {
230a3c86
CV
635 if (fd == -1 && ml->anonymous ())
636 return ml;
5a101414
CV
637 /* The fd isn't sufficient since it could already be the fd of another
638 file. So we use the inode number as evaluated by fstat to identify
639 the file. */
230a3c86
CV
640 if (fd != -1 && st && ml->get_hash () == st->st_ino)
641 return ml;
5a101414 642 }
1fd5e000
CF
643 return 0;
644}
645
230a3c86
CV
646mmap_list *
647mmap_areas::add_list (int fd, struct __stat64 *st)
1fd5e000 648{
230a3c86
CV
649 mmap_list *ml = (mmap_list *) cmalloc (HEAP_MMAP, sizeof (mmap_list));
650 if (!ml)
651 return NULL;
652 ml->set (fd, st);
653 LIST_INSERT_HEAD (&lists, ml, ml_next);
654 return ml;
1fd5e000
CF
655}
656
657void
230a3c86 658mmap_areas::del_list (mmap_list *ml)
1fd5e000 659{
230a3c86
CV
660 LIST_REMOVE (ml, ml_next);
661 cfree (ml);
1fd5e000
CF
662}
663
6d6cfa48
CV
664/* This function allows an external function to test if a given memory
665 region is part of an mmapped memory region. */
666bool
667is_mmapped_region (caddr_t start_addr, caddr_t end_address)
668{
6d6cfa48
CV
669 size_t len = end_address - start_addr;
670
671 LIST_LOCK ();
672 mmap_list *map_list = mmapped_areas.get_list_by_fd (-1, NULL);
673
5e3af166
CV
674 if (!map_list)
675 return false;
676
6d6cfa48
CV
677 mmap_record *rec;
678 caddr_t u_addr;
679 DWORD u_len;
5e3af166 680 bool ret = false;
6d6cfa48
CV
681
682 LIST_FOREACH (rec, &map_list->recs, mr_next)
683 {
684 if (rec->match (start_addr, len, u_addr, u_len))
685 {
686 ret = true;
687 break;
688 }
689 }
690 LIST_UNLOCK ();
691 return ret;
692}
693
5a101414 694/* This function is called from exception_handler when a segmentation
70e476d2
CV
695 violation has occurred. It should also be called from all Cygwin
696 functions that want to support passing noreserve mmap page addresses
697 to Windows system calls. In that case, it should be called only after
698 a system call indicates that the application buffer passed had an
699 invalid virtual address to avoid any performance impact in non-noreserve
700 cases.
510a85cb 701
70e476d2
CV
702 Check if the address range is all within noreserve mmap regions. If so,
703 call VirtualAlloc to commit the pages and return MMAP_NORESERVE_COMMITED
704 on success. If the page has __PROT_ATTACH (SUSv3 memory protection
705 extension), or if VirutalAlloc fails, return MMAP_RAISE_SIGBUS.
706 Otherwise, return MMAP_NONE if the address range is not covered by an
707 attached or noreserve map.
708
709 On MAP_NORESERVE_COMMITED, the exeception handler should return 0 to
710 allow the application to retry the memory access, or the calling Cygwin
711 function should retry the Windows system call. */
6d6cfa48 712
70e476d2
CV
713mmap_region_status
714mmap_is_attached_or_noreserve (void *addr, size_t len)
715{
a1591d3b
CV
716 mmap_region_status ret = MMAP_NONE;
717
718 LIST_LOCK ();
230a3c86 719 mmap_list *map_list = mmapped_areas.get_list_by_fd (-1, NULL);
70e476d2 720
177dc6c7 721 size_t pagesize = wincap.allocation_granularity ();
70e476d2
CV
722 caddr_t start_addr = (caddr_t) rounddown ((uintptr_t) addr, pagesize);
723 len += ((caddr_t) addr - start_addr);
724 len = roundup2 (len, pagesize);
725
726 if (map_list == NULL)
a1591d3b 727 goto out;
70e476d2 728
230a3c86
CV
729 mmap_record *rec;
730 caddr_t u_addr;
731 DWORD u_len;
70e476d2 732
230a3c86
CV
733 LIST_FOREACH (rec, &map_list->recs, mr_next)
734 {
735 if (!rec->match (start_addr, len, u_addr, u_len))
736 continue;
70e476d2 737 if (rec->attached ())
a1591d3b
CV
738 {
739 ret = MMAP_RAISE_SIGBUS;
740 break;
741 }
70e476d2 742 if (!rec->noreserve ())
a1591d3b 743 break;
70e476d2
CV
744
745 size_t commit_len = u_len - (start_addr - u_addr);
746 if (commit_len > len)
747 commit_len = len;
748
749 if (!VirtualAlloc (start_addr, commit_len, MEM_COMMIT,
750 rec->gen_protect ()))
a1591d3b
CV
751 {
752 ret = MMAP_RAISE_SIGBUS;
753 break;
754 }
70e476d2
CV
755
756 start_addr += commit_len;
757 len -= commit_len;
230a3c86 758 if (!len)
a1591d3b
CV
759 {
760 ret = MMAP_NORESERVE_COMMITED;
761 break;
762 }
70e476d2 763 }
a1591d3b
CV
764out:
765 LIST_UNLOCK ();
766 return ret;
5a101414
CV
767}
768
769static caddr_t
230a3c86 770mmap_worker (mmap_list *map_list, fhandler_base *fh, caddr_t base, size_t len,
2397d888 771 int prot, int flags, int fd, _off64_t off, struct __stat64 *st)
5a101414 772{
5a101414
CV
773 HANDLE h = fh->mmap (&base, len, prot, flags, off);
774 if (h == INVALID_HANDLE_VALUE)
775 return NULL;
2397d888
CV
776 if (!map_list
777 && !(map_list = mmapped_areas.get_list_by_fd (fd, st))
778 && !(map_list = mmapped_areas.add_list (fd, st)))
5a101414
CV
779 {
780 fh->munmap (h, base, len);
781 return NULL;
782 }
783 mmap_record mmap_rec (fd, h, fh->get_access (), prot, flags, off, len, base);
784 mmap_record *rec = map_list->add_record (mmap_rec);
785 if (!rec)
786 {
787 fh->munmap (h, base, len);
788 return NULL;
789 }
790 return base;
791}
792
807dfb3d
CV
793extern "C" void *
794mmap64 (void *addr, size_t len, int prot, int flags, int fd, _off64_t off)
1fd5e000 795{
d432ac75 796 syscall_printf ("addr %x, len %u, prot %x, flags %x, fd %d, off %D",
1fd5e000
CF
797 addr, len, prot, flags, fd, off);
798
f90e23f2
CV
799 caddr_t ret = (caddr_t) MAP_FAILED;
800 fhandler_base *fh = NULL;
4090f565
CV
801 fhandler_disk_file *fh_disk_file = NULL; /* Used for reopening a disk file
802 when necessary. */
230a3c86 803 mmap_list *map_list = NULL;
5a101414
CV
804 size_t orig_len = 0;
805 caddr_t base = NULL;
2397d888 806 struct __stat64 st;
f90e23f2 807
177dc6c7 808 DWORD pagesize = wincap.allocation_granularity ();
c6dd43f2 809
5a101414
CV
810 fh_anonymous.set_io_handle (INVALID_HANDLE_VALUE);
811 fh_anonymous.set_access (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
5a101414 812
3af64048 813 /* EINVAL error conditions. */
f90e23f2
CV
814 if (off % pagesize
815 || ((prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)))
a020c74a
CV
816 || ((flags & MAP_TYPE) != MAP_SHARED
817 && (flags & MAP_TYPE) != MAP_PRIVATE)
5a101414 818 || (fixed (flags) && ((uintptr_t) addr % pagesize))
c6dd43f2
CV
819 || !len)
820 {
821 set_errno (EINVAL);
f90e23f2 822 goto out;
c6dd43f2
CV
823 }
824
5a101414 825 if (!anonymous (flags) && fd != -1)
1fd5e000
CF
826 {
827 /* Ensure that fd is open */
df63bd49
CF
828 cygheap_fdget cfd (fd);
829 if (cfd < 0)
f90e23f2
CV
830 goto out;
831
5a230876 832 fh = cfd;
5a101414
CV
833
834 /* mmap /dev/zero is like MAP_ANONYMOUS. */
4717214c 835 if (fh->get_device () == FH_ZERO)
5a101414 836 flags |= MAP_ANONYMOUS;
57abff16
CV
837
838 /* The autoconf mmap test maps a file of size 1 byte. It then tests
839 every byte of the entire mapped page of 64K for 0-bytes since that's
840 what POSIX requires. The problem is, we can't create that mapping on
841 64 bit systems. The file mapping will be only a single page, 4K, and
842 since 64 bit systems don't support the AT_ROUND_TO_PAGE flag, the
843 remainder of the 64K slot will result in a SEGV when accessed.
844
845 So, what we do here is cheating for the sake of the autoconf test
846 on 64 bit systems. The justification is that there's very likely
847 no application actually utilizing the map beyond EOF, and we know that
848 all bytes beyond EOF are set to 0 anyway. If this test doesn't work
849 on 64 bit systems, it will result in not using mmap at all in a
850 package. But we want that mmap is treated as usable by autoconf,
851 regardless whether the autoconf test runs on a 32 bit or a 64 bit
852 system.
853
854 Ok, so we know exactly what autoconf is doing. The file is called
855 "conftest.txt", it has a size of 1 byte, the mapping size is the
856 pagesize, the requested protection is PROT_READ | PROT_WRITE, the
857 mapping is MAP_SHARED, the offset is 0.
858
859 If all these requirements are given, we just return an anonymous map.
860 This will help to get over the autoconf test even on 64 bit systems.
861 The tests are ordered for speed. */
862 if (wincap.is_wow64 ())
863 {
864 UNICODE_STRING fname;
865 IO_STATUS_BLOCK io;
866 FILE_STANDARD_INFORMATION fsi;
867
868 if (len == pagesize
869 && prot == (PROT_READ | PROT_WRITE)
870 && flags == MAP_SHARED
871 && off == 0
872 && (RtlSplitUnicodePath (fh->pc.get_nt_native_path (), NULL,
873 &fname),
874 wcscmp (fname.Buffer, L"conftest.txt") == 0)
875 && NT_SUCCESS (NtQueryInformationFile (fh->get_handle (), &io,
876 &fsi, sizeof fsi,
877 FileStandardInformation))
878 && fsi.EndOfFile.QuadPart == 1LL)
879 flags |= MAP_ANONYMOUS;
880 }
9334c89c 881 }
2e19497a 882
5a101414 883 if (anonymous (flags) || fd == -1)
9334c89c 884 {
5a101414
CV
885 fh = &fh_anonymous;
886 fd = -1;
f90e23f2 887 flags |= MAP_ANONYMOUS;
5a101414 888 /* Anonymous mappings are always forced to pagesize length with
de935f6d 889 no offset. */
5a101414 890 len = roundup2 (len, pagesize);
b5342210 891 off = 0;
4717214c 892 }
a020c74a 893 else if (fh->get_device () == FH_FS)
4717214c 894 {
5a101414 895 /* EACCES error conditions according to SUSv3. File must be opened
de935f6d 896 for reading, regardless of the requested protection, and file must
5a101414
CV
897 be opened for writing when PROT_WRITE together with MAP_SHARED
898 is requested. */
899 if (!(fh->get_access () & GENERIC_READ)
900 || (!(fh->get_access () & GENERIC_WRITE)
901 && (prot & PROT_WRITE) && !priv (flags)))
902 {
903 set_errno (EACCES);
904 goto out;
905 }
906
570858c3
CV
907 /* You can't create mappings with PAGE_EXECUTE protection if
908 the file isn't explicitely opened with EXECUTE access. */
7d2ade33
CV
909 OBJECT_ATTRIBUTES attr;
910 NTSTATUS status;
911 HANDLE h;
912 IO_STATUS_BLOCK io;
913
8deb4118 914 InitializeObjectAttributes (&attr, &ro_u_empty, fh->pc.objcaseinsensitive (),
7d2ade33
CV
915 fh->get_handle (), NULL);
916 status = NtOpenFile (&h,
917 fh->get_access () | GENERIC_EXECUTE | SYNCHRONIZE,
918 &attr, &io, FILE_SHARE_VALID_FLAGS,
919 FILE_SYNCHRONOUS_IO_NONALERT
920 | FILE_OPEN_FOR_BACKUP_INTENT);
921 if (NT_SUCCESS (status))
5a101414 922 {
a2c5f4b4
CV
923 fh_disk_file = new (ccalloc (HEAP_FHANDLER, 1, sizeof *fh_disk_file))
924 fhandler_disk_file;
4090f565
CV
925 fh_disk_file->set_name (fh->pc);
926 fh_disk_file->set_io_handle (h);
927 fh_disk_file->set_access (fh->get_access () | GENERIC_EXECUTE);
928 fh = fh_disk_file;
5a101414 929 }
570858c3 930 else if (prot & PROT_EXEC)
de935f6d 931 {
570858c3
CV
932 /* TODO: To be or not to be... I'm opting for refusing this
933 mmap request rather than faking it, but that might break
934 some non-portable code. */
935 set_errno (EACCES);
936 goto out;
5a101414
CV
937 }
938
c80480bf 939 if (fh->fstat_fs (&st))
510a85cb 940 {
2397d888
CV
941 __seterrno ();
942 goto out;
943 }
944 _off64_t fsiz = st.st_size;
d2428633 945
5a101414 946 /* Don't allow file mappings beginning beyond EOF since Windows can't
d2428633 947 handle that POSIX like, unless MAP_AUTOGROW flag is set, which
5a101414 948 mimics Windows behaviour. */
a020c74a 949 if (off >= fsiz && !autogrow (flags))
4717214c 950 {
5a101414
CV
951 /* Instead, it seems suitable to return an anonymous mapping of
952 the given size instead. Mapped addresses beyond EOF aren't
953 written back to the file anyway, so the handling is identical
954 to other pages beyond EOF. */
955 fh = &fh_anonymous;
956 len = roundup2 (len, pagesize);
957 prot = PROT_READ | PROT_WRITE | __PROT_ATTACH;
958 flags &= MAP_FIXED;
959 flags |= MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
960 fd = -1;
961 off = 0;
962 goto go_ahead;
4717214c 963 }
f90e23f2 964 fsiz -= off;
3af64048 965 /* We're creating the pages beyond EOF as reserved, anonymous pages.
b773a592
CV
966 Note that this isn't done in WOW64 environments since apparently
967 WOW64 does not support the AT_ROUND_TO_PAGE flag which is required
968 to get this right. Too bad. */
3af64048 969 if (!wincap.is_wow64 ()
895d15b8 970 && ((len > fsiz && !autogrow (flags))
177dc6c7 971 || roundup2 (len, wincap.page_size ())
895d15b8 972 < roundup2 (len, pagesize)))
5a101414 973 orig_len = len;
f90e23f2 974 if (len > fsiz)
05726ddd 975 {
a020c74a 976 if (autogrow (flags))
d2428633 977 {
5a101414 978 /* Allow mapping beyond EOF if MAP_AUTOGROW flag is set.
de935f6d 979 Check if file has been opened for writing, otherwise
5a101414 980 MAP_AUTOGROW is invalid. */
d2428633
CV
981 if (!(fh->get_access () & GENERIC_WRITE))
982 {
983 set_errno (EINVAL);
f90e23f2 984 goto out;
d2428633 985 }
d2428633
CV
986 }
987 else
5a101414
CV
988 /* Otherwise, don't map beyond EOF, since Windows would change
989 the file to the new length, in contrast to POSIX. */
f90e23f2 990 len = fsiz;
d2428633 991 }
f90e23f2
CV
992
993 /* If the requested offset + len is <= file size, drop MAP_AUTOGROW.
05726ddd 994 This simplifes fhandler::mmap's job. */
a020c74a 995 if (autogrow (flags) && (off + len) <= fsiz)
05726ddd 996 flags &= ~MAP_AUTOGROW;
4717214c
CV
997 }
998
5a101414
CV
999go_ahead:
1000
2e19497a
CV
1001 /* MAP_NORESERVE is only supported on private anonymous mappings.
1002 Remove that bit from flags so that later code doesn't have to
1003 test all bits. */
1004 if (noreserve (flags) && (!anonymous (flags) || !priv (flags)))
1005 flags &= ~MAP_NORESERVE;
1006
a1591d3b 1007 LIST_LOCK ();
2397d888 1008 map_list = mmapped_areas.get_list_by_fd (fd, &st);
db49d0b5 1009
f90e23f2 1010 /* Test if an existing anonymous mapping can be recycled. */
a020c74a 1011 if (map_list && anonymous (flags))
c68de3a2 1012 {
b0c3293e
CV
1013 caddr_t tried = map_list->try_map (addr, len, flags, off);
1014 /* try_map returns NULL if no map matched, otherwise it returns
a1591d3b 1015 a valid address, or MAP_FAILED in case of a fatal error. */
b0c3293e 1016 if (tried)
de935f6d 1017 {
b0c3293e 1018 ret = tried;
a1591d3b 1019 goto out_with_unlock;
c68de3a2
CV
1020 }
1021 }
f4f30cf1 1022
3452cdd3
CV
1023 if (orig_len)
1024 {
1025 /* If the requested length is bigger than the file size, we try to
1026 allocate an area of the full size first. This area is immediately
1027 deallocated and the address we got is used as base address for the
1028 subsequent real mappings. This ensures that we have enough space
1029 for the whole thing. */
1030 orig_len = roundup2 (orig_len, pagesize);
9f3e3df9
CV
1031 PVOID newaddr = VirtualAlloc (addr, orig_len, MEM_TOP_DOWN | MEM_RESERVE,
1032 PAGE_READWRITE);
1033 if (!newaddr)
510a85cb 1034 {
9f3e3df9
CV
1035 /* If addr is not NULL, but MAP_FIXED isn't given, allow the OS
1036 to choose. */
1037 if (addr && !fixed (flags))
1038 newaddr = VirtualAlloc (NULL, orig_len, MEM_TOP_DOWN | MEM_RESERVE,
1039 PAGE_READWRITE);
1040 if (!newaddr)
1041 {
1042 __seterrno ();
a1591d3b 1043 goto out_with_unlock;
9f3e3df9 1044 }
3452cdd3 1045 }
9f3e3df9 1046 if (!VirtualFree (newaddr, 0, MEM_RELEASE))
510a85cb 1047 {
3452cdd3 1048 __seterrno ();
a1591d3b 1049 goto out_with_unlock;
3452cdd3 1050 }
9f3e3df9 1051 addr = newaddr;
3452cdd3
CV
1052 }
1053
2397d888
CV
1054 base = mmap_worker (map_list, fh, (caddr_t) addr, len, prot, flags, fd, off,
1055 &st);
5a101414 1056 if (!base)
a1591d3b 1057 goto out_with_unlock;
f90e23f2 1058
5a101414 1059 if (orig_len)
1fd5e000 1060 {
5a101414
CV
1061 /* If the requested length is bigger than the file size, the
1062 remainder is created as anonymous mapping. Actually two
3452cdd3 1063 mappings are created, first the remainder from the file end to
5a101414
CV
1064 the next 64K boundary as accessible pages with the same
1065 protection as the file's pages, then as much pages as necessary
1066 to accomodate the requested length, but as reserved pages which
1067 raise a SIGBUS when trying to access them. AT_ROUND_TO_PAGE
b773a592 1068 and page protection on shared pages is only supported by 32 bit NT,
457c7938
CV
1069 so don't even try on WOW64. This is accomplished by not setting
1070 orig_len on WOW64 above. */
3452cdd3 1071#if 0
5a101414 1072 orig_len = roundup2 (orig_len, pagesize);
3452cdd3 1073#endif
177dc6c7 1074 len = roundup2 (len, wincap.page_size ());
5a101414
CV
1075 if (orig_len - len)
1076 {
1077 orig_len -= len;
1078 size_t valid_page_len = orig_len % pagesize;
1079 size_t sigbus_page_len = orig_len - valid_page_len;
1fd5e000 1080
5a101414
CV
1081 caddr_t at_base = base + len;
1082 if (valid_page_len)
1083 {
1084 prot |= __PROT_FILLER;
1085 flags &= MAP_SHARED | MAP_PRIVATE;
1086 flags |= MAP_ANONYMOUS | MAP_FIXED;
2397d888
CV
1087 at_base = mmap_worker (NULL, &fh_anonymous, at_base,
1088 valid_page_len, prot, flags, -1, 0, NULL);
5a101414
CV
1089 if (!at_base)
1090 {
1091 fh->munmap (fh->get_handle (), base, len);
1092 set_errno (ENOMEM);
a1591d3b 1093 goto out_with_unlock;
5a101414
CV
1094 }
1095 at_base += valid_page_len;
1096 }
1097 if (sigbus_page_len)
1098 {
1099 prot = PROT_READ | PROT_WRITE | __PROT_ATTACH;
1100 flags = MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED;
2397d888
CV
1101 at_base = mmap_worker (NULL, &fh_anonymous, at_base,
1102 sigbus_page_len, prot, flags, -1, 0, NULL);
5a101414
CV
1103 if (!at_base)
1104 debug_printf ("Warning: Mapping beyond EOF failed, %E");
1105 }
1106 }
4544f7f6
CV
1107 }
1108
f90e23f2
CV
1109 ret = base;
1110
a1591d3b
CV
1111out_with_unlock:
1112 LIST_UNLOCK ();
f90e23f2 1113
a1591d3b 1114out:
5a101414 1115
4090f565 1116 if (fh_disk_file)
a2c5f4b4
CV
1117 {
1118 NtClose (fh_disk_file->get_handle ());
1119 delete fh;
1120 }
5a101414 1121
f90e23f2 1122 syscall_printf ("%p = mmap() ", ret);
c6dd43f2 1123 return ret;
1fd5e000
CF
1124}
1125
807dfb3d
CV
1126extern "C" void *
1127mmap (void *addr, size_t len, int prot, int flags, int fd, _off_t off)
acb56175 1128{
1727fba0 1129 return mmap64 (addr, len, prot, flags, fd, (_off64_t)off);
acb56175
CV
1130}
1131
f2d3c47b 1132/* munmap () removes all mmapped pages between addr and addr+len. */
1fd5e000 1133
c367dfd0 1134extern "C" int
807dfb3d 1135munmap (void *addr, size_t len)
1fd5e000 1136{
d432ac75 1137 syscall_printf ("munmap (addr %x, len %u)", addr, len);
1fd5e000 1138
f2d3c47b 1139 /* Error conditions according to SUSv3 */
f90e23f2 1140 if (!addr || !len || check_invalid_virtual_addr (addr, len))
c6dd43f2
CV
1141 {
1142 set_errno (EINVAL);
c6dd43f2
CV
1143 return -1;
1144 }
177dc6c7 1145 size_t pagesize = wincap.allocation_granularity ();
5a101414 1146 if (((uintptr_t) addr % pagesize) || !len)
1fd5e000 1147 {
f90e23f2
CV
1148 set_errno (EINVAL);
1149 return -1;
1fd5e000 1150 }
5a101414 1151 len = roundup2 (len, pagesize);
1fd5e000 1152
a1591d3b 1153 LIST_LOCK ();
f90e23f2 1154
f2d3c47b
CV
1155 /* Iterate through the map, unmap pages between addr and addr+len
1156 in all maps. */
230a3c86
CV
1157 mmap_list *map_list, *next_map_list;
1158 LIST_FOREACH_SAFE (map_list, &mmapped_areas.lists, ml_next, next_map_list)
1fd5e000 1159 {
230a3c86 1160 mmap_record *rec, *next_rec;
4544f7f6
CV
1161 caddr_t u_addr;
1162 DWORD u_len;
f2d3c47b 1163
230a3c86 1164 LIST_FOREACH_SAFE (rec, &map_list->recs, mr_next, next_rec)
70300fdb 1165 {
230a3c86
CV
1166 if (!rec->match ((caddr_t) addr, len, u_addr, u_len))
1167 continue;
4544f7f6 1168 if (rec->unmap_pages (u_addr, u_len))
1fd5e000 1169 {
186a0e42 1170 /* The whole record has been unmapped, so we now actually
9a4d574b 1171 unmap it from the system in full length... */
4544f7f6 1172 fhandler_base *fh = rec->alloc_fh ();
186a0e42 1173 fh->munmap (rec->get_handle (),
9a4d574b 1174 rec->get_address (),
f90e23f2 1175 rec->get_len ());
4544f7f6 1176 rec->free_fh (fh);
d12eba25 1177
186a0e42 1178 /* ...and delete the record. */
230a3c86 1179 if (map_list->del_record (rec))
4544f7f6
CV
1180 {
1181 /* Yay, the last record has been removed from the list,
1182 we can remove the list now, too. */
230a3c86 1183 mmapped_areas.del_list (map_list);
4544f7f6 1184 break;
1fd5e000 1185 }
c6dd43f2
CV
1186 }
1187 }
1188 }
1fd5e000 1189
a1591d3b 1190 LIST_UNLOCK ();
f2d3c47b
CV
1191 syscall_printf ("0 = munmap(): %x", addr);
1192 return 0;
1fd5e000
CF
1193}
1194
1195/* Sync file with memory. Ignore flags for now. */
1196
c367dfd0 1197extern "C" int
807dfb3d 1198msync (void *addr, size_t len, int flags)
1fd5e000 1199{
f90e23f2 1200 int ret = -1;
230a3c86 1201 mmap_list *map_list;
f90e23f2 1202
5a101414
CV
1203 syscall_printf ("msync (addr: %p, len %u, flags %x)", addr, len, flags);
1204
1804be04
CV
1205 pthread_testcancel ();
1206
a1591d3b 1207 LIST_LOCK ();
f90e23f2 1208
177dc6c7 1209 if (((uintptr_t) addr % wincap.allocation_granularity ())
5a101414 1210 || (flags & ~(MS_ASYNC | MS_SYNC | MS_INVALIDATE))
7b9e380f 1211 || ((flags & (MS_ASYNC | MS_SYNC)) == (MS_ASYNC | MS_SYNC)))
d12eba25 1212 {
d12eba25 1213 set_errno (EINVAL);
f90e23f2 1214 goto out;
d12eba25 1215 }
add2f65f 1216#if 0 /* If I only knew why I did that... */
177dc6c7 1217 len = roundup2 (len, wincap.allocation_granularity ());
add2f65f 1218#endif
d12eba25
CV
1219
1220 /* Iterate through the map, looking for the mmapped area.
1221 Error if not found. */
230a3c86 1222 LIST_FOREACH (map_list, &mmapped_areas.lists, ml_next)
d12eba25 1223 {
4544f7f6 1224 mmap_record *rec;
230a3c86 1225 LIST_FOREACH (rec, &map_list->recs, mr_next)
d12eba25 1226 {
230a3c86 1227 if (rec->access ((caddr_t) addr))
cada03f9 1228 {
4544f7f6 1229 /* Check whole area given by len. */
177dc6c7
CV
1230 for (DWORD i = wincap.allocation_granularity ();
1231 i < len;
1232 i += wincap.allocation_granularity ())
230a3c86 1233 if (!rec->access ((caddr_t) addr + i))
f90e23f2
CV
1234 {
1235 set_errno (ENOMEM);
1236 goto out;
1237 }
4544f7f6 1238 fhandler_base *fh = rec->alloc_fh ();
230a3c86 1239 ret = fh->msync (rec->get_handle (), (caddr_t) addr, len, flags);
4544f7f6 1240 rec->free_fh (fh);
f90e23f2 1241 goto out;
2817e0dc
CV
1242 }
1243 }
1244 }
d12eba25 1245
f90e23f2 1246 /* No matching mapping exists. */
d12eba25 1247 set_errno (ENOMEM);
d12eba25 1248
f90e23f2 1249out:
a1591d3b 1250 LIST_UNLOCK ();
b9aa8149 1251 syscall_printf ("%R = msync()", ret);
f90e23f2 1252 return ret;
d12eba25
CV
1253}
1254
807dfb3d
CV
1255/* Set memory protection */
1256
1257extern "C" int
1258mprotect (void *addr, size_t len, int prot)
1259{
5a101414
CV
1260 bool in_mapped = false;
1261 bool ret = false;
807dfb3d
CV
1262 DWORD old_prot;
1263 DWORD new_prot = 0;
1264
5a101414 1265 syscall_printf ("mprotect (addr: %p, len %u, prot %x)", addr, len, prot);
807dfb3d 1266
5a101414 1267 /* See comment in mmap64 for a description. */
177dc6c7 1268 size_t pagesize = wincap.allocation_granularity ();
5a101414
CV
1269 if ((uintptr_t) addr % pagesize)
1270 {
1271 set_errno (EINVAL);
1272 goto out;
1273 }
1274 len = roundup2 (len, pagesize);
807dfb3d 1275
a1591d3b 1276 LIST_LOCK ();
09b01096 1277
f90e23f2
CV
1278 /* Iterate through the map, protect pages between addr and addr+len
1279 in all maps. */
230a3c86
CV
1280 mmap_list *map_list;
1281 LIST_FOREACH (map_list, &mmapped_areas.lists, ml_next)
1282 {
1283 mmap_record *rec;
1284 caddr_t u_addr;
1285 DWORD u_len;
1286
1287 LIST_FOREACH (rec, &map_list->recs, mr_next)
70300fdb 1288 {
230a3c86
CV
1289 if (!rec->match ((caddr_t) addr, len, u_addr, u_len))
1290 continue;
1291 in_mapped = true;
1292 if (rec->attached ())
1293 continue;
1294 new_prot = gen_protect (prot, rec->get_flags ());
1295 if (rec->noreserve ())
1296 {
1297 if (new_prot == PAGE_NOACCESS)
1298 ret = VirtualFree (u_addr, u_len, MEM_DECOMMIT);
1299 else
1300 ret = !!VirtualAlloc (u_addr, u_len, MEM_COMMIT, new_prot);
1301 }
1302 else
1303 ret = VirtualProtect (u_addr, u_len, new_prot, &old_prot);
1304 if (!ret)
1305 {
1306 __seterrno ();
1307 break;
1308 }
1309 }
1310 }
f90e23f2 1311
a1591d3b 1312 LIST_UNLOCK ();
f90e23f2
CV
1313
1314 if (!in_mapped)
0c0ad23a 1315 {
f90e23f2 1316 int flags = 0;
a020c74a
CV
1317 MEMORY_BASIC_INFORMATION mbi;
1318
5a101414
CV
1319 ret = VirtualQuery (addr, &mbi, sizeof mbi);
1320 if (ret)
de935f6d 1321 {
5a101414
CV
1322 /* If write protection is requested, check if the page was
1323 originally protected writecopy. In this case call VirtualProtect
1324 requesting PAGE_WRITECOPY, otherwise the VirtualProtect will fail
1325 on NT version >= 5.0 */
1326 if (prot & PROT_WRITE)
1327 {
1328 if (mbi.AllocationProtect == PAGE_WRITECOPY
1329 || mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY)
1330 flags = MAP_PRIVATE;
1331 }
1332 new_prot = gen_protect (prot, flags);
1333 if (new_prot != PAGE_NOACCESS && mbi.State == MEM_RESERVE)
1334 ret = VirtualAlloc (addr, len, MEM_COMMIT, new_prot);
1335 else
1336 ret = VirtualProtect (addr, len, new_prot, &old_prot);
f90e23f2 1337 }
a020c74a 1338 if (!ret)
5a101414 1339 __seterrno ();
807dfb3d
CV
1340 }
1341
5a101414
CV
1342out:
1343
b9aa8149 1344 syscall_printf ("%R = mprotect ()", ret ? 0 : -1);
5a101414 1345 return ret ? 0 : -1;
807dfb3d
CV
1346}
1347
1f5c3042
CV
1348extern "C" int
1349mlock (const void *addr, size_t len)
1350{
1f5c3042
CV
1351 int ret = -1;
1352
1f5c3042 1353 /* Align address and length values to page size. */
177dc6c7 1354 size_t pagesize = wincap.allocation_granularity ();
5a101414
CV
1355 PVOID base = (PVOID) rounddown((uintptr_t) addr, pagesize);
1356 ULONG size = roundup2 (((uintptr_t) addr - (uintptr_t) base) + len, pagesize);
1f5c3042
CV
1357 NTSTATUS status = 0;
1358 do
1359 {
f16706de 1360 status = NtLockVirtualMemory (NtCurrentProcess (), &base, &size,
8912b2e5 1361 MAP_PROCESS);
1f5c3042
CV
1362 if (status == STATUS_WORKING_SET_QUOTA)
1363 {
1364 /* The working set is too small, try to increase it so that the
1365 requested locking region fits in. Unfortunately I don't know
1366 any function which would return the currently locked pages of
1367 a process (no go with NtQueryVirtualMemory).
de935f6d 1368
1f5c3042
CV
1369 So, except for the border cases, what we do here is something
1370 really embarrassing. We raise the working set by 64K at a time
1371 and retry, until either we fail to raise the working set size
1372 further, or until NtLockVirtualMemory returns successfully (or
1373 with another error). */
1374 ULONG min, max;
f16706de 1375 if (!GetProcessWorkingSetSize (GetCurrentProcess (), &min, &max))
1f5c3042
CV
1376 {
1377 set_errno (ENOMEM);
1378 break;
1379 }
1380 if (min < size)
f90e23f2
CV
1381 min = size + pagesize;
1382 else if (size < pagesize)
1f5c3042
CV
1383 min += size;
1384 else
f90e23f2 1385 min += pagesize;
1f5c3042
CV
1386 if (max < min)
1387 max = min;
f16706de 1388 if (!SetProcessWorkingSetSize (GetCurrentProcess (), min, max))
1f5c3042
CV
1389 {
1390 set_errno (ENOMEM);
1391 break;
1392 }
1393 }
1394 else if (!NT_SUCCESS (status))
1395 __seterrno_from_nt_status (status);
1396 else
de935f6d 1397 ret = 0;
1f5c3042
CV
1398 }
1399 while (status == STATUS_WORKING_SET_QUOTA);
1400
69864e48 1401 syscall_printf ("%R = mlock(%p, %u)", ret, addr, len);
1f5c3042
CV
1402 return ret;
1403}
1404
1405extern "C" int
1406munlock (const void *addr, size_t len)
1407{
1f5c3042
CV
1408 int ret = -1;
1409
5a101414 1410 /* Align address and length values to page size. */
177dc6c7 1411 size_t pagesize = wincap.allocation_granularity ();
5a101414
CV
1412 PVOID base = (PVOID) rounddown((uintptr_t) addr, pagesize);
1413 ULONG size = roundup2 (((uintptr_t) addr - (uintptr_t) base) + len, pagesize);
f16706de 1414 NTSTATUS status = NtUnlockVirtualMemory (NtCurrentProcess (), &base, &size,
8912b2e5 1415 MAP_PROCESS);
1f5c3042
CV
1416 if (!NT_SUCCESS (status))
1417 __seterrno_from_nt_status (status);
1418 else
1419 ret = 0;
1420
69864e48 1421 syscall_printf ("%R = munlock(%p, %u)", ret, addr, len);
1f5c3042
CV
1422 return ret;
1423}
1424
59e3b6ca
CV
1425extern "C" int
1426posix_madvise (void *addr, size_t len, int advice)
1427{
69864e48 1428 int ret;
59e3b6ca
CV
1429 /* Check parameters. */
1430 if (advice < POSIX_MADV_NORMAL || advice > POSIX_MADV_DONTNEED
1431 || !len)
69864e48
CF
1432 ret = EINVAL;
1433 else
59e3b6ca 1434 {
69864e48
CF
1435 /* Check requested memory area. */
1436 MEMORY_BASIC_INFORMATION m;
1437 char *p = (char *) addr;
1438 char *endp = p + len;
1439 while (p < endp)
1440 {
1441 if (!VirtualQuery (p, &m, sizeof m) || m.State == MEM_FREE)
1442 {
1443 ret = ENOMEM;
1444 break;
1445 }
1446 p = (char *) m.BaseAddress + m.RegionSize;
1447 }
1448 ret = 0;
59e3b6ca
CV
1449 }
1450
69864e48 1451 syscall_printf ("%d = posix_madvise(%p, %u, %d)", ret, addr, len, advice);
59e3b6ca
CV
1452 /* Eventually do nothing. */
1453 return 0;
1454}
1455
d12eba25
CV
1456/*
1457 * Base implementation:
1458 *
1459 * `mmap' returns ENODEV as documented in SUSv2.
1460 * In contrast to the global function implementation, the member function
1461 * `mmap' has to return the mapped base address in `addr' and the handle to
1462 * the mapping object as return value. In case of failure, the fhandler
1463 * mmap has to close that handle by itself and return INVALID_HANDLE_VALUE.
1464 *
1465 * `munmap' and `msync' get the handle to the mapping object as first parameter
1466 * additionally.
1467*/
1468HANDLE
f90e23f2 1469fhandler_base::mmap (caddr_t *addr, size_t len, int prot,
1727fba0 1470 int flags, _off64_t off)
d12eba25
CV
1471{
1472 set_errno (ENODEV);
1473 return INVALID_HANDLE_VALUE;
1474}
1475
1476int
1477fhandler_base::munmap (HANDLE h, caddr_t addr, size_t len)
1478{
1479 set_errno (ENODEV);
1480 return -1;
1481}
1482
1483int
1484fhandler_base::msync (HANDLE h, caddr_t addr, size_t len, int flags)
1485{
1486 set_errno (ENODEV);
1487 return -1;
1488}
1489
2e008fb9 1490bool
f90e23f2 1491fhandler_base::fixup_mmap_after_fork (HANDLE h, int prot, int flags,
6d11044c
CV
1492 _off64_t offset, DWORD size,
1493 void *address)
c4efddf6
CV
1494{
1495 set_errno (ENODEV);
1496 return -1;
1497}
1498
a020c74a
CV
1499/* Implementation for anonymous maps. Using fhandler_dev_zero looks
1500 quite the natural way. */
1501HANDLE
1502fhandler_dev_zero::mmap (caddr_t *addr, size_t len, int prot,
1503 int flags, _off64_t off)
1504{
1505 HANDLE h;
1506 void *base;
1507
5a101414 1508 if (priv (flags) && !filler (prot))
a020c74a
CV
1509 {
1510 /* Private anonymous maps are now implemented using VirtualAlloc.
de935f6d 1511 This has two advantages:
a020c74a
CV
1512
1513 - VirtualAlloc has a smaller footprint than a copy-on-write
1514 anonymous map.
1515
1516 - It supports decommitting using VirtualFree, in contrast to
1517 section maps. This allows minimum footprint private maps,
1518 when using the (non-POSIX, yay-Linux) MAP_NORESERVE flag.
1519 */
1520 DWORD protect = gen_protect (prot, flags);
c2b10dc4 1521 DWORD alloc_type = MEM_TOP_DOWN | MEM_RESERVE
510a85cb 1522 | (noreserve (flags) ? 0 : MEM_COMMIT);
a020c74a
CV
1523 base = VirtualAlloc (*addr, len, alloc_type, protect);
1524 if (!base && addr && !fixed (flags))
1525 base = VirtualAlloc (NULL, len, alloc_type, protect);
1526 if (!base || (fixed (flags) && base != *addr))
1527 {
1528 if (!base)
1529 __seterrno ();
1530 else
1531 {
edd79f2d 1532 VirtualFree (base, 0, MEM_RELEASE);
a020c74a 1533 set_errno (EINVAL);
b5342210 1534 debug_printf ("VirtualAlloc: address shift with MAP_FIXED given");
a020c74a
CV
1535 }
1536 return INVALID_HANDLE_VALUE;
1537 }
1538 h = (HANDLE) 1; /* Fake handle to indicate success. */
1539 }
1540 else
1541 {
7d2ade33 1542 h = CreateMapping (get_handle (), len, off, get_access (), prot, flags);
a020c74a
CV
1543 if (!h)
1544 {
1545 __seterrno ();
b5342210 1546 debug_printf ("CreateMapping failed with %E");
a020c74a
CV
1547 return INVALID_HANDLE_VALUE;
1548 }
1549
570858c3 1550 base = MapView (h, *addr, len, get_access(), prot, flags, off);
a020c74a
CV
1551 if (!base || (fixed (flags) && base != *addr))
1552 {
1553 if (!base)
1554 __seterrno ();
1555 else
1556 {
f16706de 1557 NtUnmapViewOfSection (NtCurrentProcess (), base);
a020c74a 1558 set_errno (EINVAL);
b5342210 1559 debug_printf ("MapView: address shift with MAP_FIXED given");
a020c74a 1560 }
7d2ade33 1561 NtClose (h);
a020c74a
CV
1562 return INVALID_HANDLE_VALUE;
1563 }
a020c74a
CV
1564 }
1565 *addr = (caddr_t) base;
1566 return h;
1567}
1568
1569int
1570fhandler_dev_zero::munmap (HANDLE h, caddr_t addr, size_t len)
1571{
5a101414 1572 if (h == (HANDLE) 1) /* See fhandler_dev_zero::mmap. */
edd79f2d 1573 VirtualFree (addr, 0, MEM_RELEASE);
5a101414
CV
1574 else
1575 {
f16706de 1576 NtUnmapViewOfSection (NtCurrentProcess (), addr);
7d2ade33 1577 NtClose (h);
5a101414 1578 }
a020c74a
CV
1579 return 0;
1580}
1581
1582int
1583fhandler_dev_zero::msync (HANDLE h, caddr_t addr, size_t len, int flags)
1584{
1585 return 0;
1586}
1587
1588bool
1589fhandler_dev_zero::fixup_mmap_after_fork (HANDLE h, int prot, int flags,
1590 _off64_t offset, DWORD size,
1591 void *address)
1592{
1593 /* Re-create the map */
1594 void *base;
5a101414 1595 if (priv (flags) && !filler (prot))
a020c74a 1596 {
a020c74a 1597 DWORD alloc_type = MEM_RESERVE | (noreserve (flags) ? 0 : MEM_COMMIT);
b0c3293e 1598 /* Always allocate R/W so that ReadProcessMemory doesn't fail
de935f6d 1599 due to a non-writable target address. The protection is
b0c3293e
CV
1600 set to the correct one anyway in the fixup loop. */
1601 base = VirtualAlloc (address, size, alloc_type, PAGE_READWRITE);
a020c74a
CV
1602 }
1603 else
570858c3 1604 base = MapView (h, address, size, get_access (), prot, flags, offset);
a020c74a
CV
1605 if (base != address)
1606 {
1607 MEMORY_BASIC_INFORMATION m;
1608 VirtualQuery (address, &m, sizeof (m));
8b1ba065
CV
1609 system_printf ("requested %p != %p mem alloc base %p, state %p, "
1610 "size %d, %E", address, base, m.AllocationBase, m.State,
1611 m.RegionSize);
a020c74a
CV
1612 }
1613 return base == address;
1614}
1615
f90e23f2 1616/* Implementation for disk files and anonymous mappings. */
d12eba25 1617HANDLE
f90e23f2 1618fhandler_disk_file::mmap (caddr_t *addr, size_t len, int prot,
1727fba0 1619 int flags, _off64_t off)
d12eba25 1620{
570858c3 1621 HANDLE h = CreateMapping (get_handle (), len, off, get_access (),
7d2ade33 1622 prot, flags);
8595942c 1623 if (!h)
d12eba25
CV
1624 {
1625 __seterrno ();
b5342210 1626 debug_printf ("CreateMapping failed with %E");
d12eba25
CV
1627 return INVALID_HANDLE_VALUE;
1628 }
1629
570858c3 1630 void *base = MapView (h, *addr, len, get_access (), prot, flags, off);
a020c74a 1631 if (!base || (fixed (flags) && base != *addr))
d12eba25 1632 {
4ea34a68 1633 if (!base)
f90e23f2 1634 __seterrno ();
4ea34a68 1635 else
9867ecfd 1636 {
f16706de 1637 NtUnmapViewOfSection (NtCurrentProcess (), base);
4ea34a68 1638 set_errno (EINVAL);
b5342210 1639 debug_printf ("MapView: address shift with MAP_FIXED given");
9867ecfd 1640 }
7d2ade33 1641 NtClose (h);
d12eba25
CV
1642 return INVALID_HANDLE_VALUE;
1643 }
1644
1645 *addr = (caddr_t) base;
1646 return h;
1647}
1648
1649int
1650fhandler_disk_file::munmap (HANDLE h, caddr_t addr, size_t len)
1651{
f16706de 1652 NtUnmapViewOfSection (NtCurrentProcess (), addr);
7d2ade33 1653 NtClose (h);
d12eba25
CV
1654 return 0;
1655}
1656
1657int
1658fhandler_disk_file::msync (HANDLE h, caddr_t addr, size_t len, int flags)
1659{
c5eb7a49
CF
1660 const int retry = 100;
1661 /* The wisdom of google tells us that FlushViewOfFile may fail with
1662 ERROR_LOCK_VIOLATION if "if the memory system is writing dirty
1663 pages to disk". And, we've seen reports of this happening in the
1664 cygwin list. So retry 99 times and hope we get lucky. */
1665 for (int i = 0; i < retry; i++)
1666 if (FlushViewOfFile (addr, len))
b31aa390
CV
1667 {
1668 /* FlushViewOfFile just triggers the action and returns immediately,
1669 so it's equivalent to MS_ASYNC. MS_SYNC requires another call to
1670 FlushFileBuffers. */
1671 if (flags & MS_SYNC)
1672 FlushFileBuffers (h);
1673 return 0;
1674 }
c5eb7a49
CF
1675 else if (GetLastError () != ERROR_LOCK_VIOLATION)
1676 break;
1677 else if (i < (retry - 1))
1678 yield ();
1679
1680 __seterrno ();
1681 return -1;
1fd5e000
CF
1682}
1683
2e008fb9 1684bool
f90e23f2 1685fhandler_disk_file::fixup_mmap_after_fork (HANDLE h, int prot, int flags,
6d11044c
CV
1686 _off64_t offset, DWORD size,
1687 void *address)
c4efddf6 1688{
f90e23f2 1689 /* Re-create the map */
570858c3 1690 void *base = MapView (h, address, size, get_access (), prot, flags, offset);
f90e23f2 1691 if (base != address)
6d11044c 1692 {
f90e23f2
CV
1693 MEMORY_BASIC_INFORMATION m;
1694 VirtualQuery (address, &m, sizeof (m));
1695 system_printf ("requested %p != %p mem alloc base %p, state %p, "
1696 "size %d, %E", address, base, m.AllocationBase, m.State,
1697 m.RegionSize);
6d11044c 1698 }
f90e23f2
CV
1699 return base == address;
1700}
1701
1702HANDLE
1703fhandler_dev_mem::mmap (caddr_t *addr, size_t len, int prot,
1704 int flags, _off64_t off)
1705{
1706 if (off >= mem_size
1707 || (DWORD) len >= mem_size
1708 || off + len >= mem_size)
1709 {
1710 set_errno (EINVAL);
b5342210 1711 debug_printf ("-1 = mmap(): illegal parameter, set EINVAL");
f90e23f2
CV
1712 return INVALID_HANDLE_VALUE;
1713 }
1714
f90e23f2 1715 OBJECT_ATTRIBUTES attr;
8deb4118 1716 InitializeObjectAttributes (&attr, &ro_u_pmem,
f90e23f2
CV
1717 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
1718 NULL, NULL);
1719
1720 /* Section access is bit-wise ored, while on the Win32 level access
1721 is only one of the values. It's not quite clear if the section
1722 access has to be defined this way, or if SECTION_ALL_ACCESS would
1723 be sufficient but this worked fine so far, so why change? */
1724 ACCESS_MASK section_access;
1725 if (prot & PROT_WRITE)
1726 section_access = SECTION_MAP_READ | SECTION_MAP_WRITE;
6d11044c 1727 else
f90e23f2
CV
1728 section_access = SECTION_MAP_READ;
1729
1730 HANDLE h;
177dc6c7
CV
1731 NTSTATUS status = NtOpenSection (&h, section_access, &attr);
1732 if (!NT_SUCCESS (status))
f90e23f2 1733 {
177dc6c7 1734 __seterrno_from_nt_status (status);
b5342210 1735 debug_printf ("-1 = mmap(): NtOpenSection failed with %E");
f90e23f2
CV
1736 return INVALID_HANDLE_VALUE;
1737 }
1738
570858c3 1739 void *base = MapView (h, *addr, len, get_access (), prot,
70300fdb 1740 flags | MAP_ANONYMOUS, off);
a020c74a 1741 if (!base || (fixed (flags) && base != *addr))
f90e23f2
CV
1742 {
1743 if (!base)
de935f6d 1744 __seterrno ();
f90e23f2 1745 else
de935f6d 1746 {
f16706de 1747 NtUnmapViewOfSection (NtCurrentProcess (), base);
f90e23f2 1748 set_errno (EINVAL);
b5342210 1749 debug_printf ("MapView: address shift with MAP_FIXED given");
f90e23f2 1750 }
7d2ade33 1751 NtClose (h);
f90e23f2
CV
1752 return INVALID_HANDLE_VALUE;
1753 }
1754
1755 *addr = (caddr_t) base;
1756 return h;
1757}
1758
1759int
1760fhandler_dev_mem::munmap (HANDLE h, caddr_t addr, size_t len)
1761{
177dc6c7
CV
1762 NTSTATUS status;
1763 if (!NT_SUCCESS (status = NtUnmapViewOfSection (NtCurrentProcess (), addr)))
f90e23f2 1764 {
177dc6c7 1765 __seterrno_from_nt_status (status);
f90e23f2
CV
1766 return -1;
1767 }
7d2ade33 1768 NtClose (h);
f90e23f2
CV
1769 return 0;
1770}
1771
1772int
1773fhandler_dev_mem::msync (HANDLE h, caddr_t addr, size_t len, int flags)
1774{
1775 return 0;
1776}
1777
1778bool
1779fhandler_dev_mem::fixup_mmap_after_fork (HANDLE h, int prot, int flags,
1780 _off64_t offset, DWORD size,
1781 void *address)
1782{
570858c3
CV
1783 void *base = MapView (h, address, size, get_access (), prot,
1784 flags | MAP_ANONYMOUS, offset);
5ec14fe4
CF
1785 if (base != address)
1786 {
1787 MEMORY_BASIC_INFORMATION m;
0c55f6ed 1788 VirtualQuery (address, &m, sizeof (m));
f90e23f2
CV
1789 system_printf ("requested %p != %p mem alloc base %p, state %p, "
1790 "size %d, %E", address, base, m.AllocationBase, m.State,
1791 m.RegionSize);
5ec14fe4 1792 }
c4efddf6
CV
1793 return base == address;
1794}
1795
f90e23f2
CV
1796/* Call to re-create all the file mappings in a forked child. Called from
1797 the child in initialization. At this point we are passed a valid
1798 mmapped_areas map, and all the HANDLE's are valid for the child, but
1799 none of the mapped areas are in our address space. We need to iterate
1800 through the map, doing the MapViewOfFile calls. */
1fd5e000
CF
1801
1802int __stdcall
713fb38b 1803fixup_mmaps_after_fork (HANDLE parent)
1fd5e000 1804{
1fd5e000 1805 /* Iterate through the map */
230a3c86
CV
1806 mmap_list *map_list;
1807 LIST_FOREACH (map_list, &mmapped_areas.lists, ml_next)
1fd5e000 1808 {
4544f7f6 1809 mmap_record *rec;
230a3c86 1810 LIST_FOREACH (rec, &map_list->recs, mr_next)
1fd5e000 1811 {
b5342210
CV
1812 debug_printf ("fd %d, h 0x%x, address %p, len 0x%x, prot: 0x%x, "
1813 "flags: 0x%x, offset %X",
1814 rec->get_fd (), rec->get_handle (), rec->get_address (),
1815 rec->get_len (), rec->get_prot (), rec->get_flags (),
1816 rec->get_offset ());
4544f7f6
CV
1817
1818 fhandler_base *fh = rec->alloc_fh ();
2e008fb9 1819 bool ret = fh->fixup_mmap_after_fork (rec->get_handle (),
f90e23f2 1820 rec->get_prot (),
b5342210 1821 rec->get_flags () | MAP_FIXED,
4544f7f6 1822 rec->get_offset (),
f90e23f2 1823 rec->get_len (),
4544f7f6
CV
1824 rec->get_address ());
1825 rec->free_fh (fh);
1826
1827 if (!ret)
5a101414
CV
1828 {
1829 if (rec->attached ())
de935f6d 1830 {
5a101414
CV
1831 system_printf ("Warning: Fixup mapping beyond EOF failed");
1832 continue;
1833 }
1834 return -1;
1835 }
f90e23f2
CV
1836
1837 MEMORY_BASIC_INFORMATION mbi;
1838 DWORD old_prot;
1839
1840 for (char *address = rec->get_address ();
1841 address < rec->get_address () + rec->get_len ();
1842 address += mbi.RegionSize)
4544f7f6 1843 {
f90e23f2 1844 if (!VirtualQueryEx (parent, address, &mbi, sizeof mbi))
de935f6d 1845 {
f90e23f2 1846 system_printf ("VirtualQueryEx failed for MAP_PRIVATE "
de935f6d 1847 "address %p, %E", address);
f90e23f2
CV
1848 return -1;
1849 }
5a101414 1850 /* Just skip reserved pages. */
f90e23f2 1851 if (mbi.State == MEM_RESERVE)
5a101414 1852 continue;
f90e23f2 1853 /* Copy-on-write pages must be copied to the child to circumvent
de935f6d 1854 a strange notion how copy-on-write is supposed to work. */
f90e23f2
CV
1855 if (rec->priv ())
1856 {
2e19497a 1857 if (rec->noreserve ()
a020c74a 1858 && !VirtualAlloc (address, mbi.RegionSize,
de935f6d 1859 MEM_COMMIT, PAGE_READWRITE))
a020c74a
CV
1860 {
1861 system_printf ("VirtualAlloc failed for MAP_PRIVATE "
1862 "address %p, %E", address);
1863 return -1;
1864 }
f90e23f2
CV
1865 if (mbi.Protect == PAGE_NOACCESS
1866 && !VirtualProtectEx (parent, address, mbi.RegionSize,
1867 PAGE_READONLY, &old_prot))
1868 {
1869 system_printf ("VirtualProtectEx failed for MAP_PRIVATE "
1870 "address %p, %E", address);
1871 return -1;
1872 }
2193d02b
CV
1873 else if ((mbi.AllocationProtect == PAGE_WRITECOPY
1874 || mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY)
a020c74a
CV
1875 && (mbi.Protect == PAGE_READWRITE
1876 || mbi.Protect == PAGE_EXECUTE_READWRITE))
2193d02b
CV
1877 /* A WRITECOPY page which has been written to is set to
1878 READWRITE, but that's an incompatible protection to
1879 set the page to. Convert the protection to WRITECOPY
1880 so that the below VirtualProtect doesn't fail. */
1881 mbi.Protect <<= 1;
1882
f90e23f2
CV
1883 if (!ReadProcessMemory (parent, address, address,
1884 mbi.RegionSize, NULL))
1885 {
1886 system_printf ("ReadProcessMemory failed for MAP_PRIVATE "
1887 "address %p, %E", address);
1888 return -1;
1889 }
1890 if (mbi.Protect == PAGE_NOACCESS
1891 && !VirtualProtectEx (parent, address, mbi.RegionSize,
1892 PAGE_NOACCESS, &old_prot))
1893 {
1894 system_printf ("WARNING: VirtualProtectEx to return to "
1895 "PAGE_NOACCESS state in parent failed for "
1896 "MAP_PRIVATE address %p, %E", address);
1897 return -1;
1898 }
1899 }
b0c3293e 1900 /* Set child page protection to parent protection */
f90e23f2
CV
1901 if (!VirtualProtect (address, mbi.RegionSize,
1902 mbi.Protect, &old_prot))
1903 {
a020c74a
CV
1904 MEMORY_BASIC_INFORMATION m;
1905 VirtualQuery (address, &m, sizeof m);
f90e23f2 1906 system_printf ("VirtualProtect failed for "
de935f6d 1907 "address %p, "
a020c74a
CV
1908 "parentstate: 0x%x, "
1909 "state: 0x%x, "
1910 "parentprot: 0x%x, "
1911 "prot: 0x%x, %E",
1912 address, mbi.State, m.State,
1913 mbi.Protect, m.Protect);
f90e23f2
CV
1914 return -1;
1915 }
84aeff41
CF
1916 }
1917 }
1918 }
1fd5e000
CF
1919
1920 debug_printf ("succeeded");
1fd5e000
CF
1921 return 0;
1922}
This page took 0.639221 seconds and 5 git commands to generate.