ntdll: Don't clear the last page of the initial stack.
[wine.git] / dlls / ntdll / virtual.c
bloba452c58a44d83690367e867c321fad4b52424562
1 /*
2 * Win32 virtual memory functions
4 * Copyright 1997, 2002 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
22 #include "wine/port.h"
24 #include <assert.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #ifdef HAVE_SYS_STAT_H
36 # include <sys/stat.h>
37 #endif
38 #ifdef HAVE_SYS_MMAN_H
39 # include <sys/mman.h>
40 #endif
41 #ifdef HAVE_SYS_SYSINFO_H
42 # include <sys/sysinfo.h>
43 #endif
44 #ifdef HAVE_VALGRIND_VALGRIND_H
45 # include <valgrind/valgrind.h>
46 #endif
48 #include "ntstatus.h"
49 #define WIN32_NO_STATUS
50 #define NONAMELESSUNION
51 #include "windef.h"
52 #include "winternl.h"
53 #include "wine/library.h"
54 #include "wine/server.h"
55 #include "wine/exception.h"
56 #include "wine/rbtree.h"
57 #include "wine/debug.h"
58 #include "ntdll_misc.h"
60 WINE_DEFAULT_DEBUG_CHANNEL(virtual);
61 WINE_DECLARE_DEBUG_CHANNEL(module);
63 #ifndef MAP_NORESERVE
64 #define MAP_NORESERVE 0
65 #endif
67 /* File view */
68 struct file_view
70 struct wine_rb_entry entry; /* entry in global view tree */
71 void *base; /* base address */
72 size_t size; /* size in bytes */
73 HANDLE mapping; /* handle to the file mapping */
74 unsigned int protect; /* protection for all pages at allocation time and SEC_* flags */
77 /* per-page protection flags */
78 #define VPROT_READ 0x01
79 #define VPROT_WRITE 0x02
80 #define VPROT_EXEC 0x04
81 #define VPROT_WRITECOPY 0x08
82 #define VPROT_GUARD 0x10
83 #define VPROT_COMMITTED 0x20
84 #define VPROT_WRITEWATCH 0x40
85 /* per-mapping protection flags */
86 #define VPROT_SYSTEM 0x0200 /* system view (underlying mmap not under our control) */
88 /* Conversion from VPROT_* to Win32 flags */
89 static const BYTE VIRTUAL_Win32Flags[16] =
91 PAGE_NOACCESS, /* 0 */
92 PAGE_READONLY, /* READ */
93 PAGE_READWRITE, /* WRITE */
94 PAGE_READWRITE, /* READ | WRITE */
95 PAGE_EXECUTE, /* EXEC */
96 PAGE_EXECUTE_READ, /* READ | EXEC */
97 PAGE_EXECUTE_READWRITE, /* WRITE | EXEC */
98 PAGE_EXECUTE_READWRITE, /* READ | WRITE | EXEC */
99 PAGE_WRITECOPY, /* WRITECOPY */
100 PAGE_WRITECOPY, /* READ | WRITECOPY */
101 PAGE_WRITECOPY, /* WRITE | WRITECOPY */
102 PAGE_WRITECOPY, /* READ | WRITE | WRITECOPY */
103 PAGE_EXECUTE_WRITECOPY, /* EXEC | WRITECOPY */
104 PAGE_EXECUTE_WRITECOPY, /* READ | EXEC | WRITECOPY */
105 PAGE_EXECUTE_WRITECOPY, /* WRITE | EXEC | WRITECOPY */
106 PAGE_EXECUTE_WRITECOPY /* READ | WRITE | EXEC | WRITECOPY */
109 static struct wine_rb_tree views_tree;
111 static RTL_CRITICAL_SECTION csVirtual;
112 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
114 0, 0, &csVirtual,
115 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
116 0, 0, { (DWORD_PTR)(__FILE__ ": csVirtual") }
118 static RTL_CRITICAL_SECTION csVirtual = { &critsect_debug, -1, 0, 0, 0, 0 };
120 #ifdef __i386__
121 static const UINT page_shift = 12;
122 static const UINT_PTR page_mask = 0xfff;
123 /* Note: these are Windows limits, you cannot change them. */
124 static void *address_space_limit = (void *)0xc0000000; /* top of the total available address space */
125 static void *user_space_limit = (void *)0x7fff0000; /* top of the user address space */
126 static void *working_set_limit = (void *)0x7fff0000; /* top of the current working set */
127 static void *address_space_start = (void *)0x110000; /* keep DOS area clear */
128 #elif defined(__x86_64__)
129 static const UINT page_shift = 12;
130 static const UINT_PTR page_mask = 0xfff;
131 static void *address_space_limit = (void *)0x7fffffff0000;
132 static void *user_space_limit = (void *)0x7fffffff0000;
133 static void *working_set_limit = (void *)0x7fffffff0000;
134 static void *address_space_start = (void *)0x10000;
135 #else
136 UINT_PTR page_size = 0;
137 static UINT page_shift;
138 static UINT_PTR page_mask;
139 static void *address_space_limit;
140 static void *user_space_limit;
141 static void *working_set_limit;
142 static void *address_space_start = (void *)0x10000;
143 #endif /* __i386__ */
144 static const BOOL is_win64 = (sizeof(void *) > sizeof(int));
146 #define ROUND_ADDR(addr,mask) \
147 ((void *)((UINT_PTR)(addr) & ~(UINT_PTR)(mask)))
149 #define ROUND_SIZE(addr,size) \
150 (((SIZE_T)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask)
152 #define VIRTUAL_DEBUG_DUMP_VIEW(view) \
153 do { if (TRACE_ON(virtual)) VIRTUAL_DumpView(view); } while (0)
155 #ifdef _WIN64 /* on 64-bit the page protection bytes use a 2-level table */
156 static const size_t pages_vprot_shift = 20;
157 static const size_t pages_vprot_mask = (1 << 20) - 1;
158 static size_t pages_vprot_size;
159 static BYTE **pages_vprot;
160 #else /* on 32-bit we use a simple array with one byte per page */
161 static BYTE *pages_vprot;
162 #endif
164 static struct file_view *view_block_start, *view_block_end, *next_free_view;
165 static const size_t view_block_size = 0x100000;
166 static void *preload_reserve_start;
167 static void *preload_reserve_end;
168 static BOOL use_locks;
169 static BOOL force_exec_prot; /* whether to force PROT_EXEC on all PROT_READ mmaps */
171 static inline int is_view_valloc( const struct file_view *view )
173 return !(view->protect & (SEC_FILE | SEC_RESERVE | SEC_COMMIT));
176 /***********************************************************************
177 * get_page_vprot
179 * Return the page protection byte.
181 static BYTE get_page_vprot( const void *addr )
183 size_t idx = (size_t)addr >> page_shift;
185 #ifdef _WIN64
186 return pages_vprot[idx >> pages_vprot_shift][idx & pages_vprot_mask];
187 #else
188 return pages_vprot[idx];
189 #endif
193 /***********************************************************************
194 * set_page_vprot
196 * Set a range of page protection bytes.
198 static void set_page_vprot( const void *addr, size_t size, BYTE vprot )
200 size_t idx = (size_t)addr >> page_shift;
201 size_t end = ((size_t)addr + size + page_mask) >> page_shift;
203 #ifdef _WIN64
204 while (idx >> pages_vprot_shift != end >> pages_vprot_shift)
206 size_t dir_size = pages_vprot_mask + 1 - (idx & pages_vprot_mask);
207 memset( pages_vprot[idx >> pages_vprot_shift] + (idx & pages_vprot_mask), vprot, dir_size );
208 idx += dir_size;
210 memset( pages_vprot[idx >> pages_vprot_shift] + (idx & pages_vprot_mask), vprot, end - idx );
211 #else
212 memset( pages_vprot + idx, vprot, end - idx );
213 #endif
217 /***********************************************************************
218 * set_page_vprot_bits
220 * Set or clear bits in a range of page protection bytes.
222 static void set_page_vprot_bits( const void *addr, size_t size, BYTE set, BYTE clear )
224 size_t idx = (size_t)addr >> page_shift;
225 size_t end = ((size_t)addr + size + page_mask) >> page_shift;
227 #ifdef _WIN64
228 for ( ; idx < end; idx++)
230 BYTE *ptr = pages_vprot[idx >> pages_vprot_shift] + (idx & pages_vprot_mask);
231 *ptr = (*ptr & ~clear) | set;
233 #else
234 for ( ; idx < end; idx++) pages_vprot[idx] = (pages_vprot[idx] & ~clear) | set;
235 #endif
239 /***********************************************************************
240 * alloc_pages_vprot
242 * Allocate the page protection bytes for a given range.
244 static BOOL alloc_pages_vprot( const void *addr, size_t size )
246 #ifdef _WIN64
247 size_t idx = (size_t)addr >> page_shift;
248 size_t end = ((size_t)addr + size + page_mask) >> page_shift;
249 size_t i;
250 void *ptr;
252 assert( end <= pages_vprot_size << pages_vprot_shift );
253 for (i = idx >> pages_vprot_shift; i < (end + pages_vprot_mask) >> pages_vprot_shift; i++)
255 if (pages_vprot[i]) continue;
256 if ((ptr = wine_anon_mmap( NULL, pages_vprot_mask + 1, PROT_READ | PROT_WRITE, 0 )) == (void *)-1)
257 return FALSE;
258 pages_vprot[i] = ptr;
260 #endif
261 return TRUE;
265 /***********************************************************************
266 * compare_view
268 * View comparison function used for the rb tree.
270 static int compare_view( const void *addr, const struct wine_rb_entry *entry )
272 struct file_view *view = WINE_RB_ENTRY_VALUE( entry, struct file_view, entry );
274 if (addr < view->base) return -1;
275 if (addr > view->base) return 1;
276 return 0;
280 /***********************************************************************
281 * VIRTUAL_GetProtStr
283 static const char *VIRTUAL_GetProtStr( BYTE prot )
285 static char buffer[6];
286 buffer[0] = (prot & VPROT_COMMITTED) ? 'c' : '-';
287 buffer[1] = (prot & VPROT_GUARD) ? 'g' : ((prot & VPROT_WRITEWATCH) ? 'H' : '-');
288 buffer[2] = (prot & VPROT_READ) ? 'r' : '-';
289 buffer[3] = (prot & VPROT_WRITECOPY) ? 'W' : ((prot & VPROT_WRITE) ? 'w' : '-');
290 buffer[4] = (prot & VPROT_EXEC) ? 'x' : '-';
291 buffer[5] = 0;
292 return buffer;
296 /***********************************************************************
297 * VIRTUAL_GetUnixProt
299 * Convert page protections to protection for mmap/mprotect.
301 static int VIRTUAL_GetUnixProt( BYTE vprot )
303 int prot = 0;
304 if ((vprot & VPROT_COMMITTED) && !(vprot & VPROT_GUARD))
306 if (vprot & VPROT_READ) prot |= PROT_READ;
307 if (vprot & VPROT_WRITE) prot |= PROT_WRITE | PROT_READ;
308 if (vprot & VPROT_WRITECOPY) prot |= PROT_WRITE | PROT_READ;
309 if (vprot & VPROT_EXEC) prot |= PROT_EXEC | PROT_READ;
310 if (vprot & VPROT_WRITEWATCH) prot &= ~PROT_WRITE;
312 if (!prot) prot = PROT_NONE;
313 return prot;
317 /***********************************************************************
318 * VIRTUAL_DumpView
320 static void VIRTUAL_DumpView( struct file_view *view )
322 UINT i, count;
323 char *addr = view->base;
324 BYTE prot = get_page_vprot( addr );
326 TRACE( "View: %p - %p", addr, addr + view->size - 1 );
327 if (view->protect & VPROT_SYSTEM)
328 TRACE( " (builtin image)\n" );
329 else if (view->protect & SEC_IMAGE)
330 TRACE( " (image) %p\n", view->mapping );
331 else if (view->protect & SEC_FILE)
332 TRACE( " (file) %p\n", view->mapping );
333 else if (view->protect & (SEC_RESERVE | SEC_COMMIT))
334 TRACE( " (anonymous) %p\n", view->mapping );
335 else
336 TRACE( " (valloc)\n");
338 for (count = i = 1; i < view->size >> page_shift; i++, count++)
340 BYTE next = get_page_vprot( addr + (count << page_shift) );
341 if (next == prot) continue;
342 TRACE( " %p - %p %s\n",
343 addr, addr + (count << page_shift) - 1, VIRTUAL_GetProtStr(prot) );
344 addr += (count << page_shift);
345 prot = next;
346 count = 0;
348 if (count)
349 TRACE( " %p - %p %s\n",
350 addr, addr + (count << page_shift) - 1, VIRTUAL_GetProtStr(prot) );
354 /***********************************************************************
355 * VIRTUAL_Dump
357 #ifdef WINE_VM_DEBUG
358 static void VIRTUAL_Dump(void)
360 sigset_t sigset;
361 struct file_view *view;
363 TRACE( "Dump of all virtual memory views:\n" );
364 server_enter_uninterrupted_section( &csVirtual, &sigset );
365 WINE_RB_FOR_EACH_ENTRY( view, &views_tree, struct file_view, entry )
367 VIRTUAL_DumpView( view );
369 server_leave_uninterrupted_section( &csVirtual, &sigset );
371 #endif
374 /***********************************************************************
375 * VIRTUAL_FindView
377 * Find the view containing a given address. The csVirtual section must be held by caller.
379 * PARAMS
380 * addr [I] Address
382 * RETURNS
383 * View: Success
384 * NULL: Failure
386 static struct file_view *VIRTUAL_FindView( const void *addr, size_t size )
388 struct wine_rb_entry *ptr = views_tree.root;
390 if ((const char *)addr + size < (const char *)addr) return NULL; /* overflow */
392 while (ptr)
394 struct file_view *view = WINE_RB_ENTRY_VALUE( ptr, struct file_view, entry );
396 if (view->base > addr) ptr = ptr->left;
397 else if ((const char *)view->base + view->size <= (const char *)addr) ptr = ptr->right;
398 else if ((const char *)view->base + view->size < (const char *)addr + size) break; /* size too large */
399 else return view;
401 return NULL;
405 /***********************************************************************
406 * get_mask
408 static inline UINT_PTR get_mask( ULONG zero_bits )
410 if (!zero_bits) return 0xffff; /* allocations are aligned to 64K by default */
411 if (zero_bits < page_shift) zero_bits = page_shift;
412 if (zero_bits > 21) return 0;
413 return (1 << zero_bits) - 1;
417 /***********************************************************************
418 * find_view_range
420 * Find the first view overlapping at least part of the specified range.
421 * The csVirtual section must be held by caller.
423 static struct file_view *find_view_range( const void *addr, size_t size )
425 struct wine_rb_entry *ptr = views_tree.root;
427 while (ptr)
429 struct file_view *view = WINE_RB_ENTRY_VALUE( ptr, struct file_view, entry );
431 if ((const char *)view->base >= (const char *)addr + size) ptr = ptr->left;
432 else if ((const char *)view->base + view->size <= (const char *)addr) ptr = ptr->right;
433 else return view;
435 return NULL;
439 /***********************************************************************
440 * find_free_area
442 * Find a free area between views inside the specified range.
443 * The csVirtual section must be held by caller.
445 static void *find_free_area( void *base, void *end, size_t size, size_t mask, int top_down )
447 struct wine_rb_entry *first = NULL, *ptr = views_tree.root;
448 void *start;
450 /* find the first (resp. last) view inside the range */
451 while (ptr)
453 struct file_view *view = WINE_RB_ENTRY_VALUE( ptr, struct file_view, entry );
454 if ((char *)view->base + view->size >= (char *)end)
456 end = min( end, view->base );
457 ptr = ptr->left;
459 else if (view->base <= base)
461 base = max( (char *)base, (char *)view->base + view->size );
462 ptr = ptr->right;
464 else
466 first = ptr;
467 ptr = top_down ? ptr->right : ptr->left;
471 if (top_down)
473 start = ROUND_ADDR( (char *)end - size, mask );
474 if (start >= end || start < base) return NULL;
476 while (first)
478 struct file_view *view = WINE_RB_ENTRY_VALUE( first, struct file_view, entry );
480 if ((char *)view->base + view->size <= (char *)start) break;
481 start = ROUND_ADDR( (char *)view->base - size, mask );
482 /* stop if remaining space is not large enough */
483 if (!start || start >= end || start < base) return NULL;
484 first = wine_rb_prev( first );
487 else
489 start = ROUND_ADDR( (char *)base + mask, mask );
490 if (!start || start >= end || (char *)end - (char *)start < size) return NULL;
492 while (first)
494 struct file_view *view = WINE_RB_ENTRY_VALUE( first, struct file_view, entry );
496 if ((char *)view->base >= (char *)start + size) break;
497 start = ROUND_ADDR( (char *)view->base + view->size + mask, mask );
498 /* stop if remaining space is not large enough */
499 if (!start || start >= end || (char *)end - (char *)start < size) return NULL;
500 first = wine_rb_next( first );
503 return start;
507 /***********************************************************************
508 * add_reserved_area
510 * Add a reserved area to the list maintained by libwine.
511 * The csVirtual section must be held by caller.
513 static void add_reserved_area( void *addr, size_t size )
515 TRACE( "adding %p-%p\n", addr, (char *)addr + size );
517 if (addr < user_space_limit)
519 /* unmap the part of the area that is below the limit */
520 assert( (char *)addr + size > (char *)user_space_limit );
521 munmap( addr, (char *)user_space_limit - (char *)addr );
522 size -= (char *)user_space_limit - (char *)addr;
523 addr = user_space_limit;
525 /* blow away existing mappings */
526 wine_anon_mmap( addr, size, PROT_NONE, MAP_NORESERVE | MAP_FIXED );
527 wine_mmap_add_reserved_area( addr, size );
531 /***********************************************************************
532 * remove_reserved_area
534 * Remove a reserved area from the list maintained by libwine.
535 * The csVirtual section must be held by caller.
537 static void remove_reserved_area( void *addr, size_t size )
539 struct file_view *view;
541 TRACE( "removing %p-%p\n", addr, (char *)addr + size );
542 wine_mmap_remove_reserved_area( addr, size, 0 );
544 /* unmap areas not covered by an existing view */
545 WINE_RB_FOR_EACH_ENTRY( view, &views_tree, struct file_view, entry )
547 if ((char *)view->base >= (char *)addr + size) break;
548 if ((char *)view->base + view->size <= (char *)addr) continue;
549 if (view->base > addr) munmap( addr, (char *)view->base - (char *)addr );
550 if ((char *)view->base + view->size > (char *)addr + size) return;
551 size = (char *)addr + size - ((char *)view->base + view->size);
552 addr = (char *)view->base + view->size;
554 munmap( addr, size );
558 struct area_boundary
560 void *base;
561 size_t size;
562 void *boundary;
565 /***********************************************************************
566 * get_area_boundary_callback
568 * Get lowest boundary address between reserved area and non-reserved area
569 * in the specified region. If no boundaries are found, result is NULL.
570 * The csVirtual section must be held by caller.
572 static int get_area_boundary_callback( void *start, size_t size, void *arg )
574 struct area_boundary *area = arg;
575 void *end = (char *)start + size;
577 area->boundary = NULL;
578 if (area->base >= end) return 0;
579 if ((char *)start >= (char *)area->base + area->size) return 1;
580 if (area->base >= start)
582 if ((char *)area->base + area->size > (char *)end)
584 area->boundary = end;
585 return 1;
587 return 0;
589 area->boundary = start;
590 return 1;
594 /***********************************************************************
595 * is_beyond_limit
597 * Check if an address range goes beyond a given limit.
599 static inline BOOL is_beyond_limit( const void *addr, size_t size, const void *limit )
601 return (addr >= limit || (const char *)addr + size > (const char *)limit);
605 /***********************************************************************
606 * unmap_area
608 * Unmap an area, or simply replace it by an empty mapping if it is
609 * in a reserved area. The csVirtual section must be held by caller.
611 static inline void unmap_area( void *addr, size_t size )
613 switch (wine_mmap_is_in_reserved_area( addr, size ))
615 case -1: /* partially in a reserved area */
617 struct area_boundary area;
618 size_t lower_size;
619 area.base = addr;
620 area.size = size;
621 wine_mmap_enum_reserved_areas( get_area_boundary_callback, &area, 0 );
622 assert( area.boundary );
623 lower_size = (char *)area.boundary - (char *)addr;
624 unmap_area( addr, lower_size );
625 unmap_area( area.boundary, size - lower_size );
626 break;
628 case 1: /* in a reserved area */
629 wine_anon_mmap( addr, size, PROT_NONE, MAP_NORESERVE | MAP_FIXED );
630 break;
631 default:
632 case 0: /* not in a reserved area */
633 if (is_beyond_limit( addr, size, user_space_limit ))
634 add_reserved_area( addr, size );
635 else
636 munmap( addr, size );
637 break;
642 /***********************************************************************
643 * alloc_view
645 * Allocate a new view. The csVirtual section must be held by caller.
647 static struct file_view *alloc_view(void)
649 if (next_free_view)
651 struct file_view *ret = next_free_view;
652 next_free_view = *(struct file_view **)ret;
653 return ret;
655 if (view_block_start == view_block_end)
657 void *ptr = wine_anon_mmap( NULL, view_block_size, PROT_READ | PROT_WRITE, 0 );
658 if (ptr == (void *)-1) return NULL;
659 view_block_start = ptr;
660 view_block_end = view_block_start + view_block_size / sizeof(*view_block_start);
662 return view_block_start++;
666 /***********************************************************************
667 * delete_view
669 * Deletes a view. The csVirtual section must be held by caller.
671 static void delete_view( struct file_view *view ) /* [in] View */
673 if (!(view->protect & VPROT_SYSTEM)) unmap_area( view->base, view->size );
674 wine_rb_remove( &views_tree, &view->entry );
675 if (view->mapping) close_handle( view->mapping );
676 *(struct file_view **)view = next_free_view;
677 next_free_view = view;
681 /***********************************************************************
682 * create_view
684 * Create a view. The csVirtual section must be held by caller.
686 static NTSTATUS create_view( struct file_view **view_ret, void *base, size_t size, unsigned int vprot )
688 struct file_view *view;
689 int unix_prot = VIRTUAL_GetUnixProt( vprot );
691 assert( !((UINT_PTR)base & page_mask) );
692 assert( !(size & page_mask) );
694 /* Check for overlapping views. This can happen if the previous view
695 * was a system view that got unmapped behind our back. In that case
696 * we recover by simply deleting it. */
698 while ((view = find_view_range( base, size )))
700 TRACE( "overlapping view %p-%p for %p-%p\n",
701 view->base, (char *)view->base + view->size, base, (char *)base + size );
702 assert( view->protect & VPROT_SYSTEM );
703 delete_view( view );
706 if (!alloc_pages_vprot( base, size )) return STATUS_NO_MEMORY;
708 /* Create the view structure */
710 if (!(view = alloc_view()))
712 FIXME( "out of memory for %p-%p\n", base, (char *)base + size );
713 return STATUS_NO_MEMORY;
716 view->base = base;
717 view->size = size;
718 view->mapping = 0;
719 view->protect = vprot;
720 set_page_vprot( base, size, vprot );
722 wine_rb_put( &views_tree, view->base, &view->entry );
724 *view_ret = view;
726 if (force_exec_prot && (unix_prot & PROT_READ) && !(unix_prot & PROT_EXEC))
728 TRACE( "forcing exec permission on %p-%p\n", base, (char *)base + size - 1 );
729 mprotect( base, size, unix_prot | PROT_EXEC );
731 return STATUS_SUCCESS;
735 /***********************************************************************
736 * VIRTUAL_GetWin32Prot
738 * Convert page protections to Win32 flags.
740 static DWORD VIRTUAL_GetWin32Prot( BYTE vprot, unsigned int map_prot )
742 DWORD ret = VIRTUAL_Win32Flags[vprot & 0x0f];
743 if (vprot & VPROT_GUARD) ret |= PAGE_GUARD;
744 if (map_prot & SEC_NOCACHE) ret |= PAGE_NOCACHE;
745 return ret;
749 /***********************************************************************
750 * get_vprot_flags
752 * Build page protections from Win32 flags.
754 * PARAMS
755 * protect [I] Win32 protection flags
757 * RETURNS
758 * Value of page protection flags
760 static NTSTATUS get_vprot_flags( DWORD protect, unsigned int *vprot, BOOL image )
762 switch(protect & 0xff)
764 case PAGE_READONLY:
765 *vprot = VPROT_READ;
766 break;
767 case PAGE_READWRITE:
768 if (image)
769 *vprot = VPROT_READ | VPROT_WRITECOPY;
770 else
771 *vprot = VPROT_READ | VPROT_WRITE;
772 break;
773 case PAGE_WRITECOPY:
774 *vprot = VPROT_READ | VPROT_WRITECOPY;
775 break;
776 case PAGE_EXECUTE:
777 *vprot = VPROT_EXEC;
778 break;
779 case PAGE_EXECUTE_READ:
780 *vprot = VPROT_EXEC | VPROT_READ;
781 break;
782 case PAGE_EXECUTE_READWRITE:
783 if (image)
784 *vprot = VPROT_EXEC | VPROT_READ | VPROT_WRITECOPY;
785 else
786 *vprot = VPROT_EXEC | VPROT_READ | VPROT_WRITE;
787 break;
788 case PAGE_EXECUTE_WRITECOPY:
789 *vprot = VPROT_EXEC | VPROT_READ | VPROT_WRITECOPY;
790 break;
791 case PAGE_NOACCESS:
792 *vprot = 0;
793 break;
794 default:
795 return STATUS_INVALID_PAGE_PROTECTION;
797 if (protect & PAGE_GUARD) *vprot |= VPROT_GUARD;
798 return STATUS_SUCCESS;
802 /***********************************************************************
803 * mprotect_exec
805 * Wrapper for mprotect, adds PROT_EXEC if forced by force_exec_prot
807 static inline int mprotect_exec( void *base, size_t size, int unix_prot, unsigned int view_protect )
809 if (force_exec_prot && (unix_prot & PROT_READ) && !(unix_prot & PROT_EXEC))
811 TRACE( "forcing exec permission on %p-%p\n", base, (char *)base + size - 1 );
812 if (!mprotect( base, size, unix_prot | PROT_EXEC )) return 0;
813 /* exec + write may legitimately fail, in that case fall back to write only */
814 if (!(unix_prot & PROT_WRITE)) return -1;
817 return mprotect( base, size, unix_prot );
821 /***********************************************************************
822 * mprotect_range
824 * Call mprotect on a page range, applying the protections from the per-page byte.
826 static void mprotect_range( struct file_view *view, void *base, size_t size, BYTE set, BYTE clear )
828 size_t i, count;
829 char *addr = base;
830 int prot, next;
832 prot = VIRTUAL_GetUnixProt( (get_page_vprot( addr ) & ~clear ) | set );
833 for (count = i = 1; i < size >> page_shift; i++, count++)
835 next = VIRTUAL_GetUnixProt( (get_page_vprot( addr + (count << page_shift) ) & ~clear) | set );
836 if (next == prot) continue;
837 mprotect_exec( addr, count << page_shift, prot, view->protect );
838 addr += count << page_shift;
839 prot = next;
840 count = 0;
842 if (count) mprotect_exec( addr, count << page_shift, prot, view->protect );
846 /***********************************************************************
847 * VIRTUAL_SetProt
849 * Change the protection of a range of pages.
851 * RETURNS
852 * TRUE: Success
853 * FALSE: Failure
855 static BOOL VIRTUAL_SetProt( struct file_view *view, /* [in] Pointer to view */
856 void *base, /* [in] Starting address */
857 size_t size, /* [in] Size in bytes */
858 BYTE vprot ) /* [in] Protections to use */
860 int unix_prot = VIRTUAL_GetUnixProt(vprot);
862 if (view->protect & VPROT_WRITEWATCH)
864 /* each page may need different protections depending on write watch flag */
865 set_page_vprot_bits( base, size, vprot & ~VPROT_WRITEWATCH, ~vprot & ~VPROT_WRITEWATCH );
866 mprotect_range( view, base, size, 0, 0 );
867 return TRUE;
870 /* if setting stack guard pages, store the permissions first, as the guard may be
871 * triggered at any point after mprotect and change the permissions again */
872 if ((vprot & VPROT_GUARD) &&
873 (base >= NtCurrentTeb()->DeallocationStack) &&
874 (base < NtCurrentTeb()->Tib.StackBase))
876 set_page_vprot( base, size, vprot );
877 mprotect( base, size, unix_prot );
878 return TRUE;
881 if (mprotect_exec( base, size, unix_prot, view->protect )) /* FIXME: last error */
882 return FALSE;
884 set_page_vprot( base, size, vprot );
885 return TRUE;
889 /***********************************************************************
890 * set_protection
892 * Set page protections on a range of pages
894 static NTSTATUS set_protection( struct file_view *view, void *base, SIZE_T size, ULONG protect )
896 unsigned int vprot;
897 NTSTATUS status;
899 if ((status = get_vprot_flags( protect, &vprot, view->protect & SEC_IMAGE ))) return status;
900 if (is_view_valloc( view ))
902 if (vprot & VPROT_WRITECOPY) return STATUS_INVALID_PAGE_PROTECTION;
904 else
906 BYTE access = vprot & (VPROT_READ | VPROT_WRITE | VPROT_EXEC);
907 if ((view->protect & access) != access) return STATUS_INVALID_PAGE_PROTECTION;
910 if (!VIRTUAL_SetProt( view, base, size, vprot | VPROT_COMMITTED )) return STATUS_ACCESS_DENIED;
911 return STATUS_SUCCESS;
915 /***********************************************************************
916 * reset_write_watches
918 * Reset write watches in a memory range.
920 static void reset_write_watches( struct file_view *view, void *base, SIZE_T size )
922 set_page_vprot_bits( base, size, VPROT_WRITEWATCH, 0 );
923 mprotect_range( view, base, size, 0, 0 );
927 /***********************************************************************
928 * unmap_extra_space
930 * Release the extra memory while keeping the range starting on the granularity boundary.
932 static inline void *unmap_extra_space( void *ptr, size_t total_size, size_t wanted_size, size_t mask )
934 if ((ULONG_PTR)ptr & mask)
936 size_t extra = mask + 1 - ((ULONG_PTR)ptr & mask);
937 munmap( ptr, extra );
938 ptr = (char *)ptr + extra;
939 total_size -= extra;
941 if (total_size > wanted_size)
942 munmap( (char *)ptr + wanted_size, total_size - wanted_size );
943 return ptr;
947 struct alloc_area
949 size_t size;
950 size_t mask;
951 int top_down;
952 void *limit;
953 void *result;
956 /***********************************************************************
957 * alloc_reserved_area_callback
959 * Try to map some space inside a reserved area. Callback for wine_mmap_enum_reserved_areas.
961 static int alloc_reserved_area_callback( void *start, size_t size, void *arg )
963 struct alloc_area *alloc = arg;
964 void *end = (char *)start + size;
966 if (start < address_space_start) start = address_space_start;
967 if (is_beyond_limit( start, size, alloc->limit )) end = alloc->limit;
968 if (start >= end) return 0;
970 /* make sure we don't touch the preloader reserved range */
971 if (preload_reserve_end >= start)
973 if (preload_reserve_end >= end)
975 if (preload_reserve_start <= start) return 0; /* no space in that area */
976 if (preload_reserve_start < end) end = preload_reserve_start;
978 else if (preload_reserve_start <= start) start = preload_reserve_end;
979 else
981 /* range is split in two by the preloader reservation, try first part */
982 if ((alloc->result = find_free_area( start, preload_reserve_start, alloc->size,
983 alloc->mask, alloc->top_down )))
984 return 1;
985 /* then fall through to try second part */
986 start = preload_reserve_end;
989 if ((alloc->result = find_free_area( start, end, alloc->size, alloc->mask, alloc->top_down )))
990 return 1;
992 return 0;
995 /***********************************************************************
996 * map_fixed_area
998 * mmap the fixed memory area.
999 * The csVirtual section must be held by caller.
1001 static NTSTATUS map_fixed_area( void *base, size_t size, unsigned int vprot )
1003 void *ptr;
1005 switch (wine_mmap_is_in_reserved_area( base, size ))
1007 case -1: /* partially in a reserved area */
1009 NTSTATUS status;
1010 struct area_boundary area;
1011 size_t lower_size;
1012 area.base = base;
1013 area.size = size;
1014 wine_mmap_enum_reserved_areas( get_area_boundary_callback, &area, 0 );
1015 assert( area.boundary );
1016 lower_size = (char *)area.boundary - (char *)base;
1017 status = map_fixed_area( base, lower_size, vprot );
1018 if (status == STATUS_SUCCESS)
1020 status = map_fixed_area( area.boundary, size - lower_size, vprot);
1021 if (status != STATUS_SUCCESS) unmap_area( base, lower_size );
1023 return status;
1025 case 0: /* not in a reserved area, do a normal allocation */
1026 if ((ptr = wine_anon_mmap( base, size, VIRTUAL_GetUnixProt(vprot), 0 )) == (void *)-1)
1028 if (errno == ENOMEM) return STATUS_NO_MEMORY;
1029 return STATUS_INVALID_PARAMETER;
1031 if (ptr != base)
1033 /* We couldn't get the address we wanted */
1034 if (is_beyond_limit( ptr, size, user_space_limit )) add_reserved_area( ptr, size );
1035 else munmap( ptr, size );
1036 return STATUS_CONFLICTING_ADDRESSES;
1038 break;
1040 default:
1041 case 1: /* in a reserved area, make sure the address is available */
1042 if (find_view_range( base, size )) return STATUS_CONFLICTING_ADDRESSES;
1043 /* replace the reserved area by our mapping */
1044 if ((ptr = wine_anon_mmap( base, size, VIRTUAL_GetUnixProt(vprot), MAP_FIXED )) != base)
1045 return STATUS_INVALID_PARAMETER;
1046 break;
1048 if (is_beyond_limit( ptr, size, working_set_limit )) working_set_limit = address_space_limit;
1049 return STATUS_SUCCESS;
1052 /***********************************************************************
1053 * map_view
1055 * Create a view and mmap the corresponding memory area.
1056 * The csVirtual section must be held by caller.
1058 static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, size_t mask,
1059 int top_down, unsigned int vprot )
1061 void *ptr;
1062 NTSTATUS status;
1064 if (base)
1066 if (is_beyond_limit( base, size, address_space_limit ))
1067 return STATUS_WORKING_SET_LIMIT_RANGE;
1068 status = map_fixed_area( base, size, vprot );
1069 if (status != STATUS_SUCCESS) return status;
1070 ptr = base;
1072 else
1074 size_t view_size = size + mask + 1;
1075 struct alloc_area alloc;
1077 alloc.size = size;
1078 alloc.mask = mask;
1079 alloc.top_down = top_down;
1080 alloc.limit = user_space_limit;
1081 if (wine_mmap_enum_reserved_areas( alloc_reserved_area_callback, &alloc, top_down ))
1083 ptr = alloc.result;
1084 TRACE( "got mem in reserved area %p-%p\n", ptr, (char *)ptr + size );
1085 if (wine_anon_mmap( ptr, size, VIRTUAL_GetUnixProt(vprot), MAP_FIXED ) != ptr)
1086 return STATUS_INVALID_PARAMETER;
1087 goto done;
1090 for (;;)
1092 if ((ptr = wine_anon_mmap( NULL, view_size, VIRTUAL_GetUnixProt(vprot), 0 )) == (void *)-1)
1094 if (errno == ENOMEM) return STATUS_NO_MEMORY;
1095 return STATUS_INVALID_PARAMETER;
1097 TRACE( "got mem with anon mmap %p-%p\n", ptr, (char *)ptr + size );
1098 /* if we got something beyond the user limit, unmap it and retry */
1099 if (is_beyond_limit( ptr, view_size, user_space_limit )) add_reserved_area( ptr, view_size );
1100 else break;
1102 ptr = unmap_extra_space( ptr, view_size, size, mask );
1104 done:
1105 status = create_view( view_ret, ptr, size, vprot );
1106 if (status != STATUS_SUCCESS) unmap_area( ptr, size );
1107 return status;
1111 /***********************************************************************
1112 * map_file_into_view
1114 * Wrapper for mmap() to map a file into a view, falling back to read if mmap fails.
1115 * The csVirtual section must be held by caller.
1117 static NTSTATUS map_file_into_view( struct file_view *view, int fd, size_t start, size_t size,
1118 off_t offset, unsigned int vprot, BOOL removable )
1120 void *ptr;
1121 int prot = VIRTUAL_GetUnixProt( vprot | VPROT_COMMITTED /* make sure it is accessible */ );
1122 unsigned int flags = MAP_FIXED | ((vprot & VPROT_WRITECOPY) ? MAP_PRIVATE : MAP_SHARED);
1124 assert( start < view->size );
1125 assert( start + size <= view->size );
1127 if (force_exec_prot && (vprot & VPROT_READ))
1129 TRACE( "forcing exec permission on mapping %p-%p\n",
1130 (char *)view->base + start, (char *)view->base + start + size - 1 );
1131 prot |= PROT_EXEC;
1134 /* only try mmap if media is not removable (or if we require write access) */
1135 if (!removable || (flags & MAP_SHARED))
1137 if (mmap( (char *)view->base + start, size, prot, flags, fd, offset ) != (void *)-1)
1138 goto done;
1140 if ((errno == EPERM) && (prot & PROT_EXEC))
1141 ERR( "failed to set %08x protection on file map, noexec filesystem?\n", prot );
1143 /* mmap() failed; if this is because the file offset is not */
1144 /* page-aligned (EINVAL), or because the underlying filesystem */
1145 /* does not support mmap() (ENOEXEC,ENODEV), we do it by hand. */
1146 if ((errno != ENOEXEC) && (errno != EINVAL) && (errno != ENODEV)) return FILE_GetNtStatus();
1147 if (flags & MAP_SHARED) /* we cannot fake shared mappings */
1149 if (errno == EINVAL) return STATUS_INVALID_PARAMETER;
1150 ERR( "shared writable mmap not supported, broken filesystem?\n" );
1151 return STATUS_NOT_SUPPORTED;
1155 /* Reserve the memory with an anonymous mmap */
1156 ptr = wine_anon_mmap( (char *)view->base + start, size, PROT_READ | PROT_WRITE, MAP_FIXED );
1157 if (ptr == (void *)-1) return FILE_GetNtStatus();
1158 /* Now read in the file */
1159 pread( fd, ptr, size, offset );
1160 if (prot != (PROT_READ|PROT_WRITE)) mprotect( ptr, size, prot ); /* Set the right protection */
1161 done:
1162 set_page_vprot( (char *)view->base + start, size, vprot );
1163 return STATUS_SUCCESS;
1167 /***********************************************************************
1168 * get_committed_size
1170 * Get the size of the committed range starting at base.
1171 * Also return the protections for the first page.
1173 static SIZE_T get_committed_size( struct file_view *view, void *base, BYTE *vprot )
1175 SIZE_T i, start;
1177 start = ((char *)base - (char *)view->base) >> page_shift;
1178 *vprot = get_page_vprot( base );
1180 if (view->protect & SEC_RESERVE)
1182 SIZE_T ret = 0;
1183 SERVER_START_REQ( get_mapping_committed_range )
1185 req->handle = wine_server_obj_handle( view->mapping );
1186 req->offset = start << page_shift;
1187 if (!wine_server_call( req ))
1189 ret = reply->size;
1190 if (reply->committed)
1192 *vprot |= VPROT_COMMITTED;
1193 set_page_vprot_bits( base, ret, VPROT_COMMITTED, 0 );
1197 SERVER_END_REQ;
1198 return ret;
1200 for (i = start + 1; i < view->size >> page_shift; i++)
1201 if ((*vprot ^ get_page_vprot( (char *)view->base + (i << page_shift) )) & VPROT_COMMITTED) break;
1202 return (i - start) << page_shift;
1206 /***********************************************************************
1207 * decommit_view
1209 * Decommit some pages of a given view.
1210 * The csVirtual section must be held by caller.
1212 static NTSTATUS decommit_pages( struct file_view *view, size_t start, size_t size )
1214 if (wine_anon_mmap( (char *)view->base + start, size, PROT_NONE, MAP_FIXED ) != (void *)-1)
1216 set_page_vprot_bits( (char *)view->base + start, size, 0, VPROT_COMMITTED );
1217 return STATUS_SUCCESS;
1219 return FILE_GetNtStatus();
1223 /***********************************************************************
1224 * allocate_dos_memory
1226 * Allocate the DOS memory range.
1228 static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot )
1230 size_t size;
1231 void *addr = NULL;
1232 void * const low_64k = (void *)0x10000;
1233 const size_t dosmem_size = 0x110000;
1234 int unix_prot = VIRTUAL_GetUnixProt( vprot );
1236 /* check for existing view */
1238 if (find_view_range( 0, dosmem_size )) return STATUS_CONFLICTING_ADDRESSES;
1240 /* check without the first 64K */
1242 if (wine_mmap_is_in_reserved_area( low_64k, dosmem_size - 0x10000 ) != 1)
1244 addr = wine_anon_mmap( low_64k, dosmem_size - 0x10000, unix_prot, 0 );
1245 if (addr != low_64k)
1247 if (addr != (void *)-1) munmap( addr, dosmem_size - 0x10000 );
1248 return map_view( view, NULL, dosmem_size, 0xffff, 0, vprot );
1252 /* now try to allocate the low 64K too */
1254 if (wine_mmap_is_in_reserved_area( NULL, 0x10000 ) != 1)
1256 addr = wine_anon_mmap( (void *)page_size, 0x10000 - page_size, unix_prot, 0 );
1257 if (addr == (void *)page_size)
1259 if (!wine_anon_mmap( NULL, page_size, unix_prot, MAP_FIXED ))
1261 addr = NULL;
1262 TRACE( "successfully mapped low 64K range\n" );
1264 else TRACE( "failed to map page 0\n" );
1266 else
1268 if (addr != (void *)-1) munmap( addr, 0x10000 - page_size );
1269 addr = low_64k;
1270 TRACE( "failed to map low 64K range\n" );
1274 /* now reserve the whole range */
1276 size = (char *)dosmem_size - (char *)addr;
1277 wine_anon_mmap( addr, size, unix_prot, MAP_FIXED );
1278 return create_view( view, addr, size, vprot );
1282 /***********************************************************************
1283 * stat_mapping_file
1285 * Stat the underlying file for a memory view.
1287 static NTSTATUS stat_mapping_file( struct file_view *view, struct stat *st )
1289 NTSTATUS status;
1290 int unix_fd, needs_close;
1292 if (!view->mapping) return STATUS_NOT_MAPPED_VIEW;
1293 if (!(status = server_get_unix_fd( view->mapping, 0, &unix_fd, &needs_close, NULL, NULL )))
1295 if (fstat( unix_fd, st ) == -1) status = FILE_GetNtStatus();
1296 if (needs_close) close( unix_fd );
1298 return status;
1301 /***********************************************************************
1302 * map_image
1304 * Map an executable (PE format) image into memory.
1306 static NTSTATUS map_image( HANDLE hmapping, int fd, char *base, SIZE_T total_size, SIZE_T mask,
1307 SIZE_T header_size, int shared_fd, HANDLE dup_mapping, PVOID *addr_ptr )
1309 IMAGE_DOS_HEADER *dos;
1310 IMAGE_NT_HEADERS *nt;
1311 IMAGE_SECTION_HEADER sections[96];
1312 IMAGE_SECTION_HEADER *sec;
1313 IMAGE_DATA_DIRECTORY *imports;
1314 NTSTATUS status = STATUS_CONFLICTING_ADDRESSES;
1315 int i;
1316 off_t pos;
1317 sigset_t sigset;
1318 struct stat st;
1319 struct file_view *view = NULL;
1320 char *ptr, *header_end, *header_start;
1322 /* zero-map the whole range */
1324 server_enter_uninterrupted_section( &csVirtual, &sigset );
1326 if (base >= (char *)address_space_start) /* make sure the DOS area remains free */
1327 status = map_view( &view, base, total_size, mask, FALSE, SEC_IMAGE | SEC_FILE |
1328 VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY );
1330 if (status != STATUS_SUCCESS)
1331 status = map_view( &view, NULL, total_size, mask, FALSE, SEC_IMAGE | SEC_FILE |
1332 VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY );
1334 if (status != STATUS_SUCCESS) goto error;
1336 ptr = view->base;
1337 TRACE_(module)( "mapped PE file at %p-%p\n", ptr, ptr + total_size );
1339 /* map the header */
1341 if (fstat( fd, &st ) == -1)
1343 status = FILE_GetNtStatus();
1344 goto error;
1346 status = STATUS_INVALID_IMAGE_FORMAT; /* generic error */
1347 if (!st.st_size) goto error;
1348 header_size = min( header_size, st.st_size );
1349 if (map_file_into_view( view, fd, 0, header_size, 0, VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY,
1350 !dup_mapping ) != STATUS_SUCCESS) goto error;
1351 dos = (IMAGE_DOS_HEADER *)ptr;
1352 nt = (IMAGE_NT_HEADERS *)(ptr + dos->e_lfanew);
1353 header_end = ptr + ROUND_SIZE( 0, header_size );
1354 memset( ptr + header_size, 0, header_end - (ptr + header_size) );
1355 if ((char *)(nt + 1) > header_end) goto error;
1356 header_start = (char*)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader;
1357 if (nt->FileHeader.NumberOfSections > sizeof(sections)/sizeof(*sections)) goto error;
1358 if (header_start + sizeof(*sections) * nt->FileHeader.NumberOfSections > header_end) goto error;
1359 /* Some applications (e.g. the Steam version of Borderlands) map over the top of the section headers,
1360 * copying the headers into local memory is necessary to properly load such applications. */
1361 memcpy(sections, header_start, sizeof(*sections) * nt->FileHeader.NumberOfSections);
1362 sec = sections;
1364 imports = nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_IMPORT;
1365 if (!imports->Size || !imports->VirtualAddress) imports = NULL;
1367 /* check for non page-aligned binary */
1369 if (nt->OptionalHeader.SectionAlignment <= page_mask)
1371 /* unaligned sections, this happens for native subsystem binaries */
1372 /* in that case Windows simply maps in the whole file */
1374 if (map_file_into_view( view, fd, 0, total_size, 0, VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY,
1375 !dup_mapping ) != STATUS_SUCCESS) goto error;
1377 /* check that all sections are loaded at the right offset */
1378 if (nt->OptionalHeader.FileAlignment != nt->OptionalHeader.SectionAlignment) goto error;
1379 for (i = 0; i < nt->FileHeader.NumberOfSections; i++)
1381 if (sec[i].VirtualAddress != sec[i].PointerToRawData)
1382 goto error; /* Windows refuses to load in that case too */
1385 /* set the image protections */
1386 VIRTUAL_SetProt( view, ptr, total_size,
1387 VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY | VPROT_EXEC );
1389 /* no relocations are performed on non page-aligned binaries */
1390 goto done;
1394 /* map all the sections */
1396 for (i = pos = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
1398 static const SIZE_T sector_align = 0x1ff;
1399 SIZE_T map_size, file_start, file_size, end;
1401 if (!sec->Misc.VirtualSize)
1402 map_size = ROUND_SIZE( 0, sec->SizeOfRawData );
1403 else
1404 map_size = ROUND_SIZE( 0, sec->Misc.VirtualSize );
1406 /* file positions are rounded to sector boundaries regardless of OptionalHeader.FileAlignment */
1407 file_start = sec->PointerToRawData & ~sector_align;
1408 file_size = (sec->SizeOfRawData + (sec->PointerToRawData & sector_align) + sector_align) & ~sector_align;
1409 if (file_size > map_size) file_size = map_size;
1411 /* a few sanity checks */
1412 end = sec->VirtualAddress + ROUND_SIZE( sec->VirtualAddress, map_size );
1413 if (sec->VirtualAddress > total_size || end > total_size || end < sec->VirtualAddress)
1415 WARN_(module)( "Section %.8s too large (%x+%lx/%lx)\n",
1416 sec->Name, sec->VirtualAddress, map_size, total_size );
1417 goto error;
1420 if ((sec->Characteristics & IMAGE_SCN_MEM_SHARED) &&
1421 (sec->Characteristics & IMAGE_SCN_MEM_WRITE))
1423 TRACE_(module)( "mapping shared section %.8s at %p off %x (%x) size %lx (%lx) flags %x\n",
1424 sec->Name, ptr + sec->VirtualAddress,
1425 sec->PointerToRawData, (int)pos, file_size, map_size,
1426 sec->Characteristics );
1427 if (map_file_into_view( view, shared_fd, sec->VirtualAddress, map_size, pos,
1428 VPROT_COMMITTED | VPROT_READ | VPROT_WRITE, FALSE ) != STATUS_SUCCESS)
1430 ERR_(module)( "Could not map shared section %.8s\n", sec->Name );
1431 goto error;
1434 /* check if the import directory falls inside this section */
1435 if (imports && imports->VirtualAddress >= sec->VirtualAddress &&
1436 imports->VirtualAddress < sec->VirtualAddress + map_size)
1438 UINT_PTR base = imports->VirtualAddress & ~page_mask;
1439 UINT_PTR end = base + ROUND_SIZE( imports->VirtualAddress, imports->Size );
1440 if (end > sec->VirtualAddress + map_size) end = sec->VirtualAddress + map_size;
1441 if (end > base)
1442 map_file_into_view( view, shared_fd, base, end - base,
1443 pos + (base - sec->VirtualAddress),
1444 VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY, FALSE );
1446 pos += map_size;
1447 continue;
1450 TRACE_(module)( "mapping section %.8s at %p off %x size %x virt %x flags %x\n",
1451 sec->Name, ptr + sec->VirtualAddress,
1452 sec->PointerToRawData, sec->SizeOfRawData,
1453 sec->Misc.VirtualSize, sec->Characteristics );
1455 if (!sec->PointerToRawData || !file_size) continue;
1457 /* Note: if the section is not aligned properly map_file_into_view will magically
1458 * fall back to read(), so we don't need to check anything here.
1460 end = file_start + file_size;
1461 if (sec->PointerToRawData >= st.st_size ||
1462 end > ((st.st_size + sector_align) & ~sector_align) ||
1463 end < file_start ||
1464 map_file_into_view( view, fd, sec->VirtualAddress, file_size, file_start,
1465 VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY,
1466 !dup_mapping ) != STATUS_SUCCESS)
1468 ERR_(module)( "Could not map section %.8s, file probably truncated\n", sec->Name );
1469 goto error;
1472 if (file_size & page_mask)
1474 end = ROUND_SIZE( 0, file_size );
1475 if (end > map_size) end = map_size;
1476 TRACE_(module)("clearing %p - %p\n",
1477 ptr + sec->VirtualAddress + file_size,
1478 ptr + sec->VirtualAddress + end );
1479 memset( ptr + sec->VirtualAddress + file_size, 0, end - file_size );
1483 /* set the image protections */
1485 VIRTUAL_SetProt( view, ptr, ROUND_SIZE( 0, header_size ), VPROT_COMMITTED | VPROT_READ );
1487 sec = sections;
1488 for (i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
1490 SIZE_T size;
1491 BYTE vprot = VPROT_COMMITTED;
1493 if (sec->Misc.VirtualSize)
1494 size = ROUND_SIZE( sec->VirtualAddress, sec->Misc.VirtualSize );
1495 else
1496 size = ROUND_SIZE( sec->VirtualAddress, sec->SizeOfRawData );
1498 if (sec->Characteristics & IMAGE_SCN_MEM_READ) vprot |= VPROT_READ;
1499 if (sec->Characteristics & IMAGE_SCN_MEM_WRITE) vprot |= VPROT_WRITECOPY;
1500 if (sec->Characteristics & IMAGE_SCN_MEM_EXECUTE) vprot |= VPROT_EXEC;
1502 /* Dumb game crack lets the AOEP point into a data section. Adjust. */
1503 if ((nt->OptionalHeader.AddressOfEntryPoint >= sec->VirtualAddress) &&
1504 (nt->OptionalHeader.AddressOfEntryPoint < sec->VirtualAddress + size))
1505 vprot |= VPROT_EXEC;
1507 if (!VIRTUAL_SetProt( view, ptr + sec->VirtualAddress, size, vprot ) && (vprot & VPROT_EXEC))
1508 ERR( "failed to set %08x protection on section %.8s, noexec filesystem?\n",
1509 sec->Characteristics, sec->Name );
1512 done:
1513 view->mapping = dup_mapping;
1514 VIRTUAL_DEBUG_DUMP_VIEW( view );
1515 server_leave_uninterrupted_section( &csVirtual, &sigset );
1517 *addr_ptr = ptr;
1518 #ifdef VALGRIND_LOAD_PDB_DEBUGINFO
1519 VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, ptr - base);
1520 #endif
1521 if (ptr != base) return STATUS_IMAGE_NOT_AT_BASE;
1522 return STATUS_SUCCESS;
1524 error:
1525 if (view) delete_view( view );
1526 server_leave_uninterrupted_section( &csVirtual, &sigset );
1527 if (dup_mapping) close_handle( dup_mapping );
1528 return status;
1532 struct alloc_virtual_heap
1534 void *base;
1535 size_t size;
1538 /* callback for wine_mmap_enum_reserved_areas to allocate space for the virtual heap */
1539 static int alloc_virtual_heap( void *base, size_t size, void *arg )
1541 struct alloc_virtual_heap *alloc = arg;
1543 if (is_beyond_limit( base, size, address_space_limit )) address_space_limit = (char *)base + size;
1544 if (size < alloc->size) return 0;
1545 if (is_win64 && base < (void *)0x80000000) return 0;
1546 alloc->base = wine_anon_mmap( (char *)base + size - alloc->size, alloc->size,
1547 PROT_READ|PROT_WRITE, MAP_FIXED );
1548 return (alloc->base != (void *)-1);
1551 /***********************************************************************
1552 * virtual_init
1554 void virtual_init(void)
1556 const char *preload;
1557 struct alloc_virtual_heap alloc_views;
1558 size_t size;
1560 #if !defined(__i386__) && !defined(__x86_64__)
1561 page_size = sysconf( _SC_PAGESIZE );
1562 page_mask = page_size - 1;
1563 /* Make sure we have a power of 2 */
1564 assert( !(page_size & page_mask) );
1565 page_shift = 0;
1566 while ((1 << page_shift) != page_size) page_shift++;
1567 #ifdef _WIN64
1568 address_space_limit = (void *)(((1UL << 47) - 1) & ~page_mask);
1569 #else
1570 address_space_limit = (void *)~page_mask;
1571 #endif
1572 user_space_limit = working_set_limit = address_space_limit;
1573 #endif
1574 if ((preload = getenv("WINEPRELOADRESERVE")))
1576 unsigned long start, end;
1577 if (sscanf( preload, "%lx-%lx", &start, &end ) == 2)
1579 preload_reserve_start = (void *)start;
1580 preload_reserve_end = (void *)end;
1584 /* try to find space in a reserved area for the views and pages protection table */
1585 #ifdef _WIN64
1586 pages_vprot_size = ((size_t)address_space_limit >> page_shift >> pages_vprot_shift) + 1;
1587 alloc_views.size = view_block_size + pages_vprot_size * sizeof(*pages_vprot);
1588 #else
1589 alloc_views.size = view_block_size + (1U << (32 - page_shift));
1590 #endif
1591 if (wine_mmap_enum_reserved_areas( alloc_virtual_heap, &alloc_views, 1 ))
1592 wine_mmap_remove_reserved_area( alloc_views.base, alloc_views.size, 0 );
1593 else
1594 alloc_views.base = wine_anon_mmap( NULL, alloc_views.size, PROT_READ | PROT_WRITE, 0 );
1596 assert( alloc_views.base != (void *)-1 );
1597 view_block_start = alloc_views.base;
1598 view_block_end = view_block_start + view_block_size / sizeof(*view_block_start);
1599 pages_vprot = (void *)((char *)alloc_views.base + view_block_size);
1600 wine_rb_init( &views_tree, compare_view );
1602 /* make the DOS area accessible (except the low 64K) to hide bugs in broken apps like Excel 2003 */
1603 size = (char *)address_space_start - (char *)0x10000;
1604 if (size && wine_mmap_is_in_reserved_area( (void*)0x10000, size ) == 1)
1605 wine_anon_mmap( (void *)0x10000, size, PROT_READ | PROT_WRITE, MAP_FIXED );
1609 /***********************************************************************
1610 * virtual_init_threading
1612 void virtual_init_threading(void)
1614 use_locks = TRUE;
1618 /***********************************************************************
1619 * virtual_get_system_info
1621 void virtual_get_system_info( SYSTEM_BASIC_INFORMATION *info )
1623 #ifdef HAVE_SYSINFO
1624 struct sysinfo sinfo;
1625 #endif
1627 info->unknown = 0;
1628 info->KeMaximumIncrement = 0; /* FIXME */
1629 info->PageSize = page_size;
1630 info->MmLowestPhysicalPage = 1;
1631 info->MmHighestPhysicalPage = 0x7fffffff / page_size;
1632 #ifdef HAVE_SYSINFO
1633 if (!sysinfo(&sinfo))
1635 ULONG64 total = (ULONG64)sinfo.totalram * sinfo.mem_unit;
1636 info->MmHighestPhysicalPage = max(1, total / page_size);
1638 #endif
1639 info->MmNumberOfPhysicalPages = info->MmHighestPhysicalPage - info->MmLowestPhysicalPage;
1640 info->AllocationGranularity = get_mask(0) + 1;
1641 info->LowestUserAddress = (void *)0x10000;
1642 info->HighestUserAddress = (char *)user_space_limit - 1;
1643 info->ActiveProcessorsAffinityMask = get_system_affinity_mask();
1644 info->NumberOfProcessors = NtCurrentTeb()->Peb->NumberOfProcessors;
1648 /***********************************************************************
1649 * virtual_create_builtin_view
1651 NTSTATUS virtual_create_builtin_view( void *module )
1653 NTSTATUS status;
1654 sigset_t sigset;
1655 IMAGE_NT_HEADERS *nt = RtlImageNtHeader( module );
1656 SIZE_T size = nt->OptionalHeader.SizeOfImage;
1657 IMAGE_SECTION_HEADER *sec;
1658 struct file_view *view;
1659 void *base;
1660 int i;
1662 size = ROUND_SIZE( module, size );
1663 base = ROUND_ADDR( module, page_mask );
1664 server_enter_uninterrupted_section( &csVirtual, &sigset );
1665 status = create_view( &view, base, size, SEC_IMAGE | SEC_FILE | VPROT_SYSTEM |
1666 VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY | VPROT_EXEC );
1667 if (!status)
1669 TRACE( "created %p-%p\n", base, (char *)base + size );
1671 /* The PE header is always read-only, no write, no execute. */
1672 set_page_vprot( base, page_size, VPROT_COMMITTED | VPROT_READ );
1674 sec = (IMAGE_SECTION_HEADER *)((char *)&nt->OptionalHeader + nt->FileHeader.SizeOfOptionalHeader);
1675 for (i = 0; i < nt->FileHeader.NumberOfSections; i++)
1677 BYTE flags = VPROT_COMMITTED;
1679 if (sec[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) flags |= VPROT_EXEC;
1680 if (sec[i].Characteristics & IMAGE_SCN_MEM_READ) flags |= VPROT_READ;
1681 if (sec[i].Characteristics & IMAGE_SCN_MEM_WRITE) flags |= VPROT_WRITE;
1682 set_page_vprot( (char *)base + sec[i].VirtualAddress, sec[i].Misc.VirtualSize, flags );
1684 VIRTUAL_DEBUG_DUMP_VIEW( view );
1686 server_leave_uninterrupted_section( &csVirtual, &sigset );
1687 return status;
1691 /***********************************************************************
1692 * virtual_alloc_thread_stack
1694 NTSTATUS virtual_alloc_thread_stack( TEB *teb, SIZE_T reserve_size, SIZE_T commit_size )
1696 struct file_view *view;
1697 NTSTATUS status;
1698 sigset_t sigset;
1699 SIZE_T size;
1701 if (!reserve_size || !commit_size)
1703 IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress );
1704 if (!reserve_size) reserve_size = nt->OptionalHeader.SizeOfStackReserve;
1705 if (!commit_size) commit_size = nt->OptionalHeader.SizeOfStackCommit;
1708 size = max( reserve_size, commit_size );
1709 if (size < 1024 * 1024) size = 1024 * 1024; /* Xlib needs a large stack */
1710 size = (size + 0xffff) & ~0xffff; /* round to 64K boundary */
1712 server_enter_uninterrupted_section( &csVirtual, &sigset );
1714 if ((status = map_view( &view, NULL, size, 0xffff, 0,
1715 VPROT_READ | VPROT_WRITE | VPROT_COMMITTED )) != STATUS_SUCCESS)
1716 goto done;
1718 #ifdef VALGRIND_STACK_REGISTER
1719 VALGRIND_STACK_REGISTER( view->base, (char *)view->base + view->size );
1720 #endif
1722 /* setup no access guard page */
1723 VIRTUAL_SetProt( view, view->base, page_size, VPROT_COMMITTED );
1724 VIRTUAL_SetProt( view, (char *)view->base + page_size, page_size,
1725 VPROT_READ | VPROT_WRITE | VPROT_COMMITTED | VPROT_GUARD );
1726 VIRTUAL_DEBUG_DUMP_VIEW( view );
1728 /* note: limit is lower than base since the stack grows down */
1729 teb->DeallocationStack = view->base;
1730 teb->Tib.StackBase = (char *)view->base + view->size;
1731 teb->Tib.StackLimit = (char *)view->base + 2 * page_size;
1732 done:
1733 server_leave_uninterrupted_section( &csVirtual, &sigset );
1734 return status;
1738 /***********************************************************************
1739 * virtual_clear_thread_stack
1741 * Clear the stack contents before calling the main entry point, some broken apps need that.
1743 void virtual_clear_thread_stack(void)
1745 void *stack = NtCurrentTeb()->Tib.StackLimit;
1746 size_t size = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit;
1748 wine_anon_mmap( stack, size - page_size, PROT_READ | PROT_WRITE, MAP_FIXED );
1749 if (force_exec_prot) mprotect( stack, size - page_size, PROT_READ | PROT_WRITE | PROT_EXEC );
1753 /***********************************************************************
1754 * virtual_handle_fault
1756 NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err, BOOL on_signal_stack )
1758 struct file_view *view;
1759 NTSTATUS ret = STATUS_ACCESS_VIOLATION;
1760 sigset_t sigset;
1762 server_enter_uninterrupted_section( &csVirtual, &sigset );
1763 if ((view = VIRTUAL_FindView( addr, 0 )))
1765 void *page = ROUND_ADDR( addr, page_mask );
1766 BYTE vprot = get_page_vprot( page );
1767 if ((err & EXCEPTION_WRITE_FAULT) && (view->protect & VPROT_WRITEWATCH))
1769 if (vprot & VPROT_WRITEWATCH)
1771 set_page_vprot_bits( page, page_size, 0, VPROT_WRITEWATCH );
1772 mprotect_range( view, page, page_size, 0, 0 );
1774 /* ignore fault if page is writable now */
1775 if (VIRTUAL_GetUnixProt( get_page_vprot( page )) & PROT_WRITE) ret = STATUS_SUCCESS;
1777 if (!on_signal_stack && (vprot & VPROT_GUARD))
1779 set_page_vprot_bits( page, page_size, 0, VPROT_GUARD );
1780 mprotect_range( view, page, page_size, 0, 0 );
1781 ret = STATUS_GUARD_PAGE_VIOLATION;
1784 server_leave_uninterrupted_section( &csVirtual, &sigset );
1785 return ret;
1790 /***********************************************************************
1791 * virtual_is_valid_code_address
1793 BOOL virtual_is_valid_code_address( const void *addr, SIZE_T size )
1795 struct file_view *view;
1796 BOOL ret = FALSE;
1797 sigset_t sigset;
1799 server_enter_uninterrupted_section( &csVirtual, &sigset );
1800 if ((view = VIRTUAL_FindView( addr, size )))
1801 ret = !(view->protect & VPROT_SYSTEM); /* system views are not visible to the app */
1802 server_leave_uninterrupted_section( &csVirtual, &sigset );
1803 return ret;
1807 /***********************************************************************
1808 * virtual_handle_stack_fault
1810 * Handle an access fault inside the current thread stack.
1811 * Called from inside a signal handler.
1813 BOOL virtual_handle_stack_fault( void *addr )
1815 struct file_view *view;
1816 BOOL ret = FALSE;
1818 RtlEnterCriticalSection( &csVirtual ); /* no need for signal masking inside signal handler */
1819 if ((view = VIRTUAL_FindView( addr, 0 )))
1821 void *page = ROUND_ADDR( addr, page_mask );
1822 BYTE vprot = get_page_vprot( page );
1823 if (vprot & VPROT_GUARD)
1825 VIRTUAL_SetProt( view, page, page_size, vprot & ~VPROT_GUARD );
1826 NtCurrentTeb()->Tib.StackLimit = page;
1827 if ((char *)page >= (char *)NtCurrentTeb()->DeallocationStack + 2*page_size)
1829 vprot = get_page_vprot( (char *)page - page_size );
1830 VIRTUAL_SetProt( view, (char *)page - page_size, page_size, vprot | VPROT_COMMITTED | VPROT_GUARD );
1832 ret = TRUE;
1835 RtlLeaveCriticalSection( &csVirtual );
1836 return ret;
1840 /***********************************************************************
1841 * virtual_check_buffer_for_read
1843 * Check if a memory buffer can be read, triggering page faults if needed for DIB section access.
1845 BOOL virtual_check_buffer_for_read( const void *ptr, SIZE_T size )
1847 if (!size) return TRUE;
1848 if (!ptr) return FALSE;
1850 __TRY
1852 volatile const char *p = ptr;
1853 char dummy __attribute__((unused));
1854 SIZE_T count = size;
1856 while (count > page_size)
1858 dummy = *p;
1859 p += page_size;
1860 count -= page_size;
1862 dummy = p[0];
1863 dummy = p[count - 1];
1865 __EXCEPT_PAGE_FAULT
1867 return FALSE;
1869 __ENDTRY
1870 return TRUE;
1874 /***********************************************************************
1875 * virtual_check_buffer_for_write
1877 * Check if a memory buffer can be written to, triggering page faults if needed for write watches.
1879 BOOL virtual_check_buffer_for_write( void *ptr, SIZE_T size )
1881 if (!size) return TRUE;
1882 if (!ptr) return FALSE;
1884 __TRY
1886 volatile char *p = ptr;
1887 SIZE_T count = size;
1889 while (count > page_size)
1891 *p |= 0;
1892 p += page_size;
1893 count -= page_size;
1895 p[0] |= 0;
1896 p[count - 1] |= 0;
1898 __EXCEPT_PAGE_FAULT
1900 return FALSE;
1902 __ENDTRY
1903 return TRUE;
1907 /***********************************************************************
1908 * virtual_uninterrupted_read_memory
1910 * Similar to NtReadVirtualMemory, but without wineserver calls. Moreover
1911 * permissions are checked before accessing each page, to ensure that no
1912 * exceptions can happen.
1914 SIZE_T virtual_uninterrupted_read_memory( const void *addr, void *buffer, SIZE_T size )
1916 struct file_view *view;
1917 sigset_t sigset;
1918 SIZE_T bytes_read = 0;
1920 if (!size) return 0;
1922 server_enter_uninterrupted_section( &csVirtual, &sigset );
1923 if ((view = VIRTUAL_FindView( addr, size )))
1925 if (!(view->protect & VPROT_SYSTEM))
1927 char *page = ROUND_ADDR( addr, page_mask );
1929 while (bytes_read < size && (VIRTUAL_GetUnixProt( get_page_vprot( page )) & PROT_READ))
1931 SIZE_T block_size = min( size, page_size - ((UINT_PTR)addr & page_mask) );
1932 memcpy( buffer, addr, block_size );
1934 addr = (const void *)((const char *)addr + block_size);
1935 buffer = (void *)((char *)buffer + block_size);
1936 bytes_read += block_size;
1937 page += page_size;
1941 server_leave_uninterrupted_section( &csVirtual, &sigset );
1942 return bytes_read;
1946 /***********************************************************************
1947 * virtual_uninterrupted_write_memory
1949 * Similar to NtWriteVirtualMemory, but without wineserver calls. Moreover
1950 * permissions are checked before accessing each page, to ensure that no
1951 * exceptions can happen.
1953 NTSTATUS virtual_uninterrupted_write_memory( void *addr, const void *buffer, SIZE_T size )
1955 struct file_view *view;
1956 sigset_t sigset;
1957 NTSTATUS ret = STATUS_ACCESS_VIOLATION;
1959 if (!size) return STATUS_SUCCESS;
1961 server_enter_uninterrupted_section( &csVirtual, &sigset );
1962 if ((view = VIRTUAL_FindView( addr, size )) && !(view->protect & VPROT_SYSTEM))
1964 char *page = ROUND_ADDR( addr, page_mask );
1965 size_t i, total = ROUND_SIZE( addr, size );
1967 for (i = 0; i < total; i += page_size)
1969 int prot = VIRTUAL_GetUnixProt( get_page_vprot( page + i ) & ~VPROT_WRITEWATCH );
1970 if (!(prot & PROT_WRITE)) goto done;
1972 if (view->protect & VPROT_WRITEWATCH) /* enable write access by clearing write watches */
1974 set_page_vprot_bits( addr, size, 0, VPROT_WRITEWATCH );
1975 mprotect_range( view, addr, size, 0, 0 );
1977 memcpy( addr, buffer, size );
1978 ret = STATUS_SUCCESS;
1980 done:
1981 server_leave_uninterrupted_section( &csVirtual, &sigset );
1982 return ret;
1986 /***********************************************************************
1987 * VIRTUAL_SetForceExec
1989 * Whether to force exec prot on all views.
1991 void VIRTUAL_SetForceExec( BOOL enable )
1993 struct file_view *view;
1994 sigset_t sigset;
1996 server_enter_uninterrupted_section( &csVirtual, &sigset );
1997 if (!force_exec_prot != !enable) /* change all existing views */
1999 force_exec_prot = enable;
2001 WINE_RB_FOR_EACH_ENTRY( view, &views_tree, struct file_view, entry )
2003 /* file mappings are always accessible */
2004 BYTE commit = is_view_valloc( view ) ? 0 : VPROT_COMMITTED;
2006 mprotect_range( view, view->base, view->size, commit, 0 );
2009 server_leave_uninterrupted_section( &csVirtual, &sigset );
2012 struct free_range
2014 char *base;
2015 char *limit;
2018 /* free reserved areas above the limit; callback for wine_mmap_enum_reserved_areas */
2019 static int free_reserved_memory( void *base, size_t size, void *arg )
2021 struct free_range *range = arg;
2023 if ((char *)base >= range->limit) return 0;
2024 if ((char *)base + size <= range->base) return 0;
2025 if ((char *)base < range->base)
2027 size -= range->base - (char *)base;
2028 base = range->base;
2030 if ((char *)base + size > range->limit) size = range->limit - (char *)base;
2031 remove_reserved_area( base, size );
2032 return 1; /* stop enumeration since the list has changed */
2035 /***********************************************************************
2036 * virtual_release_address_space
2038 * Release some address space once we have loaded and initialized the app.
2040 void virtual_release_address_space(void)
2042 struct free_range range;
2043 sigset_t sigset;
2045 if (is_win64) return;
2047 server_enter_uninterrupted_section( &csVirtual, &sigset );
2049 range.base = (char *)0x82000000;
2050 range.limit = user_space_limit;
2052 if (range.limit > range.base)
2054 while (wine_mmap_enum_reserved_areas( free_reserved_memory, &range, 1 )) /* nothing */;
2056 else
2058 #ifndef __APPLE__ /* dyld doesn't support parts of the WINE_DOS segment being unmapped */
2059 range.base = (char *)0x20000000;
2060 range.limit = (char *)0x7f000000;
2061 while (wine_mmap_enum_reserved_areas( free_reserved_memory, &range, 0 )) /* nothing */;
2062 #endif
2065 server_leave_uninterrupted_section( &csVirtual, &sigset );
2069 /***********************************************************************
2070 * virtual_set_large_address_space
2072 * Enable use of a large address space when allowed by the application.
2074 void virtual_set_large_address_space(void)
2076 IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress );
2078 if (!(nt->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE)) return;
2079 /* no large address space on win9x */
2080 if (NtCurrentTeb()->Peb->OSPlatformId != VER_PLATFORM_WIN32_NT) return;
2082 user_space_limit = working_set_limit = address_space_limit;
2086 /***********************************************************************
2087 * NtAllocateVirtualMemory (NTDLL.@)
2088 * ZwAllocateVirtualMemory (NTDLL.@)
2090 NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG zero_bits,
2091 SIZE_T *size_ptr, ULONG type, ULONG protect )
2093 void *base;
2094 unsigned int vprot;
2095 SIZE_T size = *size_ptr;
2096 SIZE_T mask = get_mask( zero_bits );
2097 NTSTATUS status = STATUS_SUCCESS;
2098 BOOL is_dos_memory = FALSE;
2099 struct file_view *view;
2100 sigset_t sigset;
2102 TRACE("%p %p %08lx %x %08x\n", process, *ret, size, type, protect );
2104 if (!size) return STATUS_INVALID_PARAMETER;
2105 if (!mask) return STATUS_INVALID_PARAMETER_3;
2107 if (process != NtCurrentProcess())
2109 apc_call_t call;
2110 apc_result_t result;
2112 memset( &call, 0, sizeof(call) );
2114 call.virtual_alloc.type = APC_VIRTUAL_ALLOC;
2115 call.virtual_alloc.addr = wine_server_client_ptr( *ret );
2116 call.virtual_alloc.size = *size_ptr;
2117 call.virtual_alloc.zero_bits = zero_bits;
2118 call.virtual_alloc.op_type = type;
2119 call.virtual_alloc.prot = protect;
2120 status = server_queue_process_apc( process, &call, &result );
2121 if (status != STATUS_SUCCESS) return status;
2123 if (result.virtual_alloc.status == STATUS_SUCCESS)
2125 *ret = wine_server_get_ptr( result.virtual_alloc.addr );
2126 *size_ptr = result.virtual_alloc.size;
2128 return result.virtual_alloc.status;
2131 /* Round parameters to a page boundary */
2133 if (is_beyond_limit( 0, size, working_set_limit )) return STATUS_WORKING_SET_LIMIT_RANGE;
2135 if (*ret)
2137 if (type & MEM_RESERVE) /* Round down to 64k boundary */
2138 base = ROUND_ADDR( *ret, mask );
2139 else
2140 base = ROUND_ADDR( *ret, page_mask );
2141 size = (((UINT_PTR)*ret + size + page_mask) & ~page_mask) - (UINT_PTR)base;
2143 /* disallow low 64k, wrap-around and kernel space */
2144 if (((char *)base < (char *)0x10000) ||
2145 ((char *)base + size < (char *)base) ||
2146 is_beyond_limit( base, size, address_space_limit ))
2148 /* address 1 is magic to mean DOS area */
2149 if (!base && *ret == (void *)1 && size == 0x110000) is_dos_memory = TRUE;
2150 else return STATUS_INVALID_PARAMETER;
2153 else
2155 base = NULL;
2156 size = (size + page_mask) & ~page_mask;
2159 /* Compute the alloc type flags */
2161 if (!(type & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)) ||
2162 (type & ~(MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_RESET)))
2164 WARN("called with wrong alloc type flags (%08x) !\n", type);
2165 return STATUS_INVALID_PARAMETER;
2168 /* Reserve the memory */
2170 if (use_locks) server_enter_uninterrupted_section( &csVirtual, &sigset );
2172 if ((type & MEM_RESERVE) || !base)
2174 if (!(status = get_vprot_flags( protect, &vprot, FALSE )))
2176 if (type & MEM_COMMIT) vprot |= VPROT_COMMITTED;
2177 if (type & MEM_WRITE_WATCH) vprot |= VPROT_WRITEWATCH;
2178 if (protect & PAGE_NOCACHE) vprot |= SEC_NOCACHE;
2180 if (vprot & VPROT_WRITECOPY) status = STATUS_INVALID_PAGE_PROTECTION;
2181 else if (is_dos_memory) status = allocate_dos_memory( &view, vprot );
2182 else status = map_view( &view, base, size, mask, type & MEM_TOP_DOWN, vprot );
2184 if (status == STATUS_SUCCESS) base = view->base;
2187 else if (type & MEM_RESET)
2189 if (!(view = VIRTUAL_FindView( base, size ))) status = STATUS_NOT_MAPPED_VIEW;
2190 else madvise( base, size, MADV_DONTNEED );
2192 else /* commit the pages */
2194 if (!(view = VIRTUAL_FindView( base, size ))) status = STATUS_NOT_MAPPED_VIEW;
2195 else if (view->protect & SEC_FILE) status = STATUS_ALREADY_COMMITTED;
2196 else if (!(status = set_protection( view, base, size, protect )) && (view->protect & SEC_RESERVE))
2198 SERVER_START_REQ( add_mapping_committed_range )
2200 req->handle = wine_server_obj_handle( view->mapping );
2201 req->offset = (char *)base - (char *)view->base;
2202 req->size = size;
2203 wine_server_call( req );
2205 SERVER_END_REQ;
2209 if (!status) VIRTUAL_DEBUG_DUMP_VIEW( view );
2211 if (use_locks) server_leave_uninterrupted_section( &csVirtual, &sigset );
2213 if (status == STATUS_SUCCESS)
2215 *ret = base;
2216 *size_ptr = size;
2218 return status;
2222 /***********************************************************************
2223 * NtFreeVirtualMemory (NTDLL.@)
2224 * ZwFreeVirtualMemory (NTDLL.@)
2226 NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *size_ptr, ULONG type )
2228 struct file_view *view;
2229 char *base;
2230 sigset_t sigset;
2231 NTSTATUS status = STATUS_SUCCESS;
2232 LPVOID addr = *addr_ptr;
2233 SIZE_T size = *size_ptr;
2235 TRACE("%p %p %08lx %x\n", process, addr, size, type );
2237 if (process != NtCurrentProcess())
2239 apc_call_t call;
2240 apc_result_t result;
2242 memset( &call, 0, sizeof(call) );
2244 call.virtual_free.type = APC_VIRTUAL_FREE;
2245 call.virtual_free.addr = wine_server_client_ptr( addr );
2246 call.virtual_free.size = size;
2247 call.virtual_free.op_type = type;
2248 status = server_queue_process_apc( process, &call, &result );
2249 if (status != STATUS_SUCCESS) return status;
2251 if (result.virtual_free.status == STATUS_SUCCESS)
2253 *addr_ptr = wine_server_get_ptr( result.virtual_free.addr );
2254 *size_ptr = result.virtual_free.size;
2256 return result.virtual_free.status;
2259 /* Fix the parameters */
2261 size = ROUND_SIZE( addr, size );
2262 base = ROUND_ADDR( addr, page_mask );
2264 /* avoid freeing the DOS area when a broken app passes a NULL pointer */
2265 if (!base) return STATUS_INVALID_PARAMETER;
2267 server_enter_uninterrupted_section( &csVirtual, &sigset );
2269 if (!(view = VIRTUAL_FindView( base, size )) || !is_view_valloc( view ))
2271 status = STATUS_INVALID_PARAMETER;
2273 else if (type == MEM_RELEASE)
2275 /* Free the pages */
2277 if (size || (base != view->base)) status = STATUS_INVALID_PARAMETER;
2278 else
2280 delete_view( view );
2281 *addr_ptr = base;
2282 *size_ptr = size;
2285 else if (type == MEM_DECOMMIT)
2287 status = decommit_pages( view, base - (char *)view->base, size );
2288 if (status == STATUS_SUCCESS)
2290 *addr_ptr = base;
2291 *size_ptr = size;
2294 else
2296 WARN("called with wrong free type flags (%08x) !\n", type);
2297 status = STATUS_INVALID_PARAMETER;
2300 server_leave_uninterrupted_section( &csVirtual, &sigset );
2301 return status;
2305 /***********************************************************************
2306 * NtProtectVirtualMemory (NTDLL.@)
2307 * ZwProtectVirtualMemory (NTDLL.@)
2309 NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *size_ptr,
2310 ULONG new_prot, ULONG *old_prot )
2312 struct file_view *view;
2313 sigset_t sigset;
2314 NTSTATUS status = STATUS_SUCCESS;
2315 char *base;
2316 BYTE vprot;
2317 SIZE_T size = *size_ptr;
2318 LPVOID addr = *addr_ptr;
2319 DWORD old;
2321 TRACE("%p %p %08lx %08x\n", process, addr, size, new_prot );
2323 if (!old_prot)
2324 return STATUS_ACCESS_VIOLATION;
2326 if (process != NtCurrentProcess())
2328 apc_call_t call;
2329 apc_result_t result;
2331 memset( &call, 0, sizeof(call) );
2333 call.virtual_protect.type = APC_VIRTUAL_PROTECT;
2334 call.virtual_protect.addr = wine_server_client_ptr( addr );
2335 call.virtual_protect.size = size;
2336 call.virtual_protect.prot = new_prot;
2337 status = server_queue_process_apc( process, &call, &result );
2338 if (status != STATUS_SUCCESS) return status;
2340 if (result.virtual_protect.status == STATUS_SUCCESS)
2342 *addr_ptr = wine_server_get_ptr( result.virtual_protect.addr );
2343 *size_ptr = result.virtual_protect.size;
2344 if (old_prot) *old_prot = result.virtual_protect.prot;
2346 return result.virtual_protect.status;
2349 /* Fix the parameters */
2351 size = ROUND_SIZE( addr, size );
2352 base = ROUND_ADDR( addr, page_mask );
2354 server_enter_uninterrupted_section( &csVirtual, &sigset );
2356 if ((view = VIRTUAL_FindView( base, size )))
2358 /* Make sure all the pages are committed */
2359 if (get_committed_size( view, base, &vprot ) >= size && (vprot & VPROT_COMMITTED))
2361 old = VIRTUAL_GetWin32Prot( vprot, view->protect );
2362 status = set_protection( view, base, size, new_prot );
2364 else status = STATUS_NOT_COMMITTED;
2366 else status = STATUS_INVALID_PARAMETER;
2368 if (!status) VIRTUAL_DEBUG_DUMP_VIEW( view );
2370 server_leave_uninterrupted_section( &csVirtual, &sigset );
2372 if (status == STATUS_SUCCESS)
2374 *addr_ptr = base;
2375 *size_ptr = size;
2376 *old_prot = old;
2378 return status;
2382 /* retrieve state for a free memory area; callback for wine_mmap_enum_reserved_areas */
2383 static int get_free_mem_state_callback( void *start, size_t size, void *arg )
2385 MEMORY_BASIC_INFORMATION *info = arg;
2386 void *end = (char *)start + size;
2388 if ((char *)info->BaseAddress + info->RegionSize < (char *)start) return 0;
2390 if (info->BaseAddress >= end)
2392 if (info->AllocationBase < end) info->AllocationBase = end;
2393 return 0;
2396 if (info->BaseAddress >= start || start <= address_space_start)
2398 /* it's a real free area */
2399 info->State = MEM_FREE;
2400 info->Protect = PAGE_NOACCESS;
2401 info->AllocationBase = 0;
2402 info->AllocationProtect = 0;
2403 info->Type = 0;
2404 if ((char *)info->BaseAddress + info->RegionSize > (char *)end)
2405 info->RegionSize = (char *)end - (char *)info->BaseAddress;
2407 else /* outside of the reserved area, pretend it's allocated */
2409 info->RegionSize = (char *)start - (char *)info->BaseAddress;
2410 info->State = MEM_RESERVE;
2411 info->Protect = PAGE_NOACCESS;
2412 info->AllocationProtect = PAGE_NOACCESS;
2413 info->Type = MEM_PRIVATE;
2415 return 1;
2418 #define UNIMPLEMENTED_INFO_CLASS(c) \
2419 case c: \
2420 FIXME("(process=%p,addr=%p) Unimplemented information class: " #c "\n", process, addr); \
2421 return STATUS_INVALID_INFO_CLASS
2423 /***********************************************************************
2424 * NtQueryVirtualMemory (NTDLL.@)
2425 * ZwQueryVirtualMemory (NTDLL.@)
2427 NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr,
2428 MEMORY_INFORMATION_CLASS info_class, PVOID buffer,
2429 SIZE_T len, SIZE_T *res_len )
2431 struct file_view *view;
2432 char *base, *alloc_base = 0, *alloc_end = working_set_limit;
2433 struct wine_rb_entry *ptr;
2434 MEMORY_BASIC_INFORMATION *info = buffer;
2435 sigset_t sigset;
2437 if (info_class != MemoryBasicInformation)
2439 switch(info_class)
2441 UNIMPLEMENTED_INFO_CLASS(MemoryWorkingSetList);
2442 UNIMPLEMENTED_INFO_CLASS(MemorySectionName);
2443 UNIMPLEMENTED_INFO_CLASS(MemoryBasicVlmInformation);
2445 default:
2446 FIXME("(%p,%p,info_class=%d,%p,%ld,%p) Unknown information class\n",
2447 process, addr, info_class, buffer, len, res_len);
2448 return STATUS_INVALID_INFO_CLASS;
2452 if (process != NtCurrentProcess())
2454 NTSTATUS status;
2455 apc_call_t call;
2456 apc_result_t result;
2458 memset( &call, 0, sizeof(call) );
2460 call.virtual_query.type = APC_VIRTUAL_QUERY;
2461 call.virtual_query.addr = wine_server_client_ptr( addr );
2462 status = server_queue_process_apc( process, &call, &result );
2463 if (status != STATUS_SUCCESS) return status;
2465 if (result.virtual_query.status == STATUS_SUCCESS)
2467 info->BaseAddress = wine_server_get_ptr( result.virtual_query.base );
2468 info->AllocationBase = wine_server_get_ptr( result.virtual_query.alloc_base );
2469 info->RegionSize = result.virtual_query.size;
2470 info->Protect = result.virtual_query.prot;
2471 info->AllocationProtect = result.virtual_query.alloc_prot;
2472 info->State = (DWORD)result.virtual_query.state << 12;
2473 info->Type = (DWORD)result.virtual_query.alloc_type << 16;
2474 if (info->RegionSize != result.virtual_query.size) /* truncated */
2475 return STATUS_INVALID_PARAMETER; /* FIXME */
2476 if (res_len) *res_len = sizeof(*info);
2478 return result.virtual_query.status;
2481 base = ROUND_ADDR( addr, page_mask );
2483 if (is_beyond_limit( base, 1, working_set_limit )) return STATUS_WORKING_SET_LIMIT_RANGE;
2485 /* Find the view containing the address */
2487 server_enter_uninterrupted_section( &csVirtual, &sigset );
2488 ptr = views_tree.root;
2489 while (ptr)
2491 view = WINE_RB_ENTRY_VALUE( ptr, struct file_view, entry );
2492 if ((char *)view->base > base)
2494 alloc_end = view->base;
2495 ptr = ptr->left;
2497 else if ((char *)view->base + view->size <= base)
2499 alloc_base = (char *)view->base + view->size;
2500 ptr = ptr->right;
2502 else
2504 alloc_base = view->base;
2505 alloc_end = (char *)view->base + view->size;
2506 break;
2510 /* Fill the info structure */
2512 info->AllocationBase = alloc_base;
2513 info->BaseAddress = base;
2514 info->RegionSize = alloc_end - base;
2516 if (!ptr)
2518 if (!wine_mmap_enum_reserved_areas( get_free_mem_state_callback, info, 0 ))
2520 /* not in a reserved area at all, pretend it's allocated */
2521 #ifdef __i386__
2522 if (base >= (char *)address_space_start)
2524 info->State = MEM_RESERVE;
2525 info->Protect = PAGE_NOACCESS;
2526 info->AllocationProtect = PAGE_NOACCESS;
2527 info->Type = MEM_PRIVATE;
2529 else
2530 #endif
2532 info->State = MEM_FREE;
2533 info->Protect = PAGE_NOACCESS;
2534 info->AllocationBase = 0;
2535 info->AllocationProtect = 0;
2536 info->Type = 0;
2540 else
2542 BYTE vprot;
2543 char *ptr;
2544 SIZE_T range_size = get_committed_size( view, base, &vprot );
2546 info->State = (vprot & VPROT_COMMITTED) ? MEM_COMMIT : MEM_RESERVE;
2547 info->Protect = (vprot & VPROT_COMMITTED) ? VIRTUAL_GetWin32Prot( vprot, view->protect ) : 0;
2548 info->AllocationProtect = VIRTUAL_GetWin32Prot( view->protect, view->protect );
2549 if (view->protect & SEC_IMAGE) info->Type = MEM_IMAGE;
2550 else if (view->protect & (SEC_FILE | SEC_RESERVE | SEC_COMMIT)) info->Type = MEM_MAPPED;
2551 else info->Type = MEM_PRIVATE;
2552 for (ptr = base; ptr < base + range_size; ptr += page_size)
2553 if ((get_page_vprot( ptr ) ^ vprot) & ~VPROT_WRITEWATCH) break;
2554 info->RegionSize = ptr - base;
2556 server_leave_uninterrupted_section( &csVirtual, &sigset );
2558 if (res_len) *res_len = sizeof(*info);
2559 return STATUS_SUCCESS;
2563 /***********************************************************************
2564 * NtLockVirtualMemory (NTDLL.@)
2565 * ZwLockVirtualMemory (NTDLL.@)
2567 NTSTATUS WINAPI NtLockVirtualMemory( HANDLE process, PVOID *addr, SIZE_T *size, ULONG unknown )
2569 NTSTATUS status = STATUS_SUCCESS;
2571 if (process != NtCurrentProcess())
2573 apc_call_t call;
2574 apc_result_t result;
2576 memset( &call, 0, sizeof(call) );
2578 call.virtual_lock.type = APC_VIRTUAL_LOCK;
2579 call.virtual_lock.addr = wine_server_client_ptr( *addr );
2580 call.virtual_lock.size = *size;
2581 status = server_queue_process_apc( process, &call, &result );
2582 if (status != STATUS_SUCCESS) return status;
2584 if (result.virtual_lock.status == STATUS_SUCCESS)
2586 *addr = wine_server_get_ptr( result.virtual_lock.addr );
2587 *size = result.virtual_lock.size;
2589 return result.virtual_lock.status;
2592 *size = ROUND_SIZE( *addr, *size );
2593 *addr = ROUND_ADDR( *addr, page_mask );
2595 if (mlock( *addr, *size )) status = STATUS_ACCESS_DENIED;
2596 return status;
2600 /***********************************************************************
2601 * NtUnlockVirtualMemory (NTDLL.@)
2602 * ZwUnlockVirtualMemory (NTDLL.@)
2604 NTSTATUS WINAPI NtUnlockVirtualMemory( HANDLE process, PVOID *addr, SIZE_T *size, ULONG unknown )
2606 NTSTATUS status = STATUS_SUCCESS;
2608 if (process != NtCurrentProcess())
2610 apc_call_t call;
2611 apc_result_t result;
2613 memset( &call, 0, sizeof(call) );
2615 call.virtual_unlock.type = APC_VIRTUAL_UNLOCK;
2616 call.virtual_unlock.addr = wine_server_client_ptr( *addr );
2617 call.virtual_unlock.size = *size;
2618 status = server_queue_process_apc( process, &call, &result );
2619 if (status != STATUS_SUCCESS) return status;
2621 if (result.virtual_unlock.status == STATUS_SUCCESS)
2623 *addr = wine_server_get_ptr( result.virtual_unlock.addr );
2624 *size = result.virtual_unlock.size;
2626 return result.virtual_unlock.status;
2629 *size = ROUND_SIZE( *addr, *size );
2630 *addr = ROUND_ADDR( *addr, page_mask );
2632 if (munlock( *addr, *size )) status = STATUS_ACCESS_DENIED;
2633 return status;
2637 /***********************************************************************
2638 * NtCreateSection (NTDLL.@)
2639 * ZwCreateSection (NTDLL.@)
2641 NTSTATUS WINAPI NtCreateSection( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
2642 const LARGE_INTEGER *size, ULONG protect,
2643 ULONG sec_flags, HANDLE file )
2645 NTSTATUS ret;
2646 unsigned int vprot, file_access = 0;
2647 data_size_t len;
2648 struct object_attributes *objattr;
2650 if ((ret = get_vprot_flags( protect, &vprot, sec_flags & SEC_IMAGE ))) return ret;
2651 if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
2653 if (vprot & VPROT_READ) file_access |= FILE_READ_DATA;
2654 if (vprot & VPROT_WRITE) file_access |= FILE_WRITE_DATA;
2656 SERVER_START_REQ( create_mapping )
2658 req->access = access;
2659 req->flags = sec_flags;
2660 req->file_handle = wine_server_obj_handle( file );
2661 req->file_access = file_access;
2662 req->size = size ? size->QuadPart : 0;
2663 wine_server_add_data( req, objattr, len );
2664 ret = wine_server_call( req );
2665 *handle = wine_server_ptr_handle( reply->handle );
2667 SERVER_END_REQ;
2669 RtlFreeHeap( GetProcessHeap(), 0, objattr );
2670 return ret;
2674 /***********************************************************************
2675 * NtOpenSection (NTDLL.@)
2676 * ZwOpenSection (NTDLL.@)
2678 NTSTATUS WINAPI NtOpenSection( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
2680 NTSTATUS ret;
2682 if ((ret = validate_open_object_attributes( attr ))) return ret;
2684 SERVER_START_REQ( open_mapping )
2686 req->access = access;
2687 req->attributes = attr->Attributes;
2688 req->rootdir = wine_server_obj_handle( attr->RootDirectory );
2689 if (attr->ObjectName)
2690 wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
2691 ret = wine_server_call( req );
2692 *handle = wine_server_ptr_handle( reply->handle );
2694 SERVER_END_REQ;
2695 return ret;
2699 /***********************************************************************
2700 * NtMapViewOfSection (NTDLL.@)
2701 * ZwMapViewOfSection (NTDLL.@)
2703 NTSTATUS WINAPI NtMapViewOfSection( HANDLE handle, HANDLE process, PVOID *addr_ptr, ULONG zero_bits,
2704 SIZE_T commit_size, const LARGE_INTEGER *offset_ptr, SIZE_T *size_ptr,
2705 SECTION_INHERIT inherit, ULONG alloc_type, ULONG protect )
2707 NTSTATUS res;
2708 mem_size_t full_size;
2709 ACCESS_MASK access;
2710 SIZE_T size, mask = get_mask( zero_bits );
2711 int unix_handle = -1, needs_close;
2712 unsigned int vprot, sec_flags;
2713 struct file_view *view;
2714 pe_image_info_t image_info;
2715 HANDLE dup_mapping, shared_file;
2716 LARGE_INTEGER offset;
2717 sigset_t sigset;
2719 offset.QuadPart = offset_ptr ? offset_ptr->QuadPart : 0;
2721 TRACE("handle=%p process=%p addr=%p off=%x%08x size=%lx access=%x\n",
2722 handle, process, *addr_ptr, offset.u.HighPart, offset.u.LowPart, *size_ptr, protect );
2724 /* Check parameters */
2726 if ((*addr_ptr && zero_bits) || !mask)
2727 return STATUS_INVALID_PARAMETER_4;
2729 #ifndef _WIN64
2730 if (!is_wow64 && (alloc_type & AT_ROUND_TO_PAGE))
2732 *addr_ptr = ROUND_ADDR( *addr_ptr, page_mask );
2733 mask = page_mask;
2735 #endif
2737 if ((offset.u.LowPart & mask) || (*addr_ptr && ((UINT_PTR)*addr_ptr & mask)))
2738 return STATUS_MAPPED_ALIGNMENT;
2740 switch(protect)
2742 case PAGE_NOACCESS:
2743 case PAGE_READONLY:
2744 case PAGE_WRITECOPY:
2745 access = SECTION_MAP_READ;
2746 break;
2747 case PAGE_READWRITE:
2748 access = SECTION_MAP_WRITE;
2749 break;
2750 case PAGE_EXECUTE:
2751 case PAGE_EXECUTE_READ:
2752 case PAGE_EXECUTE_WRITECOPY:
2753 access = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
2754 break;
2755 case PAGE_EXECUTE_READWRITE:
2756 access = SECTION_MAP_WRITE | SECTION_MAP_EXECUTE;
2757 break;
2758 default:
2759 return STATUS_INVALID_PAGE_PROTECTION;
2762 if (process != NtCurrentProcess())
2764 apc_call_t call;
2765 apc_result_t result;
2767 memset( &call, 0, sizeof(call) );
2769 call.map_view.type = APC_MAP_VIEW;
2770 call.map_view.handle = wine_server_obj_handle( handle );
2771 call.map_view.addr = wine_server_client_ptr( *addr_ptr );
2772 call.map_view.size = *size_ptr;
2773 call.map_view.offset = offset.QuadPart;
2774 call.map_view.zero_bits = zero_bits;
2775 call.map_view.alloc_type = alloc_type;
2776 call.map_view.prot = protect;
2777 res = server_queue_process_apc( process, &call, &result );
2778 if (res != STATUS_SUCCESS) return res;
2780 if ((NTSTATUS)result.map_view.status >= 0)
2782 *addr_ptr = wine_server_get_ptr( result.map_view.addr );
2783 *size_ptr = result.map_view.size;
2785 return result.map_view.status;
2788 SERVER_START_REQ( get_mapping_info )
2790 req->handle = wine_server_obj_handle( handle );
2791 req->access = access;
2792 wine_server_set_reply( req, &image_info, sizeof(image_info) );
2793 res = wine_server_call( req );
2794 sec_flags = reply->flags;
2795 full_size = reply->size;
2796 dup_mapping = wine_server_ptr_handle( reply->mapping );
2797 shared_file = wine_server_ptr_handle( reply->shared_file );
2799 SERVER_END_REQ;
2800 if (res) return res;
2802 if ((res = server_get_unix_fd( handle, 0, &unix_handle, &needs_close, NULL, NULL ))) goto done;
2804 if (sec_flags & SEC_IMAGE)
2806 void *base = wine_server_get_ptr( image_info.base );
2808 if ((ULONG_PTR)base != image_info.base) base = NULL;
2809 size = image_info.map_size;
2810 if (size != image_info.map_size) /* truncated */
2812 WARN( "Modules larger than 4Gb (%s) not supported\n",
2813 wine_dbgstr_longlong(image_info.map_size) );
2814 res = STATUS_INVALID_PARAMETER;
2815 goto done;
2817 if (shared_file)
2819 int shared_fd, shared_needs_close;
2821 if ((res = server_get_unix_fd( shared_file, FILE_READ_DATA|FILE_WRITE_DATA,
2822 &shared_fd, &shared_needs_close, NULL, NULL ))) goto done;
2823 res = map_image( handle, unix_handle, base, size, mask, image_info.header_size,
2824 shared_fd, dup_mapping, addr_ptr );
2825 if (shared_needs_close) close( shared_fd );
2826 close_handle( shared_file );
2828 else
2830 res = map_image( handle, unix_handle, base, size, mask, image_info.header_size,
2831 -1, dup_mapping, addr_ptr );
2833 if (needs_close) close( unix_handle );
2834 if (res >= 0) *size_ptr = size;
2835 return res;
2838 res = STATUS_INVALID_PARAMETER;
2839 if (offset.QuadPart >= full_size) goto done;
2840 if (*size_ptr)
2842 size = *size_ptr;
2843 if (size > full_size - offset.QuadPart)
2845 res = STATUS_INVALID_VIEW_SIZE;
2846 goto done;
2849 else
2851 size = full_size - offset.QuadPart;
2852 if (size != full_size - offset.QuadPart) /* truncated */
2854 WARN( "Files larger than 4Gb (%s) not supported on this platform\n",
2855 wine_dbgstr_longlong(full_size) );
2856 goto done;
2859 if (!(size = ROUND_SIZE( 0, size ))) goto done; /* wrap-around */
2861 /* Reserve a properly aligned area */
2863 server_enter_uninterrupted_section( &csVirtual, &sigset );
2865 get_vprot_flags( protect, &vprot, sec_flags & SEC_IMAGE );
2866 vprot |= sec_flags;
2867 if (!(sec_flags & SEC_RESERVE)) vprot |= VPROT_COMMITTED;
2868 res = map_view( &view, *addr_ptr, size, mask, FALSE, vprot );
2869 if (res)
2871 server_leave_uninterrupted_section( &csVirtual, &sigset );
2872 goto done;
2875 /* Map the file */
2877 TRACE("handle=%p size=%lx offset=%x%08x\n",
2878 handle, size, offset.u.HighPart, offset.u.LowPart );
2880 res = map_file_into_view( view, unix_handle, 0, size, offset.QuadPart, vprot, !dup_mapping );
2881 if (res == STATUS_SUCCESS)
2883 *addr_ptr = view->base;
2884 *size_ptr = size;
2885 view->mapping = dup_mapping;
2886 dup_mapping = 0; /* don't close it */
2887 VIRTUAL_DEBUG_DUMP_VIEW( view );
2889 else
2891 ERR( "map_file_into_view %p %lx %x%08x failed\n",
2892 view->base, size, offset.u.HighPart, offset.u.LowPart );
2893 delete_view( view );
2896 server_leave_uninterrupted_section( &csVirtual, &sigset );
2898 done:
2899 if (dup_mapping) close_handle( dup_mapping );
2900 if (needs_close) close( unix_handle );
2901 return res;
2905 /***********************************************************************
2906 * NtUnmapViewOfSection (NTDLL.@)
2907 * ZwUnmapViewOfSection (NTDLL.@)
2909 NTSTATUS WINAPI NtUnmapViewOfSection( HANDLE process, PVOID addr )
2911 struct file_view *view;
2912 NTSTATUS status = STATUS_NOT_MAPPED_VIEW;
2913 sigset_t sigset;
2915 if (process != NtCurrentProcess())
2917 apc_call_t call;
2918 apc_result_t result;
2920 memset( &call, 0, sizeof(call) );
2922 call.unmap_view.type = APC_UNMAP_VIEW;
2923 call.unmap_view.addr = wine_server_client_ptr( addr );
2924 status = server_queue_process_apc( process, &call, &result );
2925 if (status == STATUS_SUCCESS) status = result.unmap_view.status;
2926 return status;
2929 server_enter_uninterrupted_section( &csVirtual, &sigset );
2930 if ((view = VIRTUAL_FindView( addr, 0 )) && !is_view_valloc( view ))
2932 delete_view( view );
2933 status = STATUS_SUCCESS;
2935 server_leave_uninterrupted_section( &csVirtual, &sigset );
2936 return status;
2940 /******************************************************************************
2941 * NtQuerySection (NTDLL.@)
2942 * ZwQuerySection (NTDLL.@)
2944 NTSTATUS WINAPI NtQuerySection( HANDLE handle, SECTION_INFORMATION_CLASS class, void *ptr,
2945 ULONG size, ULONG *ret_size )
2947 NTSTATUS status;
2948 pe_image_info_t image_info;
2950 switch (class)
2952 case SectionBasicInformation:
2953 if (size < sizeof(SECTION_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2954 break;
2955 case SectionImageInformation:
2956 if (size < sizeof(SECTION_IMAGE_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
2957 break;
2958 default:
2959 FIXME( "class %u not implemented\n", class );
2960 return STATUS_NOT_IMPLEMENTED;
2962 if (!ptr) return STATUS_ACCESS_VIOLATION;
2964 SERVER_START_REQ( get_mapping_info )
2966 req->handle = wine_server_obj_handle( handle );
2967 req->access = SECTION_QUERY;
2968 wine_server_set_reply( req, &image_info, sizeof(image_info) );
2969 if (!(status = wine_server_call( req )))
2971 if (class == SectionBasicInformation)
2973 SECTION_BASIC_INFORMATION *info = ptr;
2974 info->Attributes = reply->flags;
2975 info->BaseAddress = NULL;
2976 info->Size.QuadPart = reply->size;
2977 if (ret_size) *ret_size = sizeof(*info);
2979 else if (reply->flags & SEC_IMAGE)
2981 SECTION_IMAGE_INFORMATION *info = ptr;
2982 info->TransferAddress = wine_server_get_ptr( image_info.entry_point );
2983 info->ZeroBits = image_info.zerobits;
2984 info->MaximumStackSize = image_info.stack_size;
2985 info->CommittedStackSize = image_info.stack_commit;
2986 info->SubSystemType = image_info.subsystem;
2987 info->SubsystemVersionLow = image_info.subsystem_low;
2988 info->SubsystemVersionHigh = image_info.subsystem_high;
2989 info->GpValue = image_info.gp;
2990 info->ImageCharacteristics = image_info.image_charact;
2991 info->DllCharacteristics = image_info.dll_charact;
2992 info->Machine = image_info.machine;
2993 info->ImageContainsCode = image_info.contains_code;
2994 info->ImageFlags = image_info.image_flags;
2995 info->LoaderFlags = image_info.loader_flags;
2996 info->ImageFileSize = image_info.file_size;
2997 info->CheckSum = image_info.checksum;
2998 if (ret_size) *ret_size = sizeof(*info);
3000 else status = STATUS_SECTION_NOT_IMAGE;
3003 SERVER_END_REQ;
3005 return status;
3009 /***********************************************************************
3010 * NtFlushVirtualMemory (NTDLL.@)
3011 * ZwFlushVirtualMemory (NTDLL.@)
3013 NTSTATUS WINAPI NtFlushVirtualMemory( HANDLE process, LPCVOID *addr_ptr,
3014 SIZE_T *size_ptr, ULONG unknown )
3016 struct file_view *view;
3017 NTSTATUS status = STATUS_SUCCESS;
3018 sigset_t sigset;
3019 void *addr = ROUND_ADDR( *addr_ptr, page_mask );
3021 if (process != NtCurrentProcess())
3023 apc_call_t call;
3024 apc_result_t result;
3026 memset( &call, 0, sizeof(call) );
3028 call.virtual_flush.type = APC_VIRTUAL_FLUSH;
3029 call.virtual_flush.addr = wine_server_client_ptr( addr );
3030 call.virtual_flush.size = *size_ptr;
3031 status = server_queue_process_apc( process, &call, &result );
3032 if (status != STATUS_SUCCESS) return status;
3034 if (result.virtual_flush.status == STATUS_SUCCESS)
3036 *addr_ptr = wine_server_get_ptr( result.virtual_flush.addr );
3037 *size_ptr = result.virtual_flush.size;
3039 return result.virtual_flush.status;
3042 server_enter_uninterrupted_section( &csVirtual, &sigset );
3043 if (!(view = VIRTUAL_FindView( addr, *size_ptr ))) status = STATUS_INVALID_PARAMETER;
3044 else
3046 if (!*size_ptr) *size_ptr = view->size;
3047 *addr_ptr = addr;
3048 #ifdef MS_ASYNC
3049 if (msync( addr, *size_ptr, MS_ASYNC )) status = STATUS_NOT_MAPPED_DATA;
3050 #endif
3052 server_leave_uninterrupted_section( &csVirtual, &sigset );
3053 return status;
3057 /***********************************************************************
3058 * NtGetWriteWatch (NTDLL.@)
3059 * ZwGetWriteWatch (NTDLL.@)
3061 NTSTATUS WINAPI NtGetWriteWatch( HANDLE process, ULONG flags, PVOID base, SIZE_T size, PVOID *addresses,
3062 ULONG_PTR *count, ULONG *granularity )
3064 struct file_view *view;
3065 NTSTATUS status = STATUS_SUCCESS;
3066 sigset_t sigset;
3068 size = ROUND_SIZE( base, size );
3069 base = ROUND_ADDR( base, page_mask );
3071 if (!count || !granularity) return STATUS_ACCESS_VIOLATION;
3072 if (!*count || !size) return STATUS_INVALID_PARAMETER;
3073 if (flags & ~WRITE_WATCH_FLAG_RESET) return STATUS_INVALID_PARAMETER;
3075 if (!addresses) return STATUS_ACCESS_VIOLATION;
3077 TRACE( "%p %x %p-%p %p %lu\n", process, flags, base, (char *)base + size,
3078 addresses, *count );
3080 server_enter_uninterrupted_section( &csVirtual, &sigset );
3082 if ((view = VIRTUAL_FindView( base, size )) && (view->protect & VPROT_WRITEWATCH))
3084 ULONG_PTR pos = 0;
3085 char *addr = base;
3086 char *end = addr + size;
3088 while (pos < *count && addr < end)
3090 if (!(get_page_vprot( addr ) & VPROT_WRITEWATCH)) addresses[pos++] = addr;
3091 addr += page_size;
3093 if (flags & WRITE_WATCH_FLAG_RESET) reset_write_watches( view, base, addr - (char *)base );
3094 *count = pos;
3095 *granularity = page_size;
3097 else status = STATUS_INVALID_PARAMETER;
3099 server_leave_uninterrupted_section( &csVirtual, &sigset );
3100 return status;
3104 /***********************************************************************
3105 * NtResetWriteWatch (NTDLL.@)
3106 * ZwResetWriteWatch (NTDLL.@)
3108 NTSTATUS WINAPI NtResetWriteWatch( HANDLE process, PVOID base, SIZE_T size )
3110 struct file_view *view;
3111 NTSTATUS status = STATUS_SUCCESS;
3112 sigset_t sigset;
3114 size = ROUND_SIZE( base, size );
3115 base = ROUND_ADDR( base, page_mask );
3117 TRACE( "%p %p-%p\n", process, base, (char *)base + size );
3119 if (!size) return STATUS_INVALID_PARAMETER;
3121 server_enter_uninterrupted_section( &csVirtual, &sigset );
3123 if ((view = VIRTUAL_FindView( base, size )) && (view->protect & VPROT_WRITEWATCH))
3124 reset_write_watches( view, base, size );
3125 else
3126 status = STATUS_INVALID_PARAMETER;
3128 server_leave_uninterrupted_section( &csVirtual, &sigset );
3129 return status;
3133 /***********************************************************************
3134 * NtReadVirtualMemory (NTDLL.@)
3135 * ZwReadVirtualMemory (NTDLL.@)
3137 NTSTATUS WINAPI NtReadVirtualMemory( HANDLE process, const void *addr, void *buffer,
3138 SIZE_T size, SIZE_T *bytes_read )
3140 NTSTATUS status;
3142 if (virtual_check_buffer_for_write( buffer, size ))
3144 SERVER_START_REQ( read_process_memory )
3146 req->handle = wine_server_obj_handle( process );
3147 req->addr = wine_server_client_ptr( addr );
3148 wine_server_set_reply( req, buffer, size );
3149 if ((status = wine_server_call( req ))) size = 0;
3151 SERVER_END_REQ;
3153 else
3155 status = STATUS_ACCESS_VIOLATION;
3156 size = 0;
3158 if (bytes_read) *bytes_read = size;
3159 return status;
3163 /***********************************************************************
3164 * NtWriteVirtualMemory (NTDLL.@)
3165 * ZwWriteVirtualMemory (NTDLL.@)
3167 NTSTATUS WINAPI NtWriteVirtualMemory( HANDLE process, void *addr, const void *buffer,
3168 SIZE_T size, SIZE_T *bytes_written )
3170 NTSTATUS status;
3172 if (virtual_check_buffer_for_read( buffer, size ))
3174 SERVER_START_REQ( write_process_memory )
3176 req->handle = wine_server_obj_handle( process );
3177 req->addr = wine_server_client_ptr( addr );
3178 wine_server_add_data( req, buffer, size );
3179 if ((status = wine_server_call( req ))) size = 0;
3181 SERVER_END_REQ;
3183 else
3185 status = STATUS_PARTIAL_COPY;
3186 size = 0;
3188 if (bytes_written) *bytes_written = size;
3189 return status;
3193 /***********************************************************************
3194 * NtAreMappedFilesTheSame (NTDLL.@)
3195 * ZwAreMappedFilesTheSame (NTDLL.@)
3197 NTSTATUS WINAPI NtAreMappedFilesTheSame(PVOID addr1, PVOID addr2)
3199 struct file_view *view1, *view2;
3200 struct stat st1, st2;
3201 NTSTATUS status;
3202 sigset_t sigset;
3204 TRACE("%p %p\n", addr1, addr2);
3206 server_enter_uninterrupted_section( &csVirtual, &sigset );
3208 view1 = VIRTUAL_FindView( addr1, 0 );
3209 view2 = VIRTUAL_FindView( addr2, 0 );
3211 if (!view1 || !view2)
3212 status = STATUS_INVALID_ADDRESS;
3213 else if (is_view_valloc( view1 ) || is_view_valloc( view2 ))
3214 status = STATUS_CONFLICTING_ADDRESSES;
3215 else if (view1 == view2)
3216 status = STATUS_SUCCESS;
3217 else if (!(view1->protect & SEC_IMAGE) || !(view2->protect & SEC_IMAGE))
3218 status = STATUS_NOT_SAME_DEVICE;
3219 else if (!stat_mapping_file( view1, &st1 ) && !stat_mapping_file( view2, &st2 ) &&
3220 st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
3221 status = STATUS_SUCCESS;
3222 else
3223 status = STATUS_NOT_SAME_DEVICE;
3225 server_leave_uninterrupted_section( &csVirtual, &sigset );
3226 return status;