]>
Commit | Line | Data |
---|---|---|
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 | |
6 | This file is part of Cygwin. | |
7 | ||
8 | This software is a copyrighted work licensed under the terms of the | |
9 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
10 | details. */ | |
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. */ |
50 | static fhandler_dev_zero fh_anonymous; | |
a020c74a | 51 | |
a1591d3b CV |
52 | /* Used for thread synchronization while accessing mmap bookkeeping lists. */ |
53 | static 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. */ |
58 | static inline bool | |
59 | priv (int flags) | |
60 | { | |
61 | return (flags & MAP_PRIVATE) == MAP_PRIVATE; | |
62 | } | |
63 | ||
64 | static inline bool | |
65 | fixed (int flags) | |
66 | { | |
67 | return (flags & MAP_FIXED) == MAP_FIXED; | |
68 | } | |
69 | ||
70 | static inline bool | |
71 | anonymous (int flags) | |
72 | { | |
73 | return (flags & MAP_ANONYMOUS) == MAP_ANONYMOUS; | |
74 | } | |
75 | ||
76 | static inline bool | |
77 | noreserve (int flags) | |
78 | { | |
79 | return (flags & MAP_NORESERVE) == MAP_NORESERVE; | |
80 | } | |
81 | ||
82 | static inline bool | |
83 | autogrow (int flags) | |
84 | { | |
85 | return (flags & MAP_AUTOGROW) == MAP_AUTOGROW; | |
86 | } | |
4544f7f6 | 87 | |
5a101414 CV |
88 | static inline bool |
89 | attached (int prot) | |
90 | { | |
91 | return (prot & __PROT_ATTACH) == __PROT_ATTACH; | |
92 | } | |
93 | ||
94 | static inline bool | |
95 | filler (int prot) | |
96 | { | |
97 | return (prot & __PROT_FILLER) == __PROT_FILLER; | |
98 | } | |
99 | ||
100 | static inline DWORD | |
101 | gen_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 | 117 | static inline DWORD |
5a101414 | 118 | gen_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 | 139 | static HANDLE |
570858c3 | 140 | CreateMapping (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, §ionsize, |
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, §ionsize, |
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, §ionsize, |
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, §ionsize, |
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 | 192 | static void * |
570858c3 CV |
193 | MapView (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 |
250 | class 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 | 323 | class 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 | 344 | class 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 | 355 | static mmap_areas mmapped_areas; |
f90e23f2 CV |
356 | |
357 | bool | |
358 | mmap_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 | 364 | DWORD |
f90e23f2 | 365 | mmap_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 |
385 | bool |
386 | mmap_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 |
404 | void |
405 | mmap_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 | 423 | mmap_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 |
449 | bool |
450 | mmap_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 | 477 | bool |
4544f7f6 | 478 | mmap_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 | 501 | int |
857b65dd | 502 | mmap_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 |
510 | fhandler_base * |
511 | mmap_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 | ||
534 | void | |
535 | mmap_record::free_fh (fhandler_base *fh) | |
536 | { | |
a020c74a | 537 | if (!anonymous ()) |
1c1b04b8 | 538 | delete fh; |
7ade56ca CV |
539 | } |
540 | ||
c6dd43f2 | 541 | mmap_record * |
ac706ac1 | 542 | mmap_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 | 555 | void |
230a3c86 | 556 | mmap_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 | 569 | bool |
230a3c86 | 570 | mmap_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 | 579 | caddr_t |
230a3c86 | 580 | mmap_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 |
629 | mmap_list * |
630 | mmap_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 |
646 | mmap_list * |
647 | mmap_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 | ||
657 | void | |
230a3c86 | 658 | mmap_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. */ | |
666 | bool | |
667 | is_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 |
713 | mmap_region_status |
714 | mmap_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 |
764 | out: |
765 | LIST_UNLOCK (); | |
766 | return ret; | |
5a101414 CV |
767 | } |
768 | ||
769 | static caddr_t | |
230a3c86 | 770 | mmap_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 |
793 | extern "C" void * |
794 | mmap64 (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 |
999 | go_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 |
1111 | out_with_unlock: |
1112 | LIST_UNLOCK (); | |
f90e23f2 | 1113 | |
a1591d3b | 1114 | out: |
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 |
1126 | extern "C" void * |
1127 | mmap (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 | 1134 | extern "C" int |
807dfb3d | 1135 | munmap (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 | 1197 | extern "C" int |
807dfb3d | 1198 | msync (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 | 1249 | out: |
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 | ||
1257 | extern "C" int | |
1258 | mprotect (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 |
1342 | out: |
1343 | ||
b9aa8149 | 1344 | syscall_printf ("%R = mprotect ()", ret ? 0 : -1); |
5a101414 | 1345 | return ret ? 0 : -1; |
807dfb3d CV |
1346 | } |
1347 | ||
1f5c3042 CV |
1348 | extern "C" int |
1349 | mlock (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 | ||
1405 | extern "C" int | |
1406 | munlock (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 |
1425 | extern "C" int |
1426 | posix_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 | */ | |
1468 | HANDLE | |
f90e23f2 | 1469 | fhandler_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 | ||
1476 | int | |
1477 | fhandler_base::munmap (HANDLE h, caddr_t addr, size_t len) | |
1478 | { | |
1479 | set_errno (ENODEV); | |
1480 | return -1; | |
1481 | } | |
1482 | ||
1483 | int | |
1484 | fhandler_base::msync (HANDLE h, caddr_t addr, size_t len, int flags) | |
1485 | { | |
1486 | set_errno (ENODEV); | |
1487 | return -1; | |
1488 | } | |
1489 | ||
2e008fb9 | 1490 | bool |
f90e23f2 | 1491 | fhandler_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. */ | |
1501 | HANDLE | |
1502 | fhandler_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 | ||
1569 | int | |
1570 | fhandler_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 | ||
1582 | int | |
1583 | fhandler_dev_zero::msync (HANDLE h, caddr_t addr, size_t len, int flags) | |
1584 | { | |
1585 | return 0; | |
1586 | } | |
1587 | ||
1588 | bool | |
1589 | fhandler_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 | 1617 | HANDLE |
f90e23f2 | 1618 | fhandler_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 | ||
1649 | int | |
1650 | fhandler_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 | ||
1657 | int | |
1658 | fhandler_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 | 1684 | bool |
f90e23f2 | 1685 | fhandler_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 | ||
1702 | HANDLE | |
1703 | fhandler_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 | ||
1759 | int | |
1760 | fhandler_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 | ||
1772 | int | |
1773 | fhandler_dev_mem::msync (HANDLE h, caddr_t addr, size_t len, int flags) | |
1774 | { | |
1775 | return 0; | |
1776 | } | |
1777 | ||
1778 | bool | |
1779 | fhandler_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 | |
1802 | int __stdcall | |
713fb38b | 1803 | fixup_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 | } |