2 * Win32 virtual memory functions
4 * Copyright 1997, 2002, 2020 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
35 #include <sys/types.h>
36 #include <sys/socket.h>
39 #ifdef HAVE_SYS_SYSINFO_H
40 # include <sys/sysinfo.h>
42 #ifdef HAVE_SYS_SYSCTL_H
43 # include <sys/sysctl.h>
45 #ifdef HAVE_SYS_PARAM_H
46 # include <sys/param.h>
48 #ifdef HAVE_SYS_QUEUE_H
49 # include <sys/queue.h>
51 #ifdef HAVE_SYS_USER_H
52 # include <sys/user.h>
54 #ifdef HAVE_LIBPROCSTAT_H
55 # include <libprocstat.h>
59 #ifdef HAVE_VALGRIND_VALGRIND_H
60 # include <valgrind/valgrind.h>
62 #if defined(__APPLE__)
63 # include <mach/mach_init.h>
64 # include <mach/mach_vm.h>
68 #define WIN32_NO_STATUS
73 #include "wine/list.h"
74 #include "wine/rbtree.h"
75 #include "unix_private.h"
76 #include "wine/debug.h"
78 WINE_DEFAULT_DEBUG_CHANNEL(virtual);
79 WINE_DECLARE_DEBUG_CHANNEL(module
);
80 WINE_DECLARE_DEBUG_CHANNEL(virtual_ranges
);
95 static struct list reserved_areas
= LIST_INIT(reserved_areas
);
100 unsigned int refcount
;
107 static struct list builtin_modules
= LIST_INIT( builtin_modules
);
111 struct wine_rb_entry entry
; /* entry in global view tree */
112 void *base
; /* base address */
113 size_t size
; /* size in bytes */
114 unsigned int protect
; /* protection for all pages at allocation time and SEC_* flags */
117 /* per-page protection flags */
118 #define VPROT_READ 0x01
119 #define VPROT_WRITE 0x02
120 #define VPROT_EXEC 0x04
121 #define VPROT_WRITECOPY 0x08
122 #define VPROT_GUARD 0x10
123 #define VPROT_COMMITTED 0x20
124 #define VPROT_WRITEWATCH 0x40
125 /* per-mapping protection flags */
126 #define VPROT_SYSTEM 0x0200 /* system view (underlying mmap not under our control) */
127 #define VPROT_PLACEHOLDER 0x0400
128 #define VPROT_FREE_PLACEHOLDER 0x0800
130 /* Conversion from VPROT_* to Win32 flags */
131 static const BYTE VIRTUAL_Win32Flags
[16] =
133 PAGE_NOACCESS
, /* 0 */
134 PAGE_READONLY
, /* READ */
135 PAGE_READWRITE
, /* WRITE */
136 PAGE_READWRITE
, /* READ | WRITE */
137 PAGE_EXECUTE
, /* EXEC */
138 PAGE_EXECUTE_READ
, /* READ | EXEC */
139 PAGE_EXECUTE_READWRITE
, /* WRITE | EXEC */
140 PAGE_EXECUTE_READWRITE
, /* READ | WRITE | EXEC */
141 PAGE_WRITECOPY
, /* WRITECOPY */
142 PAGE_WRITECOPY
, /* READ | WRITECOPY */
143 PAGE_WRITECOPY
, /* WRITE | WRITECOPY */
144 PAGE_WRITECOPY
, /* READ | WRITE | WRITECOPY */
145 PAGE_EXECUTE_WRITECOPY
, /* EXEC | WRITECOPY */
146 PAGE_EXECUTE_WRITECOPY
, /* READ | EXEC | WRITECOPY */
147 PAGE_EXECUTE_WRITECOPY
, /* WRITE | EXEC | WRITECOPY */
148 PAGE_EXECUTE_WRITECOPY
/* READ | WRITE | EXEC | WRITECOPY */
151 static struct wine_rb_tree views_tree
;
152 static pthread_mutex_t virtual_mutex
;
154 static const UINT page_shift
= 12;
155 static const UINT_PTR page_mask
= 0xfff;
156 static const UINT_PTR granularity_mask
= 0xffff;
158 /* Note: these are Windows limits, you cannot change them. */
160 static void *address_space_start
= (void *)0x110000; /* keep DOS area clear */
162 static void *address_space_start
= (void *)0x10000;
166 static void *address_space_limit
= (void *)0xffffffff0000; /* top of the total available address space */
167 #elif defined(_WIN64)
168 static void *address_space_limit
= (void *)0x7fffffff0000;
170 static void *address_space_limit
= (void *)0xc0000000;
174 static void *user_space_limit
= (void *)0x7fffffff0000; /* top of the user address space */
175 static void *working_set_limit
= (void *)0x7fffffff0000; /* top of the current working set */
177 static void *user_space_limit
= (void *)0x7fff0000;
178 static void *working_set_limit
= (void *)0x7fff0000;
181 static UINT64
*arm64ec_map
;
183 struct _KUSER_SHARED_DATA
*user_shared_data
= (void *)0x7ffe0000;
185 /* TEB allocation blocks */
186 static void *teb_block
;
187 static void **next_free_teb
;
188 static int teb_block_pos
;
189 static struct list teb_list
= LIST_INIT( teb_list
);
191 #define ROUND_ADDR(addr,mask) ((void *)((UINT_PTR)(addr) & ~(UINT_PTR)(mask)))
192 #define ROUND_SIZE(addr,size) (((SIZE_T)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask)
194 #define VIRTUAL_DEBUG_DUMP_VIEW(view) do { if (TRACE_ON(virtual)) dump_view(view); } while (0)
195 #define VIRTUAL_DEBUG_DUMP_RANGES() do { if (TRACE_ON(virtual_ranges)) dump_free_ranges(); } while (0)
197 #ifndef MAP_NORESERVE
198 #define MAP_NORESERVE 0
201 #ifdef _WIN64 /* on 64-bit the page protection bytes use a 2-level table */
202 static const size_t pages_vprot_shift
= 20;
203 static const size_t pages_vprot_mask
= (1 << 20) - 1;
204 static size_t pages_vprot_size
;
205 static BYTE
**pages_vprot
;
206 #else /* on 32-bit we use a simple array with one byte per page */
207 static BYTE
*pages_vprot
;
210 static struct file_view
*view_block_start
, *view_block_end
, *next_free_view
;
211 static const size_t view_block_size
= 0x100000;
212 static void *preload_reserve_start
;
213 static void *preload_reserve_end
;
214 static BOOL force_exec_prot
; /* whether to force PROT_EXEC on all PROT_READ mmaps */
222 static struct range_entry
*free_ranges
;
223 static struct range_entry
*free_ranges_end
;
226 static inline BOOL
is_beyond_limit( const void *addr
, size_t size
, const void *limit
)
228 return (addr
>= limit
|| (const char *)addr
+ size
> (const char *)limit
);
231 /* mmap() anonymous memory at a fixed address */
232 void *anon_mmap_fixed( void *start
, size_t size
, int prot
, int flags
)
234 return mmap( start
, size
, prot
, MAP_PRIVATE
| MAP_ANON
| MAP_FIXED
| flags
, -1, 0 );
237 /* allocate anonymous mmap() memory at any address */
238 void *anon_mmap_alloc( size_t size
, int prot
)
240 return mmap( NULL
, size
, prot
, MAP_PRIVATE
| MAP_ANON
, -1, 0 );
244 static void mmap_add_reserved_area( void *addr
, SIZE_T size
)
246 struct reserved_area
*area
;
249 if (!((intptr_t)addr
+ size
)) size
--; /* avoid wrap-around */
251 LIST_FOR_EACH( ptr
, &reserved_areas
)
253 area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
254 if (area
->base
> addr
)
256 /* try to merge with the next one */
257 if ((char *)addr
+ size
== (char *)area
->base
)
265 else if ((char *)area
->base
+ area
->size
== (char *)addr
)
267 /* merge with the previous one */
270 /* try to merge with the next one too */
271 if ((ptr
= list_next( &reserved_areas
, ptr
)))
273 struct reserved_area
*next
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
274 if ((char *)addr
+ size
== (char *)next
->base
)
276 area
->size
+= next
->size
;
277 list_remove( &next
->entry
);
285 if ((area
= malloc( sizeof(*area
) )))
289 list_add_before( ptr
, &area
->entry
);
293 static void mmap_remove_reserved_area( void *addr
, SIZE_T size
)
295 struct reserved_area
*area
;
298 if (!((intptr_t)addr
+ size
)) size
--; /* avoid wrap-around */
300 ptr
= list_head( &reserved_areas
);
301 /* find the first area covering address */
304 area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
305 if ((char *)area
->base
>= (char *)addr
+ size
) break; /* outside the range */
306 if ((char *)area
->base
+ area
->size
> (char *)addr
) /* overlaps range */
308 if (area
->base
>= addr
)
310 if ((char *)area
->base
+ area
->size
> (char *)addr
+ size
)
312 /* range overlaps beginning of area only -> shrink area */
313 area
->size
-= (char *)addr
+ size
- (char *)area
->base
;
314 area
->base
= (char *)addr
+ size
;
319 /* range contains the whole area -> remove area completely */
320 ptr
= list_next( &reserved_areas
, ptr
);
321 list_remove( &area
->entry
);
328 if ((char *)area
->base
+ area
->size
> (char *)addr
+ size
)
330 /* range is in the middle of area -> split area in two */
331 struct reserved_area
*new_area
= malloc( sizeof(*new_area
) );
334 new_area
->base
= (char *)addr
+ size
;
335 new_area
->size
= (char *)area
->base
+ area
->size
- (char *)new_area
->base
;
336 list_add_after( ptr
, &new_area
->entry
);
338 else size
= (char *)area
->base
+ area
->size
- (char *)addr
;
339 area
->size
= (char *)addr
- (char *)area
->base
;
344 /* range overlaps end of area only -> shrink area */
345 area
->size
= (char *)addr
- (char *)area
->base
;
349 ptr
= list_next( &reserved_areas
, ptr
);
353 static int mmap_is_in_reserved_area( void *addr
, SIZE_T size
)
355 struct reserved_area
*area
;
358 LIST_FOR_EACH( ptr
, &reserved_areas
)
360 area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
361 if (area
->base
> addr
) break;
362 if ((char *)area
->base
+ area
->size
<= (char *)addr
) continue;
363 /* area must contain block completely */
364 if ((char *)area
->base
+ area
->size
< (char *)addr
+ size
) return -1;
370 static int mmap_enum_reserved_areas( int (*enum_func
)(void *base
, SIZE_T size
, void *arg
),
371 void *arg
, int top_down
)
378 for (ptr
= reserved_areas
.prev
; ptr
!= &reserved_areas
; ptr
= ptr
->prev
)
380 struct reserved_area
*area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
381 if ((ret
= enum_func( area
->base
, area
->size
, arg
))) break;
386 for (ptr
= reserved_areas
.next
; ptr
!= &reserved_areas
; ptr
= ptr
->next
)
388 struct reserved_area
*area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
389 if ((ret
= enum_func( area
->base
, area
->size
, arg
))) break;
395 static void *anon_mmap_tryfixed( void *start
, size_t size
, int prot
, int flags
)
399 #ifdef MAP_FIXED_NOREPLACE
400 ptr
= mmap( start
, size
, prot
, MAP_FIXED_NOREPLACE
| MAP_PRIVATE
| MAP_ANON
| flags
, -1, 0 );
401 #elif defined(MAP_TRYFIXED)
402 ptr
= mmap( start
, size
, prot
, MAP_TRYFIXED
| MAP_PRIVATE
| MAP_ANON
| flags
, -1, 0 );
403 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
404 ptr
= mmap( start
, size
, prot
, MAP_FIXED
| MAP_EXCL
| MAP_PRIVATE
| MAP_ANON
| flags
, -1, 0 );
405 if (ptr
== MAP_FAILED
&& errno
== EINVAL
) errno
= EEXIST
;
406 #elif defined(__APPLE__)
407 mach_vm_address_t result
= (mach_vm_address_t
)start
;
408 kern_return_t ret
= mach_vm_map( mach_task_self(), &result
, size
, 0, VM_FLAGS_FIXED
,
409 MEMORY_OBJECT_NULL
, 0, 0, prot
, VM_PROT_ALL
, VM_INHERIT_COPY
);
413 if ((ptr
= anon_mmap_fixed( start
, size
, prot
, flags
)) == MAP_FAILED
)
414 mach_vm_deallocate( mach_task_self(), result
, size
);
418 errno
= (ret
== KERN_NO_SPACE
? EEXIST
: ENOMEM
);
422 ptr
= mmap( start
, size
, prot
, MAP_PRIVATE
| MAP_ANON
| flags
, -1, 0 );
424 if (ptr
!= MAP_FAILED
&& ptr
!= start
)
426 if (is_beyond_limit( ptr
, size
, user_space_limit
))
428 anon_mmap_fixed( ptr
, size
, PROT_NONE
, MAP_NORESERVE
);
429 mmap_add_reserved_area( ptr
, size
);
431 else munmap( ptr
, size
);
438 static void reserve_area( void *addr
, void *end
)
443 static const mach_vm_address_t max_address
= VM_MAX_ADDRESS
;
445 static const mach_vm_address_t max_address
= MACH_VM_MAX_ADDRESS
;
447 mach_vm_address_t address
= (mach_vm_address_t
)addr
;
448 mach_vm_address_t end_address
= (mach_vm_address_t
)end
;
450 if (!end_address
|| max_address
< end_address
)
451 end_address
= max_address
;
453 while (address
< end_address
)
455 mach_vm_address_t hole_address
= address
;
458 vm_region_basic_info_data_64_t info
;
459 mach_msg_type_number_t count
= VM_REGION_BASIC_INFO_COUNT_64
;
460 mach_port_t dummy_object_name
= MACH_PORT_NULL
;
462 /* find the mapped region at or above the current address. */
463 ret
= mach_vm_region(mach_task_self(), &address
, &size
, VM_REGION_BASIC_INFO_64
,
464 (vm_region_info_t
)&info
, &count
, &dummy_object_name
);
465 if (ret
!= KERN_SUCCESS
)
467 address
= max_address
;
471 if (end_address
< address
)
472 address
= end_address
;
473 if (hole_address
< address
)
475 /* found a hole, attempt to reserve it. */
476 size_t hole_size
= address
- hole_address
;
477 mach_vm_address_t alloc_address
= hole_address
;
479 ret
= mach_vm_map( mach_task_self(), &alloc_address
, hole_size
, 0, VM_FLAGS_FIXED
,
480 MEMORY_OBJECT_NULL
, 0, 0, PROT_NONE
, VM_PROT_ALL
, VM_INHERIT_COPY
);
481 if (!ret
) mmap_add_reserved_area( (void*)hole_address
, hole_size
);
482 else if (ret
== KERN_NO_SPACE
)
484 /* something filled (part of) the hole before we could.
485 go back and look again. */
486 address
= hole_address
;
494 size_t size
= (char *)end
- (char *)addr
;
498 if ((ptr
= anon_mmap_tryfixed( addr
, size
, PROT_NONE
, MAP_NORESERVE
)) != MAP_FAILED
)
500 mmap_add_reserved_area( addr
, size
);
503 size
= (size
/ 2) & ~granularity_mask
;
506 reserve_area( addr
, (char *)addr
+ size
);
507 reserve_area( (char *)addr
+ size
, end
);
509 #endif /* __APPLE__ */
513 static void mmap_init( const struct preload_info
*preload_info
)
518 char * const stack_ptr
= &stack
;
520 char *user_space_limit
= (char *)0x7ffe0000;
525 /* check for a reserved area starting at the user space limit */
526 /* to avoid wasting time trying to allocate it again */
527 for (i
= 0; preload_info
[i
].size
; i
++)
529 if ((char *)preload_info
[i
].addr
> user_space_limit
) break;
530 if ((char *)preload_info
[i
].addr
+ preload_info
[i
].size
> user_space_limit
)
532 user_space_limit
= (char *)preload_info
[i
].addr
+ preload_info
[i
].size
;
537 else reserve_area( (void *)0x00010000, (void *)0x40000000 );
541 if (stack_ptr
>= user_space_limit
)
544 char *base
= stack_ptr
- ((unsigned int)stack_ptr
& granularity_mask
) - (granularity_mask
+ 1);
545 if (base
> user_space_limit
) reserve_area( user_space_limit
, base
);
546 base
= stack_ptr
- ((unsigned int)stack_ptr
& granularity_mask
) + (granularity_mask
+ 1);
547 #if defined(linux) || defined(__FreeBSD__) || defined (__FreeBSD_kernel__) || defined(__DragonFly__)
548 /* Heuristic: assume the stack is near the end of the address */
549 /* space, this avoids a lot of futile allocation attempts */
550 end
= (char *)(((unsigned long)base
+ 0x0fffffff) & 0xf0000000);
552 reserve_area( base
, end
);
556 reserve_area( user_space_limit
, 0 );
560 if (preload_info
) return;
561 /* if we don't have a preloader, try to reserve the space now */
562 reserve_area( (void *)0x000000010000, (void *)0x000068000000 );
563 reserve_area( (void *)0x00007ff00000, (void *)0x00007fff0000 );
564 reserve_area( (void *)0x7ffffe000000, (void *)0x7fffffff0000 );
570 /***********************************************************************
571 * get_wow_user_space_limit
573 static void *get_wow_user_space_limit(void)
576 if (main_image_info
.ImageCharacteristics
& IMAGE_FILE_LARGE_ADDRESS_AWARE
) return (void *)0xc0000000;
577 return (void *)0x7fff0000;
579 return user_space_limit
;
583 /***********************************************************************
586 static void add_builtin_module( void *module
, void *handle
)
588 struct builtin_module
*builtin
;
590 if (!(builtin
= malloc( sizeof(*builtin
) ))) return;
591 builtin
->handle
= handle
;
592 builtin
->module
= module
;
593 builtin
->refcount
= 1;
594 builtin
->unix_path
= NULL
;
595 builtin
->unix_handle
= NULL
;
596 list_add_tail( &builtin_modules
, &builtin
->entry
);
600 /***********************************************************************
601 * release_builtin_module
603 static void release_builtin_module( void *module
)
605 struct builtin_module
*builtin
;
607 LIST_FOR_EACH_ENTRY( builtin
, &builtin_modules
, struct builtin_module
, entry
)
609 if (builtin
->module
!= module
) continue;
610 if (!--builtin
->refcount
)
612 list_remove( &builtin
->entry
);
613 if (builtin
->handle
) dlclose( builtin
->handle
);
614 if (builtin
->unix_handle
) dlclose( builtin
->unix_handle
);
615 free( builtin
->unix_path
);
623 /***********************************************************************
624 * get_builtin_so_handle
626 void *get_builtin_so_handle( void *module
)
630 struct builtin_module
*builtin
;
632 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
633 LIST_FOR_EACH_ENTRY( builtin
, &builtin_modules
, struct builtin_module
, entry
)
635 if (builtin
->module
!= module
) continue;
636 ret
= builtin
->handle
;
637 if (ret
) builtin
->refcount
++;
640 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
645 /***********************************************************************
646 * get_builtin_unix_funcs
648 static NTSTATUS
get_builtin_unix_funcs( void *module
, BOOL wow
, const void **funcs
)
650 const char *ptr_name
= wow
? "__wine_unix_call_wow64_funcs" : "__wine_unix_call_funcs";
652 NTSTATUS status
= STATUS_DLL_NOT_FOUND
;
653 struct builtin_module
*builtin
;
655 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
656 LIST_FOR_EACH_ENTRY( builtin
, &builtin_modules
, struct builtin_module
, entry
)
658 if (builtin
->module
!= module
) continue;
659 if (builtin
->unix_path
&& !builtin
->unix_handle
)
660 builtin
->unix_handle
= dlopen( builtin
->unix_path
, RTLD_NOW
);
661 if (builtin
->unix_handle
)
663 *funcs
= dlsym( builtin
->unix_handle
, ptr_name
);
664 status
= *funcs
? STATUS_SUCCESS
: STATUS_ENTRYPOINT_NOT_FOUND
;
668 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
673 /***********************************************************************
674 * load_builtin_unixlib
676 NTSTATUS
load_builtin_unixlib( void *module
, const char *name
)
679 NTSTATUS status
= STATUS_SUCCESS
;
680 struct builtin_module
*builtin
;
682 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
683 LIST_FOR_EACH_ENTRY( builtin
, &builtin_modules
, struct builtin_module
, entry
)
685 if (builtin
->module
!= module
) continue;
686 if (!builtin
->unix_path
) builtin
->unix_path
= strdup( name
);
687 else status
= STATUS_IMAGE_ALREADY_LOADED
;
690 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
695 /***********************************************************************
696 * free_ranges_lower_bound
698 * Returns the first range whose end is not less than addr, or end if there's none.
700 static struct range_entry
*free_ranges_lower_bound( void *addr
)
702 struct range_entry
*begin
= free_ranges
;
703 struct range_entry
*end
= free_ranges_end
;
704 struct range_entry
*mid
;
708 mid
= begin
+ (end
- begin
) / 2;
718 static void dump_free_ranges(void)
720 struct range_entry
*r
;
721 for (r
= free_ranges
; r
!= free_ranges_end
; ++r
)
722 TRACE_(virtual_ranges
)("%p - %p.\n", r
->base
, r
->end
);
725 /***********************************************************************
726 * free_ranges_insert_view
728 * Updates the free_ranges after a new view has been created.
730 static void free_ranges_insert_view( struct file_view
*view
)
732 void *view_base
= ROUND_ADDR( view
->base
, granularity_mask
);
733 void *view_end
= ROUND_ADDR( (char *)view
->base
+ view
->size
+ granularity_mask
, granularity_mask
);
734 struct range_entry
*range
= free_ranges_lower_bound( view_base
);
735 struct range_entry
*next
= range
+ 1;
737 /* free_ranges initial value is such that the view is either inside range or before another one. */
738 assert( range
!= free_ranges_end
);
739 assert( range
->end
> view_base
|| next
!= free_ranges_end
);
741 /* Free ranges addresses are aligned at granularity_mask while the views may be not. */
743 if (range
->base
> view_base
)
744 view_base
= range
->base
;
745 if (range
->end
< view_end
)
746 view_end
= range
->end
;
747 if (range
->end
== view_base
&& next
->base
>= view_end
)
748 view_end
= view_base
;
750 TRACE_(virtual_ranges
)( "%p - %p, aligned %p - %p.\n",
751 view
->base
, (char *)view
->base
+ view
->size
, view_base
, view_end
);
753 if (view_end
<= view_base
)
755 VIRTUAL_DEBUG_DUMP_RANGES();
759 /* this should never happen */
760 if (range
->base
> view_base
|| range
->end
< view_end
)
761 ERR( "range %p - %p is already partially mapped\n", view_base
, view_end
);
762 assert( range
->base
<= view_base
&& range
->end
>= view_end
);
764 /* need to split the range in two */
765 if (range
->base
< view_base
&& range
->end
> view_end
)
767 memmove( next
+ 1, next
, (free_ranges_end
- next
) * sizeof(struct range_entry
) );
768 free_ranges_end
+= 1;
769 if ((char *)free_ranges_end
- (char *)free_ranges
> view_block_size
)
770 ERR( "Free range sequence is full, trouble ahead!\n" );
771 assert( (char *)free_ranges_end
- (char *)free_ranges
<= view_block_size
);
773 next
->base
= view_end
;
774 next
->end
= range
->end
;
775 range
->end
= view_base
;
779 /* otherwise we just have to shrink it */
780 if (range
->base
< view_base
)
781 range
->end
= view_base
;
783 range
->base
= view_end
;
785 if (range
->base
< range
->end
)
787 VIRTUAL_DEBUG_DUMP_RANGES();
790 /* and possibly remove it if it's now empty */
791 memmove( range
, next
, (free_ranges_end
- next
) * sizeof(struct range_entry
) );
792 free_ranges_end
-= 1;
793 assert( free_ranges_end
- free_ranges
> 0 );
795 VIRTUAL_DEBUG_DUMP_RANGES();
798 /***********************************************************************
799 * free_ranges_remove_view
801 * Updates the free_ranges after a view has been destroyed.
803 static void free_ranges_remove_view( struct file_view
*view
)
805 void *view_base
= ROUND_ADDR( view
->base
, granularity_mask
);
806 void *view_end
= ROUND_ADDR( (char *)view
->base
+ view
->size
+ granularity_mask
, granularity_mask
);
807 struct range_entry
*range
= free_ranges_lower_bound( view_base
);
808 struct range_entry
*next
= range
+ 1;
810 /* Free ranges addresses are aligned at granularity_mask while the views may be not. */
811 struct file_view
*prev_view
= RB_ENTRY_VALUE( rb_prev( &view
->entry
), struct file_view
, entry
);
812 struct file_view
*next_view
= RB_ENTRY_VALUE( rb_next( &view
->entry
), struct file_view
, entry
);
813 void *prev_view_base
= prev_view
? ROUND_ADDR( prev_view
->base
, granularity_mask
) : NULL
;
814 void *prev_view_end
= prev_view
? ROUND_ADDR( (char *)prev_view
->base
+ prev_view
->size
+ granularity_mask
, granularity_mask
) : NULL
;
815 void *next_view_base
= next_view
? ROUND_ADDR( next_view
->base
, granularity_mask
) : NULL
;
816 void *next_view_end
= next_view
? ROUND_ADDR( (char *)next_view
->base
+ next_view
->size
+ granularity_mask
, granularity_mask
) : NULL
;
818 if (prev_view_end
&& prev_view_end
> view_base
&& prev_view_base
< view_end
)
819 view_base
= prev_view_end
;
820 if (next_view_base
&& next_view_base
< view_end
&& next_view_end
> view_base
)
821 view_end
= next_view_base
;
823 TRACE_(virtual_ranges
)( "%p - %p, aligned %p - %p.\n",
824 view
->base
, (char *)view
->base
+ view
->size
, view_base
, view_end
);
826 if (view_end
<= view_base
)
828 VIRTUAL_DEBUG_DUMP_RANGES();
831 /* free_ranges initial value is such that the view is either inside range or before another one. */
832 assert( range
!= free_ranges_end
);
833 assert( range
->end
> view_base
|| next
!= free_ranges_end
);
835 /* this should never happen, but we can safely ignore it */
836 if (range
->base
<= view_base
&& range
->end
>= view_end
)
838 WARN( "range %p - %p is already unmapped\n", view_base
, view_end
);
842 /* this should never happen */
843 if (range
->base
< view_end
&& range
->end
> view_base
)
844 ERR( "range %p - %p is already partially unmapped\n", view_base
, view_end
);
845 assert( range
->end
<= view_base
|| range
->base
>= view_end
);
847 /* merge with next if possible */
848 if (range
->end
== view_base
&& next
->base
== view_end
)
850 range
->end
= next
->end
;
851 memmove( next
, next
+ 1, (free_ranges_end
- next
- 1) * sizeof(struct range_entry
) );
852 free_ranges_end
-= 1;
853 assert( free_ranges_end
- free_ranges
> 0 );
855 /* or try growing the range */
856 else if (range
->end
== view_base
)
857 range
->end
= view_end
;
858 else if (range
->base
== view_end
)
859 range
->base
= view_base
;
860 /* otherwise create a new one */
863 memmove( range
+ 1, range
, (free_ranges_end
- range
) * sizeof(struct range_entry
) );
864 free_ranges_end
+= 1;
865 if ((char *)free_ranges_end
- (char *)free_ranges
> view_block_size
)
866 ERR( "Free range sequence is full, trouble ahead!\n" );
867 assert( (char *)free_ranges_end
- (char *)free_ranges
<= view_block_size
);
869 range
->base
= view_base
;
870 range
->end
= view_end
;
872 VIRTUAL_DEBUG_DUMP_RANGES();
876 static inline int is_view_valloc( const struct file_view
*view
)
878 return !(view
->protect
& (SEC_FILE
| SEC_RESERVE
| SEC_COMMIT
));
881 /***********************************************************************
884 * Return the page protection byte.
886 static BYTE
get_page_vprot( const void *addr
)
888 size_t idx
= (size_t)addr
>> page_shift
;
891 if ((idx
>> pages_vprot_shift
) >= pages_vprot_size
) return 0;
892 if (!pages_vprot
[idx
>> pages_vprot_shift
]) return 0;
893 return pages_vprot
[idx
>> pages_vprot_shift
][idx
& pages_vprot_mask
];
895 return pages_vprot
[idx
];
900 /***********************************************************************
901 * get_vprot_range_size
903 * Return the size of the region with equal masked vprot byte.
904 * Also return the protections for the first page.
905 * The function assumes that base and size are page aligned,
906 * base + size does not wrap around and the range is within view so
907 * vprot bytes are allocated for the range. */
908 static SIZE_T
get_vprot_range_size( char *base
, SIZE_T size
, BYTE mask
, BYTE
*vprot
)
910 static const UINT_PTR word_from_byte
= (UINT_PTR
)0x101010101010101;
911 static const UINT_PTR index_align_mask
= sizeof(UINT_PTR
) - 1;
912 SIZE_T curr_idx
, start_idx
, end_idx
, aligned_start_idx
;
913 UINT_PTR vprot_word
, mask_word
;
914 const BYTE
*vprot_ptr
;
916 TRACE("base %p, size %p, mask %#x.\n", base
, (void *)size
, mask
);
918 curr_idx
= start_idx
= (size_t)base
>> page_shift
;
919 end_idx
= start_idx
+ (size
>> page_shift
);
921 aligned_start_idx
= (start_idx
+ index_align_mask
) & ~index_align_mask
;
922 if (aligned_start_idx
> end_idx
) aligned_start_idx
= end_idx
;
925 vprot_ptr
= pages_vprot
[curr_idx
>> pages_vprot_shift
] + (curr_idx
& pages_vprot_mask
);
927 vprot_ptr
= pages_vprot
+ curr_idx
;
931 /* Page count page table is at least the multiples of sizeof(UINT_PTR)
932 * so we don't have to worry about crossing the boundary on unaligned idx values. */
934 for (; curr_idx
< aligned_start_idx
; ++curr_idx
, ++vprot_ptr
)
935 if ((*vprot
^ *vprot_ptr
) & mask
) return (curr_idx
- start_idx
) << page_shift
;
937 vprot_word
= word_from_byte
* *vprot
;
938 mask_word
= word_from_byte
* mask
;
939 for (; curr_idx
< end_idx
; curr_idx
+= sizeof(UINT_PTR
), vprot_ptr
+= sizeof(UINT_PTR
))
942 if (!(curr_idx
& pages_vprot_mask
)) vprot_ptr
= pages_vprot
[curr_idx
>> pages_vprot_shift
];
944 if ((vprot_word
^ *(UINT_PTR
*)vprot_ptr
) & mask_word
)
946 for (; curr_idx
< end_idx
; ++curr_idx
, ++vprot_ptr
)
947 if ((*vprot
^ *vprot_ptr
) & mask
) break;
948 return (curr_idx
- start_idx
) << page_shift
;
954 /***********************************************************************
957 * Set a range of page protection bytes.
959 static void set_page_vprot( const void *addr
, size_t size
, BYTE vprot
)
961 size_t idx
= (size_t)addr
>> page_shift
;
962 size_t end
= ((size_t)addr
+ size
+ page_mask
) >> page_shift
;
965 while (idx
>> pages_vprot_shift
!= end
>> pages_vprot_shift
)
967 size_t dir_size
= pages_vprot_mask
+ 1 - (idx
& pages_vprot_mask
);
968 memset( pages_vprot
[idx
>> pages_vprot_shift
] + (idx
& pages_vprot_mask
), vprot
, dir_size
);
971 memset( pages_vprot
[idx
>> pages_vprot_shift
] + (idx
& pages_vprot_mask
), vprot
, end
- idx
);
973 memset( pages_vprot
+ idx
, vprot
, end
- idx
);
978 /***********************************************************************
979 * set_page_vprot_bits
981 * Set or clear bits in a range of page protection bytes.
983 static void set_page_vprot_bits( const void *addr
, size_t size
, BYTE set
, BYTE clear
)
985 size_t idx
= (size_t)addr
>> page_shift
;
986 size_t end
= ((size_t)addr
+ size
+ page_mask
) >> page_shift
;
989 for ( ; idx
< end
; idx
++)
991 BYTE
*ptr
= pages_vprot
[idx
>> pages_vprot_shift
] + (idx
& pages_vprot_mask
);
992 *ptr
= (*ptr
& ~clear
) | set
;
995 for ( ; idx
< end
; idx
++) pages_vprot
[idx
] = (pages_vprot
[idx
] & ~clear
) | set
;
1000 /***********************************************************************
1003 * Allocate the page protection bytes for a given range.
1005 static BOOL
alloc_pages_vprot( const void *addr
, size_t size
)
1008 size_t idx
= (size_t)addr
>> page_shift
;
1009 size_t end
= ((size_t)addr
+ size
+ page_mask
) >> page_shift
;
1013 assert( end
<= pages_vprot_size
<< pages_vprot_shift
);
1014 for (i
= idx
>> pages_vprot_shift
; i
< (end
+ pages_vprot_mask
) >> pages_vprot_shift
; i
++)
1016 if (pages_vprot
[i
]) continue;
1017 if ((ptr
= anon_mmap_alloc( pages_vprot_mask
+ 1, PROT_READ
| PROT_WRITE
)) == MAP_FAILED
)
1019 pages_vprot
[i
] = ptr
;
1026 static inline UINT64
maskbits( size_t idx
)
1028 return ~(UINT64
)0 << (idx
& 63);
1031 /***********************************************************************
1034 static void set_arm64ec_range( const void *addr
, size_t size
)
1036 size_t idx
= (size_t)addr
>> page_shift
;
1037 size_t end
= ((size_t)addr
+ size
+ page_mask
) >> page_shift
;
1038 size_t pos
= idx
/ 64;
1039 size_t end_pos
= end
/ 64;
1043 arm64ec_map
[pos
++] |= maskbits( idx
);
1044 while (pos
< end_pos
) arm64ec_map
[pos
++] = ~(UINT64
)0;
1045 if (end
& 63) arm64ec_map
[pos
] |= ~maskbits( end
);
1047 else arm64ec_map
[pos
] |= maskbits( idx
) & ~maskbits( end
);
1051 /***********************************************************************
1052 * clear_arm64ec_range
1054 static void clear_arm64ec_range( const void *addr
, size_t size
)
1056 size_t idx
= (size_t)addr
>> page_shift
;
1057 size_t end
= ((size_t)addr
+ size
+ page_mask
) >> page_shift
;
1058 size_t pos
= idx
/ 64;
1059 size_t end_pos
= end
/ 64;
1063 arm64ec_map
[pos
++] &= ~maskbits( idx
);
1064 while (pos
< end_pos
) arm64ec_map
[pos
++] = 0;
1065 if (end
& 63) arm64ec_map
[pos
] &= maskbits( end
);
1067 else arm64ec_map
[pos
] &= ~maskbits( idx
) | maskbits( end
);
1071 /***********************************************************************
1074 * View comparison function used for the rb tree.
1076 static int compare_view( const void *addr
, const struct wine_rb_entry
*entry
)
1078 struct file_view
*view
= WINE_RB_ENTRY_VALUE( entry
, struct file_view
, entry
);
1080 if (addr
< view
->base
) return -1;
1081 if (addr
> view
->base
) return 1;
1086 /***********************************************************************
1089 static const char *get_prot_str( BYTE prot
)
1091 static char buffer
[6];
1092 buffer
[0] = (prot
& VPROT_COMMITTED
) ? 'c' : '-';
1093 buffer
[1] = (prot
& VPROT_GUARD
) ? 'g' : ((prot
& VPROT_WRITEWATCH
) ? 'H' : '-');
1094 buffer
[2] = (prot
& VPROT_READ
) ? 'r' : '-';
1095 buffer
[3] = (prot
& VPROT_WRITECOPY
) ? 'W' : ((prot
& VPROT_WRITE
) ? 'w' : '-');
1096 buffer
[4] = (prot
& VPROT_EXEC
) ? 'x' : '-';
1102 /***********************************************************************
1105 * Convert page protections to protection for mmap/mprotect.
1107 static int get_unix_prot( BYTE vprot
)
1110 if ((vprot
& VPROT_COMMITTED
) && !(vprot
& VPROT_GUARD
))
1112 if (vprot
& VPROT_READ
) prot
|= PROT_READ
;
1113 if (vprot
& VPROT_WRITE
) prot
|= PROT_WRITE
| PROT_READ
;
1114 if (vprot
& VPROT_WRITECOPY
) prot
|= PROT_WRITE
| PROT_READ
;
1115 if (vprot
& VPROT_EXEC
) prot
|= PROT_EXEC
| PROT_READ
;
1116 if (vprot
& VPROT_WRITEWATCH
) prot
&= ~PROT_WRITE
;
1118 if (!prot
) prot
= PROT_NONE
;
1123 /***********************************************************************
1126 static void dump_view( struct file_view
*view
)
1129 char *addr
= view
->base
;
1130 BYTE prot
= get_page_vprot( addr
);
1132 TRACE( "View: %p - %p", addr
, addr
+ view
->size
- 1 );
1133 if (view
->protect
& VPROT_SYSTEM
)
1134 TRACE( " (builtin image)\n" );
1135 else if (view
->protect
& VPROT_FREE_PLACEHOLDER
)
1136 TRACE( " (placeholder)\n" );
1137 else if (view
->protect
& SEC_IMAGE
)
1138 TRACE( " (image)\n" );
1139 else if (view
->protect
& SEC_FILE
)
1140 TRACE( " (file)\n" );
1141 else if (view
->protect
& (SEC_RESERVE
| SEC_COMMIT
))
1142 TRACE( " (anonymous)\n" );
1144 TRACE( " (valloc)\n");
1146 for (count
= i
= 1; i
< view
->size
>> page_shift
; i
++, count
++)
1148 BYTE next
= get_page_vprot( addr
+ (count
<< page_shift
) );
1149 if (next
== prot
) continue;
1150 TRACE( " %p - %p %s\n",
1151 addr
, addr
+ (count
<< page_shift
) - 1, get_prot_str(prot
) );
1152 addr
+= (count
<< page_shift
);
1157 TRACE( " %p - %p %s\n",
1158 addr
, addr
+ (count
<< page_shift
) - 1, get_prot_str(prot
) );
1162 /***********************************************************************
1165 #ifdef WINE_VM_DEBUG
1166 static void VIRTUAL_Dump(void)
1169 struct file_view
*view
;
1171 TRACE( "Dump of all virtual memory views:\n" );
1172 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
1173 WINE_RB_FOR_EACH_ENTRY( view
, &views_tree
, struct file_view
, entry
)
1177 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
1182 /***********************************************************************
1185 * Find the view containing a given address. virtual_mutex must be held by caller.
1194 static struct file_view
*find_view( const void *addr
, size_t size
)
1196 struct wine_rb_entry
*ptr
= views_tree
.root
;
1198 if ((const char *)addr
+ size
< (const char *)addr
) return NULL
; /* overflow */
1202 struct file_view
*view
= WINE_RB_ENTRY_VALUE( ptr
, struct file_view
, entry
);
1204 if (view
->base
> addr
) ptr
= ptr
->left
;
1205 else if ((const char *)view
->base
+ view
->size
<= (const char *)addr
) ptr
= ptr
->right
;
1206 else if ((const char *)view
->base
+ view
->size
< (const char *)addr
+ size
) break; /* size too large */
1213 /***********************************************************************
1214 * is_write_watch_range
1216 static inline BOOL
is_write_watch_range( const void *addr
, size_t size
)
1218 struct file_view
*view
= find_view( addr
, size
);
1219 return view
&& (view
->protect
& VPROT_WRITEWATCH
);
1223 /***********************************************************************
1226 * Find the first view overlapping at least part of the specified range.
1227 * virtual_mutex must be held by caller.
1229 static struct file_view
*find_view_range( const void *addr
, size_t size
)
1231 struct wine_rb_entry
*ptr
= views_tree
.root
;
1235 struct file_view
*view
= WINE_RB_ENTRY_VALUE( ptr
, struct file_view
, entry
);
1237 if ((const char *)view
->base
>= (const char *)addr
+ size
) ptr
= ptr
->left
;
1238 else if ((const char *)view
->base
+ view
->size
<= (const char *)addr
) ptr
= ptr
->right
;
1245 /***********************************************************************
1246 * find_view_inside_range
1248 * Find first (resp. last, if top_down) view inside a range.
1249 * virtual_mutex must be held by caller.
1251 static struct wine_rb_entry
*find_view_inside_range( void **base_ptr
, void **end_ptr
, int top_down
)
1253 struct wine_rb_entry
*first
= NULL
, *ptr
= views_tree
.root
;
1254 void *base
= *base_ptr
, *end
= *end_ptr
;
1256 /* find the first (resp. last) view inside the range */
1259 struct file_view
*view
= WINE_RB_ENTRY_VALUE( ptr
, struct file_view
, entry
);
1260 if ((char *)view
->base
+ view
->size
>= (char *)end
)
1262 end
= min( end
, view
->base
);
1265 else if (view
->base
<= base
)
1267 base
= max( (char *)base
, (char *)view
->base
+ view
->size
);
1273 ptr
= top_down
? ptr
->right
: ptr
->left
;
1283 /***********************************************************************
1286 * Try mmaping some expected free memory region, eventually stepping and
1287 * retrying inside it, and return where it actually succeeded, or NULL.
1289 static void* try_map_free_area( void *base
, void *end
, ptrdiff_t step
,
1290 void *start
, size_t size
, int unix_prot
)
1294 while (start
&& base
<= start
&& (char*)start
+ size
<= (char*)end
)
1296 if ((ptr
= anon_mmap_tryfixed( start
, size
, unix_prot
, 0 )) != MAP_FAILED
) return start
;
1297 TRACE( "Found free area is already mapped, start %p.\n", start
);
1298 if (errno
!= EEXIST
)
1300 ERR( "mmap() error %s, range %p-%p, unix_prot %#x.\n",
1301 strerror(errno
), start
, (char *)start
+ size
, unix_prot
);
1304 if ((step
> 0 && (char *)end
- (char *)start
< step
) ||
1305 (step
< 0 && (char *)start
- (char *)base
< -step
) ||
1308 start
= (char *)start
+ step
;
1315 /***********************************************************************
1318 * Find a free area between views inside the specified range and map it.
1319 * virtual_mutex must be held by caller.
1321 static void *map_free_area( void *base
, void *end
, size_t size
, int top_down
, int unix_prot
, size_t align_mask
)
1323 struct wine_rb_entry
*first
= find_view_inside_range( &base
, &end
, top_down
);
1324 ptrdiff_t step
= top_down
? -(align_mask
+ 1) : (align_mask
+ 1);
1329 start
= ROUND_ADDR( (char *)end
- size
, align_mask
);
1330 if (start
>= end
|| start
< base
) return NULL
;
1334 struct file_view
*view
= WINE_RB_ENTRY_VALUE( first
, struct file_view
, entry
);
1335 if ((start
= try_map_free_area( (char *)view
->base
+ view
->size
, (char *)start
+ size
, step
,
1336 start
, size
, unix_prot
))) break;
1337 start
= ROUND_ADDR( (char *)view
->base
- size
, align_mask
);
1338 /* stop if remaining space is not large enough */
1339 if (!start
|| start
>= end
|| start
< base
) return NULL
;
1340 first
= rb_prev( first
);
1345 start
= ROUND_ADDR( (char *)base
+ align_mask
, align_mask
);
1346 if (!start
|| start
>= end
|| (char *)end
- (char *)start
< size
) return NULL
;
1350 struct file_view
*view
= WINE_RB_ENTRY_VALUE( first
, struct file_view
, entry
);
1351 if ((start
= try_map_free_area( start
, view
->base
, step
,
1352 start
, size
, unix_prot
))) break;
1353 start
= ROUND_ADDR( (char *)view
->base
+ view
->size
+ align_mask
, align_mask
);
1354 /* stop if remaining space is not large enough */
1355 if (!start
|| start
>= end
|| (char *)end
- (char *)start
< size
) return NULL
;
1356 first
= rb_next( first
);
1361 return try_map_free_area( base
, end
, step
, start
, size
, unix_prot
);
1367 /***********************************************************************
1368 * find_reserved_free_area
1370 * Find a free area between views inside the specified range.
1371 * virtual_mutex must be held by caller.
1372 * The range must be inside the preloader reserved range.
1374 static void *find_reserved_free_area( void *base
, void *end
, size_t size
, int top_down
, size_t align_mask
)
1376 struct range_entry
*range
;
1379 base
= ROUND_ADDR( (char *)base
+ align_mask
, align_mask
);
1380 end
= (char *)ROUND_ADDR( (char *)end
- size
, align_mask
) + size
;
1384 start
= (char *)end
- size
;
1385 range
= free_ranges_lower_bound( start
);
1386 assert(range
!= free_ranges_end
&& range
->end
>= start
);
1388 if ((char *)range
->end
- (char *)start
< size
) start
= ROUND_ADDR( (char *)range
->end
- size
, align_mask
);
1391 if (start
>= end
|| start
< base
|| (char *)end
- (char *)start
< size
) return NULL
;
1392 if (start
< range
->end
&& start
>= range
->base
&& (char *)range
->end
- (char *)start
>= size
) break;
1393 if (--range
< free_ranges
) return NULL
;
1394 start
= ROUND_ADDR( (char *)range
->end
- size
, align_mask
);
1401 range
= free_ranges_lower_bound( start
);
1402 assert(range
!= free_ranges_end
&& range
->end
>= start
);
1404 if (start
< range
->base
) start
= ROUND_ADDR( (char *)range
->base
+ align_mask
, align_mask
);
1407 if (start
>= end
|| start
< base
|| (char *)end
- (char *)start
< size
) return NULL
;
1408 if (start
< range
->end
&& start
>= range
->base
&& (char *)range
->end
- (char *)start
>= size
) break;
1409 if (++range
== free_ranges_end
) return NULL
;
1410 start
= ROUND_ADDR( (char *)range
->base
+ align_mask
, align_mask
);
1418 /***********************************************************************
1421 * Add a reserved area to the list maintained by libwine.
1422 * virtual_mutex must be held by caller.
1424 static void add_reserved_area( void *addr
, size_t size
)
1426 TRACE( "adding %p-%p\n", addr
, (char *)addr
+ size
);
1428 if (addr
< user_space_limit
)
1430 /* unmap the part of the area that is below the limit */
1431 assert( (char *)addr
+ size
> (char *)user_space_limit
);
1432 munmap( addr
, (char *)user_space_limit
- (char *)addr
);
1433 size
-= (char *)user_space_limit
- (char *)addr
;
1434 addr
= user_space_limit
;
1436 /* blow away existing mappings */
1437 anon_mmap_fixed( addr
, size
, PROT_NONE
, MAP_NORESERVE
);
1438 mmap_add_reserved_area( addr
, size
);
1442 /***********************************************************************
1443 * remove_reserved_area
1445 * Remove a reserved area from the list maintained by libwine.
1446 * virtual_mutex must be held by caller.
1448 static void remove_reserved_area( void *addr
, size_t size
)
1450 struct file_view
*view
;
1452 TRACE( "removing %p-%p\n", addr
, (char *)addr
+ size
);
1453 mmap_remove_reserved_area( addr
, size
);
1455 /* unmap areas not covered by an existing view */
1456 WINE_RB_FOR_EACH_ENTRY( view
, &views_tree
, struct file_view
, entry
)
1458 if ((char *)view
->base
>= (char *)addr
+ size
) break;
1459 if ((char *)view
->base
+ view
->size
<= (char *)addr
) continue;
1460 if (view
->base
> addr
) munmap( addr
, (char *)view
->base
- (char *)addr
);
1461 if ((char *)view
->base
+ view
->size
> (char *)addr
+ size
) return;
1462 size
= (char *)addr
+ size
- ((char *)view
->base
+ view
->size
);
1463 addr
= (char *)view
->base
+ view
->size
;
1465 munmap( addr
, size
);
1469 struct area_boundary
1476 /***********************************************************************
1477 * get_area_boundary_callback
1479 * Get lowest boundary address between reserved area and non-reserved area
1480 * in the specified region. If no boundaries are found, result is NULL.
1481 * virtual_mutex must be held by caller.
1483 static int get_area_boundary_callback( void *start
, SIZE_T size
, void *arg
)
1485 struct area_boundary
*area
= arg
;
1486 void *end
= (char *)start
+ size
;
1488 area
->boundary
= NULL
;
1489 if (area
->base
>= end
) return 0;
1490 if ((char *)start
>= (char *)area
->base
+ area
->size
) return 1;
1491 if (area
->base
>= start
)
1493 if ((char *)area
->base
+ area
->size
> (char *)end
)
1495 area
->boundary
= end
;
1500 area
->boundary
= start
;
1505 /***********************************************************************
1508 * Unmap an area, or simply replace it by an empty mapping if it is
1509 * in a reserved area. virtual_mutex must be held by caller.
1511 static inline void unmap_area( void *addr
, size_t size
)
1513 switch (mmap_is_in_reserved_area( addr
, size
))
1515 case -1: /* partially in a reserved area */
1517 struct area_boundary area
;
1521 mmap_enum_reserved_areas( get_area_boundary_callback
, &area
, 0 );
1522 assert( area
.boundary
);
1523 lower_size
= (char *)area
.boundary
- (char *)addr
;
1524 unmap_area( addr
, lower_size
);
1525 unmap_area( area
.boundary
, size
- lower_size
);
1528 case 1: /* in a reserved area */
1529 anon_mmap_fixed( addr
, size
, PROT_NONE
, MAP_NORESERVE
);
1532 case 0: /* not in a reserved area */
1533 if (is_beyond_limit( addr
, size
, user_space_limit
))
1534 add_reserved_area( addr
, size
);
1536 munmap( addr
, size
);
1542 /***********************************************************************
1545 * Allocate a new view. virtual_mutex must be held by caller.
1547 static struct file_view
*alloc_view(void)
1551 struct file_view
*ret
= next_free_view
;
1552 next_free_view
= *(struct file_view
**)ret
;
1555 if (view_block_start
== view_block_end
)
1557 void *ptr
= anon_mmap_alloc( view_block_size
, PROT_READ
| PROT_WRITE
);
1558 if (ptr
== MAP_FAILED
) return NULL
;
1559 view_block_start
= ptr
;
1560 view_block_end
= view_block_start
+ view_block_size
/ sizeof(*view_block_start
);
1562 return view_block_start
++;
1566 /***********************************************************************
1569 * Free memory for view structure. virtual_mutex must be held by caller.
1571 static void free_view( struct file_view
*view
)
1573 *(struct file_view
**)view
= next_free_view
;
1574 next_free_view
= view
;
1578 /***********************************************************************
1581 * Remove view from the tree and update free ranges. virtual_mutex must be held by caller.
1583 static void unregister_view( struct file_view
*view
)
1585 if (mmap_is_in_reserved_area( view
->base
, view
->size
))
1586 free_ranges_remove_view( view
);
1587 wine_rb_remove( &views_tree
, &view
->entry
);
1591 /***********************************************************************
1594 * Deletes a view. virtual_mutex must be held by caller.
1596 static void delete_view( struct file_view
*view
) /* [in] View */
1598 if (!(view
->protect
& VPROT_SYSTEM
)) unmap_area( view
->base
, view
->size
);
1599 set_page_vprot( view
->base
, view
->size
, 0 );
1600 if (arm64ec_map
) clear_arm64ec_range( view
->base
, view
->size
);
1601 unregister_view( view
);
1606 /***********************************************************************
1609 * Add view to the tree and update free ranges. virtual_mutex must be held by caller.
1611 static void register_view( struct file_view
*view
)
1613 wine_rb_put( &views_tree
, view
->base
, &view
->entry
);
1614 if (mmap_is_in_reserved_area( view
->base
, view
->size
))
1615 free_ranges_insert_view( view
);
1619 /***********************************************************************
1622 * Create a view. virtual_mutex must be held by caller.
1624 static NTSTATUS
create_view( struct file_view
**view_ret
, void *base
, size_t size
, unsigned int vprot
)
1626 struct file_view
*view
;
1627 int unix_prot
= get_unix_prot( vprot
);
1629 assert( !((UINT_PTR
)base
& page_mask
) );
1630 assert( !(size
& page_mask
) );
1632 /* Check for overlapping views. This can happen if the previous view
1633 * was a system view that got unmapped behind our back. In that case
1634 * we recover by simply deleting it. */
1636 while ((view
= find_view_range( base
, size
)))
1638 TRACE( "overlapping view %p-%p for %p-%p\n",
1639 view
->base
, (char *)view
->base
+ view
->size
, base
, (char *)base
+ size
);
1640 assert( view
->protect
& VPROT_SYSTEM
);
1641 delete_view( view
);
1644 if (!alloc_pages_vprot( base
, size
)) return STATUS_NO_MEMORY
;
1646 /* Create the view structure */
1648 if (!(view
= alloc_view()))
1650 FIXME( "out of memory for %p-%p\n", base
, (char *)base
+ size
);
1651 return STATUS_NO_MEMORY
;
1656 view
->protect
= vprot
;
1657 set_page_vprot( base
, size
, vprot
);
1659 register_view( view
);
1663 if (force_exec_prot
&& (unix_prot
& PROT_READ
) && !(unix_prot
& PROT_EXEC
))
1665 TRACE( "forcing exec permission on %p-%p\n", base
, (char *)base
+ size
- 1 );
1666 mprotect( base
, size
, unix_prot
| PROT_EXEC
);
1668 return STATUS_SUCCESS
;
1672 /***********************************************************************
1675 * Convert page protections to Win32 flags.
1677 static DWORD
get_win32_prot( BYTE vprot
, unsigned int map_prot
)
1679 DWORD ret
= VIRTUAL_Win32Flags
[vprot
& 0x0f];
1680 if (vprot
& VPROT_GUARD
) ret
|= PAGE_GUARD
;
1681 if (map_prot
& SEC_NOCACHE
) ret
|= PAGE_NOCACHE
;
1686 /***********************************************************************
1689 * Build page protections from Win32 flags.
1691 static NTSTATUS
get_vprot_flags( DWORD protect
, unsigned int *vprot
, BOOL image
)
1693 switch(protect
& 0xff)
1696 *vprot
= VPROT_READ
;
1698 case PAGE_READWRITE
:
1700 *vprot
= VPROT_READ
| VPROT_WRITECOPY
;
1702 *vprot
= VPROT_READ
| VPROT_WRITE
;
1704 case PAGE_WRITECOPY
:
1705 *vprot
= VPROT_READ
| VPROT_WRITECOPY
;
1708 *vprot
= VPROT_EXEC
;
1710 case PAGE_EXECUTE_READ
:
1711 *vprot
= VPROT_EXEC
| VPROT_READ
;
1713 case PAGE_EXECUTE_READWRITE
:
1715 *vprot
= VPROT_EXEC
| VPROT_READ
| VPROT_WRITECOPY
;
1717 *vprot
= VPROT_EXEC
| VPROT_READ
| VPROT_WRITE
;
1719 case PAGE_EXECUTE_WRITECOPY
:
1720 *vprot
= VPROT_EXEC
| VPROT_READ
| VPROT_WRITECOPY
;
1726 return STATUS_INVALID_PAGE_PROTECTION
;
1728 if (protect
& PAGE_GUARD
) *vprot
|= VPROT_GUARD
;
1729 return STATUS_SUCCESS
;
1733 /***********************************************************************
1736 * Wrapper for mprotect, adds PROT_EXEC if forced by force_exec_prot
1738 static inline int mprotect_exec( void *base
, size_t size
, int unix_prot
)
1740 if (force_exec_prot
&& (unix_prot
& PROT_READ
) && !(unix_prot
& PROT_EXEC
))
1742 TRACE( "forcing exec permission on %p-%p\n", base
, (char *)base
+ size
- 1 );
1743 if (!mprotect( base
, size
, unix_prot
| PROT_EXEC
)) return 0;
1744 /* exec + write may legitimately fail, in that case fall back to write only */
1745 if (!(unix_prot
& PROT_WRITE
)) return -1;
1748 return mprotect( base
, size
, unix_prot
);
1752 /***********************************************************************
1755 * Call mprotect on a page range, applying the protections from the per-page byte.
1757 static void mprotect_range( void *base
, size_t size
, BYTE set
, BYTE clear
)
1760 char *addr
= ROUND_ADDR( base
, page_mask
);
1763 size
= ROUND_SIZE( base
, size
);
1764 prot
= get_unix_prot( (get_page_vprot( addr
) & ~clear
) | set
);
1765 for (count
= i
= 1; i
< size
>> page_shift
; i
++, count
++)
1767 next
= get_unix_prot( (get_page_vprot( addr
+ (count
<< page_shift
) ) & ~clear
) | set
);
1768 if (next
== prot
) continue;
1769 mprotect_exec( addr
, count
<< page_shift
, prot
);
1770 addr
+= count
<< page_shift
;
1774 if (count
) mprotect_exec( addr
, count
<< page_shift
, prot
);
1778 /***********************************************************************
1781 * Change the protection of a range of pages.
1783 static BOOL
set_vprot( struct file_view
*view
, void *base
, size_t size
, BYTE vprot
)
1785 int unix_prot
= get_unix_prot(vprot
);
1787 if (view
->protect
& VPROT_WRITEWATCH
)
1789 /* each page may need different protections depending on write watch flag */
1790 set_page_vprot_bits( base
, size
, vprot
& ~VPROT_WRITEWATCH
, ~vprot
& ~VPROT_WRITEWATCH
);
1791 mprotect_range( base
, size
, 0, 0 );
1794 if (mprotect_exec( base
, size
, unix_prot
)) return FALSE
;
1795 set_page_vprot( base
, size
, vprot
);
1800 /***********************************************************************
1803 * Set page protections on a range of pages
1805 static NTSTATUS
set_protection( struct file_view
*view
, void *base
, SIZE_T size
, ULONG protect
)
1810 if ((status
= get_vprot_flags( protect
, &vprot
, view
->protect
& SEC_IMAGE
))) return status
;
1811 if (is_view_valloc( view
))
1813 if (vprot
& VPROT_WRITECOPY
) return STATUS_INVALID_PAGE_PROTECTION
;
1817 BYTE access
= vprot
& (VPROT_READ
| VPROT_WRITE
| VPROT_EXEC
);
1818 if ((view
->protect
& access
) != access
) return STATUS_INVALID_PAGE_PROTECTION
;
1821 if (!set_vprot( view
, base
, size
, vprot
| VPROT_COMMITTED
)) return STATUS_ACCESS_DENIED
;
1822 return STATUS_SUCCESS
;
1826 /***********************************************************************
1827 * update_write_watches
1829 static void update_write_watches( void *base
, size_t size
, size_t accessed_size
)
1831 TRACE( "updating watch %p-%p-%p\n", base
, (char *)base
+ accessed_size
, (char *)base
+ size
);
1832 /* clear write watch flag on accessed pages */
1833 set_page_vprot_bits( base
, accessed_size
, 0, VPROT_WRITEWATCH
);
1834 /* restore page protections on the entire range */
1835 mprotect_range( base
, size
, 0, 0 );
1839 /***********************************************************************
1840 * reset_write_watches
1842 * Reset write watches in a memory range.
1844 static void reset_write_watches( void *base
, SIZE_T size
)
1846 set_page_vprot_bits( base
, size
, VPROT_WRITEWATCH
, 0 );
1847 mprotect_range( base
, size
, 0, 0 );
1851 /***********************************************************************
1854 * Release the extra memory while keeping the range starting on the alignment boundary.
1856 static inline void *unmap_extra_space( void *ptr
, size_t total_size
, size_t wanted_size
, size_t align_mask
)
1858 if ((ULONG_PTR
)ptr
& align_mask
)
1860 size_t extra
= align_mask
+ 1 - ((ULONG_PTR
)ptr
& align_mask
);
1861 munmap( ptr
, extra
);
1862 ptr
= (char *)ptr
+ extra
;
1863 total_size
-= extra
;
1865 if (total_size
> wanted_size
)
1866 munmap( (char *)ptr
+ wanted_size
, total_size
- wanted_size
);
1880 /***********************************************************************
1881 * alloc_reserved_area_callback
1883 * Try to map some space inside a reserved area. Callback for mmap_enum_reserved_areas.
1885 static int alloc_reserved_area_callback( void *start
, SIZE_T size
, void *arg
)
1887 struct alloc_area
*alloc
= arg
;
1888 void *end
= (char *)start
+ size
;
1890 if (start
< address_space_start
) start
= address_space_start
;
1891 if (is_beyond_limit( start
, size
, alloc
->limit
)) end
= alloc
->limit
;
1892 if (start
>= end
) return 0;
1894 /* make sure we don't touch the preloader reserved range */
1895 if (preload_reserve_end
>= start
)
1897 if (preload_reserve_end
>= end
)
1899 if (preload_reserve_start
<= start
) return 0; /* no space in that area */
1900 if (preload_reserve_start
< end
) end
= preload_reserve_start
;
1902 else if (preload_reserve_start
<= start
) start
= preload_reserve_end
;
1905 /* range is split in two by the preloader reservation, try first part */
1906 if ((alloc
->result
= find_reserved_free_area( start
, preload_reserve_start
, alloc
->size
,
1907 alloc
->top_down
, alloc
->align_mask
)))
1909 /* then fall through to try second part */
1910 start
= preload_reserve_end
;
1913 if ((alloc
->result
= find_reserved_free_area( start
, end
, alloc
->size
, alloc
->top_down
, alloc
->align_mask
)))
1919 /***********************************************************************
1922 * mmap the fixed memory area.
1923 * virtual_mutex must be held by caller.
1925 static NTSTATUS
map_fixed_area( void *base
, size_t size
, unsigned int vprot
)
1929 switch (mmap_is_in_reserved_area( base
, size
))
1931 case -1: /* partially in a reserved area */
1934 struct area_boundary area
;
1938 mmap_enum_reserved_areas( get_area_boundary_callback
, &area
, 0 );
1939 assert( area
.boundary
);
1940 lower_size
= (char *)area
.boundary
- (char *)base
;
1941 status
= map_fixed_area( base
, lower_size
, vprot
);
1942 if (status
== STATUS_SUCCESS
)
1944 status
= map_fixed_area( area
.boundary
, size
- lower_size
, vprot
);
1945 if (status
!= STATUS_SUCCESS
) unmap_area( base
, lower_size
);
1949 case 0: /* not in a reserved area, do a normal allocation */
1950 if ((ptr
= anon_mmap_tryfixed( base
, size
, get_unix_prot(vprot
), 0 )) == MAP_FAILED
)
1952 if (errno
== ENOMEM
) return STATUS_NO_MEMORY
;
1953 if (errno
== EEXIST
) return STATUS_CONFLICTING_ADDRESSES
;
1954 return STATUS_INVALID_PARAMETER
;
1959 case 1: /* in a reserved area, make sure the address is available */
1960 if (find_view_range( base
, size
)) return STATUS_CONFLICTING_ADDRESSES
;
1961 /* replace the reserved area by our mapping */
1962 if ((ptr
= anon_mmap_fixed( base
, size
, get_unix_prot(vprot
), 0 )) != base
)
1963 return STATUS_INVALID_PARAMETER
;
1966 if (is_beyond_limit( ptr
, size
, working_set_limit
)) working_set_limit
= address_space_limit
;
1967 return STATUS_SUCCESS
;
1970 /***********************************************************************
1973 * Create a view and mmap the corresponding memory area.
1974 * virtual_mutex must be held by caller.
1976 static NTSTATUS
map_view( struct file_view
**view_ret
, void *base
, size_t size
,
1977 unsigned int alloc_type
, unsigned int vprot
, ULONG_PTR limit
, size_t align_mask
)
1979 int top_down
= alloc_type
& MEM_TOP_DOWN
;
1983 if (alloc_type
& MEM_REPLACE_PLACEHOLDER
)
1985 struct file_view
*view
;
1987 if (!(view
= find_view( base
, 0 ))) return STATUS_INVALID_PARAMETER
;
1988 if (view
->base
!= base
|| view
->size
!= size
) return STATUS_CONFLICTING_ADDRESSES
;
1989 if (!(view
->protect
& VPROT_FREE_PLACEHOLDER
)) return STATUS_INVALID_PARAMETER
;
1991 TRACE( "found view %p, size %p, protect %#x.\n", view
->base
, (void *)view
->size
, view
->protect
);
1993 view
->protect
= vprot
| VPROT_PLACEHOLDER
;
1994 set_vprot( view
, base
, size
, vprot
);
1995 if (vprot
& VPROT_WRITEWATCH
) reset_write_watches( base
, size
);
1997 return STATUS_SUCCESS
;
2002 if (is_beyond_limit( base
, size
, address_space_limit
))
2003 return STATUS_WORKING_SET_LIMIT_RANGE
;
2004 if (limit
&& is_beyond_limit( base
, size
, (void *)limit
))
2005 return STATUS_CONFLICTING_ADDRESSES
;
2006 status
= map_fixed_area( base
, size
, vprot
);
2007 if (status
!= STATUS_SUCCESS
) return status
;
2012 struct alloc_area alloc
;
2015 if (!align_mask
) align_mask
= granularity_mask
;
2016 view_size
= size
+ align_mask
+ 1;
2019 alloc
.top_down
= top_down
;
2020 alloc
.limit
= limit
? min( (void *)(limit
+ 1), user_space_limit
) : user_space_limit
;
2021 alloc
.align_mask
= align_mask
;
2023 if (mmap_enum_reserved_areas( alloc_reserved_area_callback
, &alloc
, top_down
))
2026 TRACE( "got mem in reserved area %p-%p\n", ptr
, (char *)ptr
+ size
);
2027 if (anon_mmap_fixed( ptr
, size
, get_unix_prot(vprot
), 0 ) != ptr
)
2028 return STATUS_INVALID_PARAMETER
;
2034 if (!(ptr
= map_free_area( address_space_start
, alloc
.limit
, size
,
2035 top_down
, get_unix_prot(vprot
), align_mask
)))
2036 return STATUS_NO_MEMORY
;
2037 TRACE( "got mem with map_free_area %p-%p\n", ptr
, (char *)ptr
+ size
);
2043 if ((ptr
= anon_mmap_alloc( view_size
, get_unix_prot(vprot
) )) == MAP_FAILED
)
2045 if (errno
== ENOMEM
) return STATUS_NO_MEMORY
;
2046 return STATUS_INVALID_PARAMETER
;
2048 TRACE( "got mem with anon mmap %p-%p\n", ptr
, (char *)ptr
+ size
);
2049 /* if we got something beyond the user limit, unmap it and retry */
2050 if (is_beyond_limit( ptr
, view_size
, user_space_limit
)) add_reserved_area( ptr
, view_size
);
2053 ptr
= unmap_extra_space( ptr
, view_size
, size
, align_mask
);
2056 status
= create_view( view_ret
, ptr
, size
, vprot
);
2057 if (status
!= STATUS_SUCCESS
) unmap_area( ptr
, size
);
2062 /***********************************************************************
2063 * map_file_into_view
2065 * Wrapper for mmap() to map a file into a view, falling back to read if mmap fails.
2066 * virtual_mutex must be held by caller.
2068 static NTSTATUS
map_file_into_view( struct file_view
*view
, int fd
, size_t start
, size_t size
,
2069 off_t offset
, unsigned int vprot
, BOOL removable
)
2072 int prot
= get_unix_prot( vprot
| VPROT_COMMITTED
/* make sure it is accessible */ );
2073 unsigned int flags
= MAP_FIXED
| ((vprot
& VPROT_WRITECOPY
) ? MAP_PRIVATE
: MAP_SHARED
);
2075 assert( start
< view
->size
);
2076 assert( start
+ size
<= view
->size
);
2078 if (force_exec_prot
&& (vprot
& VPROT_READ
))
2080 TRACE( "forcing exec permission on mapping %p-%p\n",
2081 (char *)view
->base
+ start
, (char *)view
->base
+ start
+ size
- 1 );
2085 /* only try mmap if media is not removable (or if we require write access) */
2086 if (!removable
|| (flags
& MAP_SHARED
))
2088 if (mmap( (char *)view
->base
+ start
, size
, prot
, flags
, fd
, offset
) != MAP_FAILED
)
2093 case EINVAL
: /* file offset is not page-aligned, fall back to read() */
2094 if (flags
& MAP_SHARED
) return STATUS_INVALID_PARAMETER
;
2097 case ENODEV
: /* filesystem doesn't support mmap(), fall back to read() */
2098 if (vprot
& VPROT_WRITE
)
2100 ERR( "shared writable mmap not supported, broken filesystem?\n" );
2101 return STATUS_NOT_SUPPORTED
;
2105 case EPERM
: /* noexec filesystem, fall back to read() */
2106 if (flags
& MAP_SHARED
)
2108 if (prot
& PROT_EXEC
) ERR( "failed to set PROT_EXEC on file map, noexec filesystem?\n" );
2109 return STATUS_ACCESS_DENIED
;
2111 if (prot
& PROT_EXEC
) WARN( "failed to set PROT_EXEC on file map, noexec filesystem?\n" );
2114 return STATUS_NO_MEMORY
;
2118 /* Reserve the memory with an anonymous mmap */
2119 ptr
= anon_mmap_fixed( (char *)view
->base
+ start
, size
, PROT_READ
| PROT_WRITE
, 0 );
2120 if (ptr
== MAP_FAILED
) return STATUS_NO_MEMORY
;
2121 /* Now read in the file */
2122 pread( fd
, ptr
, size
, offset
);
2123 if (prot
!= (PROT_READ
|PROT_WRITE
)) mprotect( ptr
, size
, prot
); /* Set the right protection */
2125 set_page_vprot( (char *)view
->base
+ start
, size
, vprot
);
2126 return STATUS_SUCCESS
;
2130 /***********************************************************************
2131 * get_committed_size
2133 * Get the size of the committed range with equal masked vprot bytes starting at base.
2134 * Also return the protections for the first page.
2136 static SIZE_T
get_committed_size( struct file_view
*view
, void *base
, BYTE
*vprot
, BYTE vprot_mask
)
2138 SIZE_T offset
, size
;
2140 base
= ROUND_ADDR( base
, page_mask
);
2141 offset
= (char *)base
- (char *)view
->base
;
2143 if (view
->protect
& SEC_RESERVE
)
2147 *vprot
= get_page_vprot( base
);
2149 SERVER_START_REQ( get_mapping_committed_range
)
2151 req
->base
= wine_server_client_ptr( view
->base
);
2152 req
->offset
= offset
;
2153 if (!wine_server_call( req
))
2156 if (reply
->committed
)
2158 *vprot
|= VPROT_COMMITTED
;
2159 set_page_vprot_bits( base
, size
, VPROT_COMMITTED
, 0 );
2165 if (!size
|| !(vprot_mask
& ~VPROT_COMMITTED
)) return size
;
2167 else size
= view
->size
- offset
;
2169 return get_vprot_range_size( base
, size
, vprot_mask
, vprot
);
2173 /***********************************************************************
2176 * Decommit some pages of a given view.
2177 * virtual_mutex must be held by caller.
2179 static NTSTATUS
decommit_pages( struct file_view
*view
, size_t start
, size_t size
)
2181 if (!size
) size
= view
->size
;
2182 if (anon_mmap_fixed( (char *)view
->base
+ start
, size
, PROT_NONE
, 0 ) != MAP_FAILED
)
2184 set_page_vprot_bits( (char *)view
->base
+ start
, size
, 0, VPROT_COMMITTED
);
2185 return STATUS_SUCCESS
;
2187 return STATUS_NO_MEMORY
;
2191 /***********************************************************************
2192 * remove_pages_from_view
2194 * Remove some pages of a given view.
2195 * virtual_mutex must be held by caller.
2197 static NTSTATUS
remove_pages_from_view( struct file_view
*view
, char *base
, size_t size
)
2199 assert( size
< view
->size
);
2201 if (view
->base
!= base
&& base
+ size
!= (char *)view
->base
+ view
->size
)
2203 struct file_view
*new_view
= alloc_view();
2207 ERR( "out of memory for %p-%p\n", base
, base
+ size
);
2208 return STATUS_NO_MEMORY
;
2210 new_view
->base
= base
+ size
;
2211 new_view
->size
= (char *)view
->base
+ view
->size
- (char *)new_view
->base
;
2212 new_view
->protect
= view
->protect
;
2214 unregister_view( view
);
2215 view
->size
= base
- (char *)view
->base
;
2216 register_view( view
);
2217 register_view( new_view
);
2219 VIRTUAL_DEBUG_DUMP_VIEW( view
);
2220 VIRTUAL_DEBUG_DUMP_VIEW( new_view
);
2224 unregister_view( view
);
2225 if (view
->base
== base
)
2227 view
->base
= base
+ size
;
2230 else view
->size
= base
- (char *)view
->base
;
2232 register_view( view
);
2233 VIRTUAL_DEBUG_DUMP_VIEW( view
);
2235 return STATUS_SUCCESS
;
2239 /***********************************************************************
2240 * free_pages_preserve_placeholder
2242 * Turn pages of a given view into a placeholder.
2243 * virtual_mutex must be held by caller.
2245 static NTSTATUS
free_pages_preserve_placeholder( struct file_view
*view
, char *base
, size_t size
)
2249 if (!size
) return STATUS_INVALID_PARAMETER_3
;
2250 if (!(view
->protect
& VPROT_PLACEHOLDER
)) return STATUS_CONFLICTING_ADDRESSES
;
2251 if (view
->protect
& VPROT_FREE_PLACEHOLDER
&& size
== view
->size
) return STATUS_CONFLICTING_ADDRESSES
;
2253 if (size
< view
->size
)
2255 status
= remove_pages_from_view( view
, base
, size
);
2256 if (status
) return status
;
2258 status
= create_view( &view
, base
, size
, VPROT_PLACEHOLDER
| VPROT_FREE_PLACEHOLDER
);
2259 if (status
) return status
;
2262 view
->protect
= VPROT_PLACEHOLDER
| VPROT_FREE_PLACEHOLDER
;
2263 set_page_vprot( view
->base
, view
->size
, 0 );
2264 anon_mmap_fixed( view
->base
, view
->size
, PROT_NONE
, 0 );
2265 return STATUS_SUCCESS
;
2269 /***********************************************************************
2272 * Free some pages of a given view.
2273 * virtual_mutex must be held by caller.
2275 static NTSTATUS
free_pages( struct file_view
*view
, char *base
, size_t size
)
2279 if (size
== view
->size
)
2281 assert( base
== view
->base
);
2282 delete_view( view
);
2283 return STATUS_SUCCESS
;
2286 status
= remove_pages_from_view( view
, base
, size
);
2289 set_page_vprot( base
, size
, 0 );
2290 if (arm64ec_map
) clear_arm64ec_range( base
, size
);
2291 unmap_area( base
, size
);
2297 /***********************************************************************
2298 * allocate_dos_memory
2300 * Allocate the DOS memory range.
2302 static NTSTATUS
allocate_dos_memory( struct file_view
**view
, unsigned int vprot
)
2306 void * const low_64k
= (void *)0x10000;
2307 const size_t dosmem_size
= 0x110000;
2308 int unix_prot
= get_unix_prot( vprot
);
2310 /* check for existing view */
2312 if (find_view_range( 0, dosmem_size
)) return STATUS_CONFLICTING_ADDRESSES
;
2314 /* check without the first 64K */
2316 if (mmap_is_in_reserved_area( low_64k
, dosmem_size
- 0x10000 ) != 1)
2318 addr
= anon_mmap_tryfixed( low_64k
, dosmem_size
- 0x10000, unix_prot
, 0 );
2319 if (addr
== MAP_FAILED
) return map_view( view
, NULL
, dosmem_size
, 0, vprot
, 0, 0 );
2322 /* now try to allocate the low 64K too */
2324 if (mmap_is_in_reserved_area( NULL
, 0x10000 ) != 1)
2326 addr
= anon_mmap_tryfixed( (void *)page_size
, 0x10000 - page_size
, unix_prot
, 0 );
2327 if (addr
!= MAP_FAILED
)
2329 if (!anon_mmap_fixed( NULL
, page_size
, unix_prot
, 0 ))
2332 TRACE( "successfully mapped low 64K range\n" );
2334 else TRACE( "failed to map page 0\n" );
2339 TRACE( "failed to map low 64K range\n" );
2343 /* now reserve the whole range */
2345 size
= (char *)dosmem_size
- (char *)addr
;
2346 anon_mmap_fixed( addr
, size
, unix_prot
, 0 );
2347 return create_view( view
, addr
, size
, vprot
);
2351 /***********************************************************************
2354 * Map the header of a PE file into memory.
2356 static NTSTATUS
map_pe_header( void *ptr
, size_t size
, int fd
, BOOL
*removable
)
2358 if (!size
) return STATUS_INVALID_IMAGE_FORMAT
;
2362 if (mmap( ptr
, size
, PROT_READ
|PROT_WRITE
|PROT_EXEC
, MAP_FIXED
|MAP_PRIVATE
, fd
, 0 ) != MAP_FAILED
)
2363 return STATUS_SUCCESS
;
2369 WARN( "noexec file system, falling back to read\n" );
2373 WARN( "file system doesn't support mmap, falling back to read\n" );
2376 return STATUS_NO_MEMORY
;
2380 pread( fd
, ptr
, size
, 0 );
2381 return STATUS_SUCCESS
; /* page protections will be updated later */
2386 /***********************************************************************
2387 * apply_arm64x_relocations
2389 static void apply_arm64x_relocations( char *base
, const IMAGE_BASE_RELOCATION
*reloc
, size_t size
)
2391 const IMAGE_BASE_RELOCATION
*reloc_end
= (const IMAGE_BASE_RELOCATION
*)((const char *)reloc
+ size
);
2393 while (reloc
< reloc_end
- 1 && reloc
->SizeOfBlock
)
2395 const USHORT
*rel
= (const USHORT
*)(reloc
+ 1);
2396 const USHORT
*rel_end
= (const USHORT
*)reloc
+ reloc
->SizeOfBlock
/ sizeof(USHORT
);
2397 char *page
= base
+ reloc
->VirtualAddress
;
2399 while (rel
< rel_end
&& *rel
)
2401 USHORT offset
= *rel
& 0xfff;
2402 USHORT type
= (*rel
>> 12) & 3;
2403 USHORT arg
= *rel
>> 14;
2408 case IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL
:
2409 memset( page
+ offset
, 0, 1 << arg
);
2411 case IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE
:
2412 memcpy( page
+ offset
, rel
, 1 << arg
);
2413 rel
+= (1 << arg
) / sizeof(USHORT
);
2415 case IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA
:
2416 val
= (unsigned int)*rel
++ * ((arg
& 2) ? 8 : 4);
2417 if (arg
& 1) val
= -val
;
2418 *(int *)(page
+ offset
) += val
;
2422 reloc
= (const IMAGE_BASE_RELOCATION
*)rel_end
;
2427 /***********************************************************************
2428 * update_arm64x_mapping
2430 static void update_arm64x_mapping( char *base
, IMAGE_NT_HEADERS
*nt
, IMAGE_SECTION_HEADER
*sections
)
2432 ULONG i
, size
, sec
, offset
;
2433 const IMAGE_DATA_DIRECTORY
*dir
;
2434 const IMAGE_LOAD_CONFIG_DIRECTORY
*cfg
;
2435 const IMAGE_ARM64EC_METADATA
*metadata
;
2436 const IMAGE_DYNAMIC_RELOCATION_TABLE
*table
;
2437 const char *ptr
, *end
;
2439 /* retrieve config directory */
2441 if (nt
->FileHeader
.Machine
!= IMAGE_FILE_MACHINE_ARM64
) return;
2442 if (nt
->OptionalHeader
.Magic
!= IMAGE_NT_OPTIONAL_HDR64_MAGIC
) return;
2443 dir
= nt
->OptionalHeader
.DataDirectory
+ IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG
;
2444 if (!dir
->VirtualAddress
|| !dir
->Size
) return;
2445 cfg
= (void *)(base
+ dir
->VirtualAddress
);
2446 size
= min( dir
->Size
, cfg
->Size
);
2448 /* update code ranges */
2450 if (size
<= offsetof( IMAGE_LOAD_CONFIG_DIRECTORY
, CHPEMetadataPointer
)) return;
2451 if (!cfg
->CHPEMetadataPointer
) return;
2452 metadata
= (void *)(base
+ (cfg
->CHPEMetadataPointer
- nt
->OptionalHeader
.ImageBase
));
2453 if (metadata
->CodeMap
&& arm64ec_map
)
2455 const IMAGE_CHPE_RANGE_ENTRY
*map
= (void *)(base
+ metadata
->CodeMap
);
2457 for (i
= 0; i
< metadata
->CodeMapCount
; i
++)
2459 if ((map
[i
].StartOffset
& 0x3) != 1 /* arm64ec */) continue;
2460 set_arm64ec_range( base
+ (map
[i
].StartOffset
& ~3), map
[i
].Length
);
2464 /* apply dynamic relocations */
2466 if (size
<= offsetof( IMAGE_LOAD_CONFIG_DIRECTORY
, DynamicValueRelocTableSection
)) return;
2467 offset
= cfg
->DynamicValueRelocTableOffset
;
2468 sec
= cfg
->DynamicValueRelocTableSection
;
2469 if (!sec
|| sec
> nt
->FileHeader
.NumberOfSections
) return;
2470 if (offset
>= sections
[sec
- 1].Misc
.VirtualSize
) return;
2471 table
= (const IMAGE_DYNAMIC_RELOCATION_TABLE
*)(base
+ sections
[sec
- 1].VirtualAddress
+ offset
);
2472 ptr
= (const char *)(table
+ 1);
2473 end
= ptr
+ table
->Size
;
2474 switch (table
->Version
)
2479 const IMAGE_DYNAMIC_RELOCATION64
*dyn
= (const IMAGE_DYNAMIC_RELOCATION64
*)ptr
;
2480 if (dyn
->Symbol
== IMAGE_DYNAMIC_RELOCATION_ARM64X
)
2482 apply_arm64x_relocations( base
, (const IMAGE_BASE_RELOCATION
*)(dyn
+ 1),
2483 dyn
->BaseRelocSize
);
2486 ptr
+= sizeof(*dyn
) + dyn
->BaseRelocSize
;
2492 const IMAGE_DYNAMIC_RELOCATION64_V2
*dyn
= (const IMAGE_DYNAMIC_RELOCATION64_V2
*)ptr
;
2493 if (dyn
->Symbol
== IMAGE_DYNAMIC_RELOCATION_ARM64X
)
2495 apply_arm64x_relocations( base
, (const IMAGE_BASE_RELOCATION
*)(dyn
+ 1),
2496 dyn
->FixupInfoSize
);
2499 ptr
+= dyn
->HeaderSize
+ dyn
->FixupInfoSize
;
2503 FIXME( "unsupported version %u\n", table
->Version
);
2508 #endif /* __aarch64__ */
2510 /***********************************************************************
2511 * map_image_into_view
2513 * Map an executable (PE format) image into an existing view.
2514 * virtual_mutex must be held by caller.
2516 static NTSTATUS
map_image_into_view( struct file_view
*view
, const WCHAR
*filename
, int fd
, void *orig_base
,
2517 pe_image_info_t
*image_info
, USHORT machine
,
2518 int shared_fd
, BOOL removable
)
2520 IMAGE_DOS_HEADER
*dos
;
2521 IMAGE_NT_HEADERS
*nt
;
2522 IMAGE_SECTION_HEADER sections
[96];
2523 IMAGE_SECTION_HEADER
*sec
;
2524 IMAGE_DATA_DIRECTORY
*imports
;
2525 NTSTATUS status
= STATUS_CONFLICTING_ADDRESSES
;
2529 char *header_end
, *header_start
;
2530 char *ptr
= view
->base
;
2531 SIZE_T header_size
, total_size
= view
->size
;
2533 TRACE_(module
)( "mapping PE file %s at %p-%p\n", debugstr_w(filename
), ptr
, ptr
+ total_size
);
2535 /* map the header */
2538 header_size
= min( image_info
->header_size
, st
.st_size
);
2539 if ((status
= map_pe_header( view
->base
, header_size
, fd
, &removable
))) return status
;
2541 status
= STATUS_INVALID_IMAGE_FORMAT
; /* generic error */
2542 dos
= (IMAGE_DOS_HEADER
*)ptr
;
2543 nt
= (IMAGE_NT_HEADERS
*)(ptr
+ dos
->e_lfanew
);
2544 header_end
= ptr
+ ROUND_SIZE( 0, header_size
);
2545 memset( ptr
+ header_size
, 0, header_end
- (ptr
+ header_size
) );
2546 if ((char *)(nt
+ 1) > header_end
) return status
;
2547 header_start
= (char*)&nt
->OptionalHeader
+nt
->FileHeader
.SizeOfOptionalHeader
;
2548 if (nt
->FileHeader
.NumberOfSections
> ARRAY_SIZE( sections
)) return status
;
2549 if (header_start
+ sizeof(*sections
) * nt
->FileHeader
.NumberOfSections
> header_end
) return status
;
2550 /* Some applications (e.g. the Steam version of Borderlands) map over the top of the section headers,
2551 * copying the headers into local memory is necessary to properly load such applications. */
2552 memcpy(sections
, header_start
, sizeof(*sections
) * nt
->FileHeader
.NumberOfSections
);
2555 imports
= nt
->OptionalHeader
.DataDirectory
+ IMAGE_DIRECTORY_ENTRY_IMPORT
;
2556 if (!imports
->Size
|| !imports
->VirtualAddress
) imports
= NULL
;
2558 /* check for non page-aligned binary */
2560 if (image_info
->image_flags
& IMAGE_FLAGS_ImageMappedFlat
)
2562 /* unaligned sections, this happens for native subsystem binaries */
2563 /* in that case Windows simply maps in the whole file */
2565 total_size
= min( total_size
, ROUND_SIZE( 0, st
.st_size
));
2566 if (map_file_into_view( view
, fd
, 0, total_size
, 0, VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITECOPY
,
2567 removable
) != STATUS_SUCCESS
) return status
;
2569 /* check that all sections are loaded at the right offset */
2570 if (nt
->OptionalHeader
.FileAlignment
!= nt
->OptionalHeader
.SectionAlignment
) return status
;
2571 for (i
= 0; i
< nt
->FileHeader
.NumberOfSections
; i
++)
2573 if (sec
[i
].VirtualAddress
!= sec
[i
].PointerToRawData
)
2574 return status
; /* Windows refuses to load in that case too */
2577 /* set the image protections */
2578 set_vprot( view
, ptr
, total_size
, VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITECOPY
| VPROT_EXEC
);
2580 /* no relocations are performed on non page-aligned binaries */
2581 return STATUS_SUCCESS
;
2585 /* map all the sections */
2587 for (i
= pos
= 0; i
< nt
->FileHeader
.NumberOfSections
; i
++, sec
++)
2589 static const SIZE_T sector_align
= 0x1ff;
2590 SIZE_T map_size
, file_start
, file_size
, end
;
2592 if (!sec
->Misc
.VirtualSize
)
2593 map_size
= ROUND_SIZE( 0, sec
->SizeOfRawData
);
2595 map_size
= ROUND_SIZE( 0, sec
->Misc
.VirtualSize
);
2597 /* file positions are rounded to sector boundaries regardless of OptionalHeader.FileAlignment */
2598 file_start
= sec
->PointerToRawData
& ~sector_align
;
2599 file_size
= (sec
->SizeOfRawData
+ (sec
->PointerToRawData
& sector_align
) + sector_align
) & ~sector_align
;
2600 if (file_size
> map_size
) file_size
= map_size
;
2602 /* a few sanity checks */
2603 end
= sec
->VirtualAddress
+ ROUND_SIZE( sec
->VirtualAddress
, map_size
);
2604 if (sec
->VirtualAddress
> total_size
|| end
> total_size
|| end
< sec
->VirtualAddress
)
2606 WARN_(module
)( "%s section %.8s too large (%x+%lx/%lx)\n",
2607 debugstr_w(filename
), sec
->Name
, (int)sec
->VirtualAddress
, map_size
, total_size
);
2611 if ((sec
->Characteristics
& IMAGE_SCN_MEM_SHARED
) &&
2612 (sec
->Characteristics
& IMAGE_SCN_MEM_WRITE
))
2614 TRACE_(module
)( "%s mapping shared section %.8s at %p off %x (%x) size %lx (%lx) flags %x\n",
2615 debugstr_w(filename
), sec
->Name
, ptr
+ sec
->VirtualAddress
,
2616 (int)sec
->PointerToRawData
, (int)pos
, file_size
, map_size
,
2617 (int)sec
->Characteristics
);
2618 if (map_file_into_view( view
, shared_fd
, sec
->VirtualAddress
, map_size
, pos
,
2619 VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITE
, FALSE
) != STATUS_SUCCESS
)
2621 ERR_(module
)( "Could not map %s shared section %.8s\n", debugstr_w(filename
), sec
->Name
);
2625 /* check if the import directory falls inside this section */
2626 if (imports
&& imports
->VirtualAddress
>= sec
->VirtualAddress
&&
2627 imports
->VirtualAddress
< sec
->VirtualAddress
+ map_size
)
2629 UINT_PTR base
= imports
->VirtualAddress
& ~page_mask
;
2630 UINT_PTR end
= base
+ ROUND_SIZE( imports
->VirtualAddress
, imports
->Size
);
2631 if (end
> sec
->VirtualAddress
+ map_size
) end
= sec
->VirtualAddress
+ map_size
;
2633 map_file_into_view( view
, shared_fd
, base
, end
- base
,
2634 pos
+ (base
- sec
->VirtualAddress
),
2635 VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITECOPY
, FALSE
);
2641 TRACE_(module
)( "mapping %s section %.8s at %p off %x size %x virt %x flags %x\n",
2642 debugstr_w(filename
), sec
->Name
, ptr
+ sec
->VirtualAddress
,
2643 (int)sec
->PointerToRawData
, (int)sec
->SizeOfRawData
,
2644 (int)sec
->Misc
.VirtualSize
, (int)sec
->Characteristics
);
2646 if (!sec
->PointerToRawData
|| !file_size
) continue;
2648 /* Note: if the section is not aligned properly map_file_into_view will magically
2649 * fall back to read(), so we don't need to check anything here.
2651 end
= file_start
+ file_size
;
2652 if (sec
->PointerToRawData
>= st
.st_size
||
2653 end
> ((st
.st_size
+ sector_align
) & ~sector_align
) ||
2655 map_file_into_view( view
, fd
, sec
->VirtualAddress
, file_size
, file_start
,
2656 VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITECOPY
,
2657 removable
) != STATUS_SUCCESS
)
2659 ERR_(module
)( "Could not map %s section %.8s, file probably truncated\n",
2660 debugstr_w(filename
), sec
->Name
);
2664 if (file_size
& page_mask
)
2666 end
= ROUND_SIZE( 0, file_size
);
2667 if (end
> map_size
) end
= map_size
;
2668 TRACE_(module
)("clearing %p - %p\n",
2669 ptr
+ sec
->VirtualAddress
+ file_size
,
2670 ptr
+ sec
->VirtualAddress
+ end
);
2671 memset( ptr
+ sec
->VirtualAddress
+ file_size
, 0, end
- file_size
);
2676 if (machine
== IMAGE_FILE_MACHINE_AMD64
||
2677 (!machine
&& main_image_info
.Machine
== IMAGE_FILE_MACHINE_AMD64
))
2679 update_arm64x_mapping( ptr
, nt
, sections
);
2680 /* reload changed data from NT header */
2681 image_info
->machine
= nt
->FileHeader
.Machine
;
2682 image_info
->entry_point
= nt
->OptionalHeader
.AddressOfEntryPoint
;
2685 if (machine
&& machine
!= nt
->FileHeader
.Machine
) return STATUS_NOT_SUPPORTED
;
2687 /* set the image protections */
2689 set_vprot( view
, ptr
, ROUND_SIZE( 0, header_size
), VPROT_COMMITTED
| VPROT_READ
);
2692 for (i
= 0; i
< nt
->FileHeader
.NumberOfSections
; i
++, sec
++)
2695 BYTE vprot
= VPROT_COMMITTED
;
2697 if (sec
->Misc
.VirtualSize
)
2698 size
= ROUND_SIZE( sec
->VirtualAddress
, sec
->Misc
.VirtualSize
);
2700 size
= ROUND_SIZE( sec
->VirtualAddress
, sec
->SizeOfRawData
);
2702 if (sec
->Characteristics
& IMAGE_SCN_MEM_READ
) vprot
|= VPROT_READ
;
2703 if (sec
->Characteristics
& IMAGE_SCN_MEM_WRITE
) vprot
|= VPROT_WRITECOPY
;
2704 if (sec
->Characteristics
& IMAGE_SCN_MEM_EXECUTE
) vprot
|= VPROT_EXEC
;
2706 if (!set_vprot( view
, ptr
+ sec
->VirtualAddress
, size
, vprot
) && (vprot
& VPROT_EXEC
))
2707 ERR( "failed to set %08x protection on %s section %.8s, noexec filesystem?\n",
2708 (int)sec
->Characteristics
, debugstr_w(filename
), sec
->Name
);
2711 #ifdef VALGRIND_LOAD_PDB_DEBUGINFO
2712 VALGRIND_LOAD_PDB_DEBUGINFO(fd
, ptr
, total_size
, ptr
- (char *)orig_base
);
2714 return STATUS_SUCCESS
;
2718 /***********************************************************************
2721 static unsigned int get_mapping_info( HANDLE handle
, ACCESS_MASK access
, unsigned int *sec_flags
,
2722 mem_size_t
*full_size
, HANDLE
*shared_file
, pe_image_info_t
**info
)
2724 pe_image_info_t
*image_info
;
2725 SIZE_T total
, size
= 1024;
2726 unsigned int status
;
2730 if (!(image_info
= malloc( size
))) return STATUS_NO_MEMORY
;
2732 SERVER_START_REQ( get_mapping_info
)
2734 req
->handle
= wine_server_obj_handle( handle
);
2735 req
->access
= access
;
2736 wine_server_set_reply( req
, image_info
, size
);
2737 status
= wine_server_call( req
);
2738 *sec_flags
= reply
->flags
;
2739 *full_size
= reply
->size
;
2740 total
= reply
->total
;
2741 *shared_file
= wine_server_ptr_handle( reply
->shared_file
);
2744 if (!status
&& total
<= size
- sizeof(WCHAR
)) break;
2746 if (status
) return status
;
2747 if (*shared_file
) NtClose( *shared_file
);
2748 size
= total
+ sizeof(WCHAR
);
2753 WCHAR
*filename
= (WCHAR
*)(image_info
+ 1);
2755 assert( total
>= sizeof(*image_info
) );
2756 total
-= sizeof(*image_info
);
2757 filename
[total
/ sizeof(WCHAR
)] = 0;
2760 else free( image_info
);
2762 return STATUS_SUCCESS
;
2766 /***********************************************************************
2769 * Map a PE image section into memory.
2771 static NTSTATUS
virtual_map_image( HANDLE mapping
, void **addr_ptr
, SIZE_T
*size_ptr
, HANDLE shared_file
,
2772 ULONG_PTR limit
, ULONG alloc_type
, USHORT machine
,
2773 pe_image_info_t
*image_info
, WCHAR
*filename
, BOOL is_builtin
)
2775 unsigned int vprot
= SEC_IMAGE
| SEC_FILE
| VPROT_COMMITTED
| VPROT_READ
| VPROT_EXEC
| VPROT_WRITECOPY
;
2776 int unix_fd
= -1, needs_close
;
2777 int shared_fd
= -1, shared_needs_close
= 0;
2778 SIZE_T size
= image_info
->map_size
;
2779 struct file_view
*view
;
2780 unsigned int status
;
2784 if ((status
= server_get_unix_fd( mapping
, 0, &unix_fd
, &needs_close
, NULL
, NULL
)))
2787 if (shared_file
&& ((status
= server_get_unix_fd( shared_file
, FILE_READ_DATA
|FILE_WRITE_DATA
,
2788 &shared_fd
, &shared_needs_close
, NULL
, NULL
))))
2790 if (needs_close
) close( unix_fd
);
2794 status
= STATUS_INVALID_PARAMETER
;
2795 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
2797 base
= wine_server_get_ptr( image_info
->base
);
2798 if ((ULONG_PTR
)base
!= image_info
->base
) base
= NULL
;
2800 if ((char *)base
>= (char *)address_space_start
) /* make sure the DOS area remains free */
2801 status
= map_view( &view
, base
, size
, alloc_type
, vprot
, limit
, 0 );
2803 if (status
) status
= map_view( &view
, NULL
, size
, alloc_type
, vprot
, limit
, 0 );
2804 if (status
) goto done
;
2806 status
= map_image_into_view( view
, filename
, unix_fd
, base
, image_info
,
2807 machine
, shared_fd
, needs_close
);
2808 if (status
== STATUS_SUCCESS
)
2810 SERVER_START_REQ( map_image_view
)
2812 req
->mapping
= wine_server_obj_handle( mapping
);
2813 req
->base
= wine_server_client_ptr( view
->base
);
2815 req
->entry
= image_info
->entry_point
;
2816 req
->machine
= image_info
->machine
;
2817 status
= wine_server_call( req
);
2821 if (NT_SUCCESS(status
))
2823 if (is_builtin
) add_builtin_module( view
->base
, NULL
);
2824 *addr_ptr
= view
->base
;
2826 VIRTUAL_DEBUG_DUMP_VIEW( view
);
2828 else delete_view( view
);
2831 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
2832 if (needs_close
) close( unix_fd
);
2833 if (shared_needs_close
) close( shared_fd
);
2838 /***********************************************************************
2839 * virtual_map_section
2841 * Map a file section into memory.
2843 static unsigned int virtual_map_section( HANDLE handle
, PVOID
*addr_ptr
, ULONG_PTR limit
,
2844 SIZE_T commit_size
, const LARGE_INTEGER
*offset_ptr
,
2845 SIZE_T
*size_ptr
, ULONG alloc_type
, ULONG protect
,
2849 mem_size_t full_size
;
2852 pe_image_info_t
*image_info
= NULL
;
2855 int unix_handle
= -1, needs_close
;
2856 unsigned int vprot
, sec_flags
;
2857 struct file_view
*view
;
2859 LARGE_INTEGER offset
;
2866 case PAGE_WRITECOPY
:
2867 access
= SECTION_MAP_READ
;
2869 case PAGE_READWRITE
:
2870 access
= SECTION_MAP_WRITE
;
2873 case PAGE_EXECUTE_READ
:
2874 case PAGE_EXECUTE_WRITECOPY
:
2875 access
= SECTION_MAP_READ
| SECTION_MAP_EXECUTE
;
2877 case PAGE_EXECUTE_READWRITE
:
2878 access
= SECTION_MAP_WRITE
| SECTION_MAP_EXECUTE
;
2881 return STATUS_INVALID_PAGE_PROTECTION
;
2884 res
= get_mapping_info( handle
, access
, &sec_flags
, &full_size
, &shared_file
, &image_info
);
2885 if (res
) return res
;
2889 filename
= (WCHAR
*)(image_info
+ 1);
2890 /* check if we can replace that mapping with the builtin */
2891 res
= load_builtin( image_info
, filename
, machine
, addr_ptr
, size_ptr
, limit
);
2892 if (res
== STATUS_IMAGE_ALREADY_LOADED
)
2893 res
= virtual_map_image( handle
, addr_ptr
, size_ptr
, shared_file
, limit
,
2894 alloc_type
, machine
, image_info
, filename
, FALSE
);
2895 if (shared_file
) NtClose( shared_file
);
2901 offset
.QuadPart
= offset_ptr
? offset_ptr
->QuadPart
: 0;
2902 if (offset
.QuadPart
>= full_size
) return STATUS_INVALID_PARAMETER
;
2906 if (size
> full_size
- offset
.QuadPart
) return STATUS_INVALID_VIEW_SIZE
;
2910 size
= full_size
- offset
.QuadPart
;
2911 if (size
!= full_size
- offset
.QuadPart
) /* truncated */
2913 WARN( "Files larger than 4Gb (%s) not supported on this platform\n",
2914 wine_dbgstr_longlong(full_size
) );
2915 return STATUS_INVALID_PARAMETER
;
2918 if (!(size
= ROUND_SIZE( 0, size
))) return STATUS_INVALID_PARAMETER
; /* wrap-around */
2920 get_vprot_flags( protect
, &vprot
, FALSE
);
2922 if (!(sec_flags
& SEC_RESERVE
)) vprot
|= VPROT_COMMITTED
;
2924 if ((res
= server_get_unix_fd( handle
, 0, &unix_handle
, &needs_close
, NULL
, NULL
))) return res
;
2926 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
2928 res
= map_view( &view
, base
, size
, alloc_type
, vprot
, limit
, 0 );
2931 TRACE( "handle=%p size=%lx offset=%s\n", handle
, size
, wine_dbgstr_longlong(offset
.QuadPart
) );
2932 res
= map_file_into_view( view
, unix_handle
, 0, size
, offset
.QuadPart
, vprot
, needs_close
);
2933 if (res
== STATUS_SUCCESS
)
2935 SERVER_START_REQ( map_view
)
2937 req
->mapping
= wine_server_obj_handle( handle
);
2938 req
->access
= access
;
2939 req
->base
= wine_server_client_ptr( view
->base
);
2941 req
->start
= offset
.QuadPart
;
2942 res
= wine_server_call( req
);
2946 else ERR( "mapping %p %lx %s failed\n", view
->base
, size
, wine_dbgstr_longlong(offset
.QuadPart
) );
2948 if (NT_SUCCESS(res
))
2950 *addr_ptr
= view
->base
;
2952 VIRTUAL_DEBUG_DUMP_VIEW( view
);
2954 else delete_view( view
);
2957 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
2958 if (needs_close
) close( unix_handle
);
2963 struct alloc_virtual_heap
2969 /* callback for mmap_enum_reserved_areas to allocate space for the virtual heap */
2970 static int alloc_virtual_heap( void *base
, SIZE_T size
, void *arg
)
2972 struct alloc_virtual_heap
*alloc
= arg
;
2973 void *end
= (char *)base
+ size
;
2975 if (is_beyond_limit( base
, size
, address_space_limit
)) address_space_limit
= (char *)base
+ size
;
2976 if (is_win64
&& base
< (void *)0x80000000) return 0;
2977 if (preload_reserve_end
>= end
)
2979 if (preload_reserve_start
<= base
) return 0; /* no space in that area */
2980 if (preload_reserve_start
< end
) end
= preload_reserve_start
;
2982 else if (preload_reserve_end
> base
)
2984 if (preload_reserve_start
<= base
) base
= preload_reserve_end
;
2985 else if ((char *)end
- (char *)preload_reserve_end
>= alloc
->size
) base
= preload_reserve_end
;
2986 else end
= preload_reserve_start
;
2988 if ((char *)end
- (char *)base
< alloc
->size
) return 0;
2989 alloc
->base
= anon_mmap_fixed( (char *)end
- alloc
->size
, alloc
->size
, PROT_READ
|PROT_WRITE
, 0 );
2990 return (alloc
->base
!= MAP_FAILED
);
2993 /***********************************************************************
2996 void virtual_init(void)
2998 const struct preload_info
**preload_info
= dlsym( RTLD_DEFAULT
, "wine_main_preload_info" );
2999 const char *preload
= getenv( "WINEPRELOADRESERVE" );
3000 struct alloc_virtual_heap alloc_views
;
3003 pthread_mutexattr_t attr
;
3005 pthread_mutexattr_init( &attr
);
3006 pthread_mutexattr_settype( &attr
, PTHREAD_MUTEX_RECURSIVE
);
3007 pthread_mutex_init( &virtual_mutex
, &attr
);
3008 pthread_mutexattr_destroy( &attr
);
3010 if (preload_info
&& *preload_info
)
3011 for (i
= 0; (*preload_info
)[i
].size
; i
++)
3012 mmap_add_reserved_area( (*preload_info
)[i
].addr
, (*preload_info
)[i
].size
);
3014 mmap_init( preload_info
? *preload_info
: NULL
);
3016 if ((preload
= getenv("WINEPRELOADRESERVE")))
3018 unsigned long start
, end
;
3019 if (sscanf( preload
, "%lx-%lx", &start
, &end
) == 2)
3021 preload_reserve_start
= (void *)start
;
3022 preload_reserve_end
= (void *)end
;
3023 /* some apps start inside the DOS area */
3024 if (preload_reserve_start
)
3025 address_space_start
= min( address_space_start
, preload_reserve_start
);
3029 /* try to find space in a reserved area for the views and pages protection table */
3031 pages_vprot_size
= ((size_t)address_space_limit
>> page_shift
>> pages_vprot_shift
) + 1;
3032 alloc_views
.size
= 2 * view_block_size
+ pages_vprot_size
* sizeof(*pages_vprot
);
3034 alloc_views
.size
= 2 * view_block_size
+ (1U << (32 - page_shift
));
3036 if (mmap_enum_reserved_areas( alloc_virtual_heap
, &alloc_views
, 1 ))
3037 mmap_remove_reserved_area( alloc_views
.base
, alloc_views
.size
);
3039 alloc_views
.base
= anon_mmap_alloc( alloc_views
.size
, PROT_READ
| PROT_WRITE
);
3041 assert( alloc_views
.base
!= MAP_FAILED
);
3042 view_block_start
= alloc_views
.base
;
3043 view_block_end
= view_block_start
+ view_block_size
/ sizeof(*view_block_start
);
3044 free_ranges
= (void *)((char *)alloc_views
.base
+ view_block_size
);
3045 pages_vprot
= (void *)((char *)alloc_views
.base
+ 2 * view_block_size
);
3046 wine_rb_init( &views_tree
, compare_view
);
3048 free_ranges
[0].base
= (void *)0;
3049 free_ranges
[0].end
= (void *)~0;
3050 free_ranges_end
= free_ranges
+ 1;
3052 /* make the DOS area accessible (except the low 64K) to hide bugs in broken apps like Excel 2003 */
3053 size
= (char *)address_space_start
- (char *)0x10000;
3054 if (size
&& mmap_is_in_reserved_area( (void*)0x10000, size
) == 1)
3055 anon_mmap_fixed( (void *)0x10000, size
, PROT_READ
| PROT_WRITE
, 0 );
3059 /***********************************************************************
3060 * get_system_affinity_mask
3062 ULONG_PTR
get_system_affinity_mask(void)
3064 ULONG num_cpus
= peb
->NumberOfProcessors
;
3065 if (num_cpus
>= sizeof(ULONG_PTR
) * 8) return ~(ULONG_PTR
)0;
3066 return ((ULONG_PTR
)1 << num_cpus
) - 1;
3069 /***********************************************************************
3070 * virtual_get_system_info
3072 void virtual_get_system_info( SYSTEM_BASIC_INFORMATION
*info
, BOOL wow64
)
3074 #if defined(HAVE_SYSINFO) \
3075 && defined(HAVE_STRUCT_SYSINFO_TOTALRAM) && defined(HAVE_STRUCT_SYSINFO_MEM_UNIT)
3076 struct sysinfo sinfo
;
3078 if (!sysinfo(&sinfo
))
3080 ULONG64 total
= (ULONG64
)sinfo
.totalram
* sinfo
.mem_unit
;
3081 info
->MmHighestPhysicalPage
= max(1, total
/ page_size
);
3083 #elif defined(_SC_PHYS_PAGES)
3084 LONG64 phys_pages
= sysconf( _SC_PHYS_PAGES
);
3086 info
->MmHighestPhysicalPage
= max(1, phys_pages
);
3088 info
->MmHighestPhysicalPage
= 0x7fffffff / page_size
;
3092 info
->KeMaximumIncrement
= 0; /* FIXME */
3093 info
->PageSize
= page_size
;
3094 info
->MmLowestPhysicalPage
= 1;
3095 info
->MmNumberOfPhysicalPages
= info
->MmHighestPhysicalPage
- info
->MmLowestPhysicalPage
;
3096 info
->AllocationGranularity
= granularity_mask
+ 1;
3097 info
->LowestUserAddress
= (void *)0x10000;
3098 info
->ActiveProcessorsAffinityMask
= get_system_affinity_mask();
3099 info
->NumberOfProcessors
= peb
->NumberOfProcessors
;
3100 if (wow64
) info
->HighestUserAddress
= (char *)get_wow_user_space_limit() - 1;
3101 else info
->HighestUserAddress
= (char *)user_space_limit
- 1;
3105 /***********************************************************************
3106 * virtual_map_builtin_module
3108 NTSTATUS
virtual_map_builtin_module( HANDLE mapping
, void **module
, SIZE_T
*size
, SECTION_IMAGE_INFORMATION
*info
,
3109 ULONG_PTR limit
, WORD machine
, BOOL prefer_native
)
3111 mem_size_t full_size
;
3112 unsigned int sec_flags
;
3114 pe_image_info_t
*image_info
= NULL
;
3118 if ((status
= get_mapping_info( mapping
, SECTION_MAP_READ
,
3119 &sec_flags
, &full_size
, &shared_file
, &image_info
)))
3122 if (!image_info
) return STATUS_INVALID_PARAMETER
;
3126 filename
= (WCHAR
*)(image_info
+ 1);
3128 if (!image_info
->wine_builtin
) /* ignore non-builtins */
3130 WARN( "%s found in WINEDLLPATH but not a builtin, ignoring\n", debugstr_w(filename
) );
3131 status
= STATUS_DLL_NOT_FOUND
;
3133 else if (prefer_native
&& (image_info
->dll_charact
& IMAGE_DLLCHARACTERISTICS_PREFER_NATIVE
))
3135 TRACE( "%s has prefer-native flag, ignoring builtin\n", debugstr_w(filename
) );
3136 status
= STATUS_IMAGE_ALREADY_LOADED
;
3140 status
= virtual_map_image( mapping
, module
, size
, shared_file
, limit
, 0,
3141 machine
, image_info
, filename
, TRUE
);
3142 virtual_fill_image_information( image_info
, info
);
3145 if (shared_file
) NtClose( shared_file
);
3151 /***********************************************************************
3152 * virtual_map_module
3154 NTSTATUS
virtual_map_module( HANDLE mapping
, void **module
, SIZE_T
*size
, SECTION_IMAGE_INFORMATION
*info
,
3155 ULONG_PTR limit
, USHORT machine
)
3157 unsigned int status
;
3158 mem_size_t full_size
;
3159 unsigned int sec_flags
;
3161 pe_image_info_t
*image_info
= NULL
;
3164 if ((status
= get_mapping_info( mapping
, SECTION_MAP_READ
,
3165 &sec_flags
, &full_size
, &shared_file
, &image_info
)))
3168 if (!image_info
) return STATUS_INVALID_PARAMETER
;
3172 filename
= (WCHAR
*)(image_info
+ 1);
3174 /* check if we can replace that mapping with the builtin */
3175 status
= load_builtin( image_info
, filename
, machine
, module
, size
, limit
);
3176 if (status
== STATUS_IMAGE_ALREADY_LOADED
)
3177 status
= virtual_map_image( mapping
, module
, size
, shared_file
, limit
, 0,
3178 machine
, image_info
, filename
, FALSE
);
3180 virtual_fill_image_information( image_info
, info
);
3181 if (shared_file
) NtClose( shared_file
);
3187 /***********************************************************************
3188 * virtual_create_builtin_view
3190 NTSTATUS
virtual_create_builtin_view( void *module
, const UNICODE_STRING
*nt_name
,
3191 pe_image_info_t
*info
, void *so_handle
)
3195 IMAGE_DOS_HEADER
*dos
= module
;
3196 IMAGE_NT_HEADERS
*nt
= (IMAGE_NT_HEADERS
*)((char *)dos
+ dos
->e_lfanew
);
3197 SIZE_T size
= info
->map_size
;
3198 IMAGE_SECTION_HEADER
*sec
;
3199 struct file_view
*view
;
3200 void *base
= wine_server_get_ptr( info
->base
);
3203 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3204 status
= create_view( &view
, base
, size
, SEC_IMAGE
| SEC_FILE
| VPROT_SYSTEM
|
3205 VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITECOPY
| VPROT_EXEC
);
3208 TRACE( "created %p-%p for %s\n", base
, (char *)base
+ size
, debugstr_us(nt_name
) );
3210 /* The PE header is always read-only, no write, no execute. */
3211 set_page_vprot( base
, page_size
, VPROT_COMMITTED
| VPROT_READ
);
3213 sec
= (IMAGE_SECTION_HEADER
*)((char *)&nt
->OptionalHeader
+ nt
->FileHeader
.SizeOfOptionalHeader
);
3214 for (i
= 0; i
< nt
->FileHeader
.NumberOfSections
; i
++)
3216 BYTE flags
= VPROT_COMMITTED
;
3218 if (sec
[i
].Characteristics
& IMAGE_SCN_MEM_EXECUTE
) flags
|= VPROT_EXEC
;
3219 if (sec
[i
].Characteristics
& IMAGE_SCN_MEM_READ
) flags
|= VPROT_READ
;
3220 if (sec
[i
].Characteristics
& IMAGE_SCN_MEM_WRITE
) flags
|= VPROT_WRITE
;
3221 set_page_vprot( (char *)base
+ sec
[i
].VirtualAddress
, sec
[i
].Misc
.VirtualSize
, flags
);
3224 SERVER_START_REQ( map_builtin_view
)
3226 wine_server_add_data( req
, info
, sizeof(*info
) );
3227 wine_server_add_data( req
, nt_name
->Buffer
, nt_name
->Length
);
3228 status
= wine_server_call( req
);
3234 add_builtin_module( view
->base
, so_handle
);
3235 VIRTUAL_DEBUG_DUMP_VIEW( view
);
3236 if (is_beyond_limit( base
, size
, working_set_limit
)) working_set_limit
= address_space_limit
;
3238 else delete_view( view
);
3240 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3246 /* set some initial values in a new TEB */
3247 static TEB
*init_teb( void *ptr
, BOOL is_wow
)
3249 struct ntdll_thread_data
*thread_data
;
3252 TEB32
*teb32
= (TEB32
*)((char *)ptr
+ teb_offset
);
3256 teb32
->Peb
= PtrToUlong( (char *)peb
+ page_size
);
3257 teb32
->Tib
.Self
= PtrToUlong( teb32
);
3258 teb32
->Tib
.ExceptionList
= ~0u;
3259 teb32
->ActivationContextStackPointer
= PtrToUlong( &teb32
->ActivationContextStack
);
3260 teb32
->ActivationContextStack
.FrameListCache
.Flink
=
3261 teb32
->ActivationContextStack
.FrameListCache
.Blink
=
3262 PtrToUlong( &teb32
->ActivationContextStack
.FrameListCache
);
3263 teb32
->StaticUnicodeString
.Buffer
= PtrToUlong( teb32
->StaticUnicodeBuffer
);
3264 teb32
->StaticUnicodeString
.MaximumLength
= sizeof( teb32
->StaticUnicodeBuffer
);
3265 teb32
->GdiBatchCount
= PtrToUlong( teb64
);
3266 teb32
->WowTebOffset
= -teb_offset
;
3267 if (is_wow
) teb64
->WowTebOffset
= teb_offset
;
3270 teb64
->Peb
= PtrToUlong( (char *)peb
- page_size
);
3271 teb64
->Tib
.Self
= PtrToUlong( teb64
);
3272 teb64
->Tib
.ExceptionList
= PtrToUlong( teb32
);
3273 teb64
->ActivationContextStackPointer
= PtrToUlong( &teb64
->ActivationContextStack
);
3274 teb64
->ActivationContextStack
.FrameListCache
.Flink
=
3275 teb64
->ActivationContextStack
.FrameListCache
.Blink
=
3276 PtrToUlong( &teb64
->ActivationContextStack
.FrameListCache
);
3277 teb64
->StaticUnicodeString
.Buffer
= PtrToUlong( teb64
->StaticUnicodeBuffer
);
3278 teb64
->StaticUnicodeString
.MaximumLength
= sizeof( teb64
->StaticUnicodeBuffer
);
3279 teb64
->WowTebOffset
= teb_offset
;
3282 teb32
->GdiBatchCount
= PtrToUlong( teb64
);
3283 teb32
->WowTebOffset
= -teb_offset
;
3287 teb
->Tib
.Self
= &teb
->Tib
;
3288 teb
->Tib
.ExceptionList
= (void *)~0ul;
3289 teb
->Tib
.StackBase
= (void *)~0ul;
3290 teb
->ActivationContextStackPointer
= &teb
->ActivationContextStack
;
3291 InitializeListHead( &teb
->ActivationContextStack
.FrameListCache
);
3292 teb
->StaticUnicodeString
.Buffer
= teb
->StaticUnicodeBuffer
;
3293 teb
->StaticUnicodeString
.MaximumLength
= sizeof(teb
->StaticUnicodeBuffer
);
3294 thread_data
= (struct ntdll_thread_data
*)&teb
->GdiTebBatch
;
3295 thread_data
->request_fd
= -1;
3296 thread_data
->reply_fd
= -1;
3297 thread_data
->wait_fd
[0] = -1;
3298 thread_data
->wait_fd
[1] = -1;
3299 list_add_head( &teb_list
, &thread_data
->entry
);
3304 /***********************************************************************
3305 * virtual_alloc_first_teb
3307 TEB
*virtual_alloc_first_teb(void)
3311 unsigned int status
;
3312 SIZE_T data_size
= page_size
;
3313 SIZE_T block_size
= signal_stack_mask
+ 1;
3314 SIZE_T total
= 32 * block_size
;
3316 /* reserve space for shared user data */
3317 status
= NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&user_shared_data
, 0, &data_size
,
3318 MEM_RESERVE
| MEM_COMMIT
, PAGE_READONLY
);
3321 ERR( "wine: failed to map the shared user data: %08x\n", status
);
3325 NtAllocateVirtualMemory( NtCurrentProcess(), &teb_block
, is_win64
? 0x7fffffff : 0, &total
,
3326 MEM_RESERVE
| MEM_TOP_DOWN
, PAGE_READWRITE
);
3328 ptr
= (char *)teb_block
+ 30 * block_size
;
3329 data_size
= 2 * block_size
;
3330 NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&ptr
, 0, &data_size
, MEM_COMMIT
, PAGE_READWRITE
);
3331 peb
= (PEB
*)((char *)teb_block
+ 31 * block_size
+ (is_win64
? 0 : page_size
));
3332 teb
= init_teb( ptr
, FALSE
);
3333 pthread_key_create( &teb_key
, NULL
);
3334 pthread_setspecific( teb_key
, teb
);
3339 /***********************************************************************
3342 NTSTATUS
virtual_alloc_teb( TEB
**ret_teb
)
3347 NTSTATUS status
= STATUS_SUCCESS
;
3348 SIZE_T block_size
= signal_stack_mask
+ 1;
3350 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3353 ptr
= next_free_teb
;
3354 next_free_teb
= *(void **)ptr
;
3355 memset( ptr
, 0, teb_size
);
3361 SIZE_T total
= 32 * block_size
;
3363 if ((status
= NtAllocateVirtualMemory( NtCurrentProcess(), &ptr
, is_win64
&& is_wow64() ? 0x7fffffff : 0,
3364 &total
, MEM_RESERVE
, PAGE_READWRITE
)))
3366 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3372 ptr
= ((char *)teb_block
+ --teb_block_pos
* block_size
);
3373 NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&ptr
, 0, &block_size
,
3374 MEM_COMMIT
, PAGE_READWRITE
);
3376 *ret_teb
= teb
= init_teb( ptr
, is_wow64() );
3377 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3379 if ((status
= signal_alloc_thread( teb
)))
3381 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3382 *(void **)ptr
= next_free_teb
;
3383 next_free_teb
= ptr
;
3384 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3390 /***********************************************************************
3393 void virtual_free_teb( TEB
*teb
)
3395 struct ntdll_thread_data
*thread_data
= (struct ntdll_thread_data
*)&teb
->GdiTebBatch
;
3399 WOW_TEB
*wow_teb
= get_wow_teb( teb
);
3401 signal_free_thread( teb
);
3402 if (teb
->DeallocationStack
)
3405 NtFreeVirtualMemory( GetCurrentProcess(), &teb
->DeallocationStack
, &size
, MEM_RELEASE
);
3407 if (thread_data
->kernel_stack
)
3410 NtFreeVirtualMemory( GetCurrentProcess(), &thread_data
->kernel_stack
, &size
, MEM_RELEASE
);
3412 if (wow_teb
&& (ptr
= ULongToPtr( wow_teb
->DeallocationStack
)))
3415 NtFreeVirtualMemory( GetCurrentProcess(), &ptr
, &size
, MEM_RELEASE
);
3418 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3419 list_remove( &thread_data
->entry
);
3421 if (!is_win64
) ptr
= (char *)ptr
- teb_offset
;
3422 *(void **)ptr
= next_free_teb
;
3423 next_free_teb
= ptr
;
3424 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3428 /***********************************************************************
3429 * virtual_clear_tls_index
3431 NTSTATUS
virtual_clear_tls_index( ULONG index
)
3433 struct ntdll_thread_data
*thread_data
;
3436 if (index
< TLS_MINIMUM_AVAILABLE
)
3438 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3439 LIST_FOR_EACH_ENTRY( thread_data
, &teb_list
, struct ntdll_thread_data
, entry
)
3441 TEB
*teb
= CONTAINING_RECORD( thread_data
, TEB
, GdiTebBatch
);
3443 WOW_TEB
*wow_teb
= get_wow_teb( teb
);
3444 if (wow_teb
) wow_teb
->TlsSlots
[index
] = 0;
3447 teb
->TlsSlots
[index
] = 0;
3449 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3453 index
-= TLS_MINIMUM_AVAILABLE
;
3454 if (index
>= 8 * sizeof(peb
->TlsExpansionBitmapBits
)) return STATUS_INVALID_PARAMETER
;
3456 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3457 LIST_FOR_EACH_ENTRY( thread_data
, &teb_list
, struct ntdll_thread_data
, entry
)
3459 TEB
*teb
= CONTAINING_RECORD( thread_data
, TEB
, GdiTebBatch
);
3461 WOW_TEB
*wow_teb
= get_wow_teb( teb
);
3464 if (wow_teb
->TlsExpansionSlots
)
3465 ((ULONG
*)ULongToPtr( wow_teb
->TlsExpansionSlots
))[index
] = 0;
3469 if (teb
->TlsExpansionSlots
) teb
->TlsExpansionSlots
[index
] = 0;
3471 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3473 return STATUS_SUCCESS
;
3477 /***********************************************************************
3478 * virtual_alloc_thread_stack
3480 NTSTATUS
virtual_alloc_thread_stack( INITIAL_TEB
*stack
, ULONG_PTR limit
, SIZE_T reserve_size
,
3481 SIZE_T commit_size
, BOOL guard_page
)
3483 struct file_view
*view
;
3488 if (!reserve_size
) reserve_size
= main_image_info
.MaximumStackSize
;
3489 if (!commit_size
) commit_size
= main_image_info
.CommittedStackSize
;
3491 size
= max( reserve_size
, commit_size
);
3492 if (size
< 1024 * 1024) size
= 1024 * 1024; /* Xlib needs a large stack */
3493 size
= (size
+ 0xffff) & ~0xffff; /* round to 64K boundary */
3495 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3497 status
= map_view( &view
, NULL
, size
, 0, VPROT_READ
| VPROT_WRITE
| VPROT_COMMITTED
, limit
, 0 );
3498 if (status
!= STATUS_SUCCESS
) goto done
;
3500 #ifdef VALGRIND_STACK_REGISTER
3501 VALGRIND_STACK_REGISTER( view
->base
, (char *)view
->base
+ view
->size
);
3504 /* setup no access guard page */
3507 set_page_vprot( view
->base
, page_size
, VPROT_COMMITTED
);
3508 set_page_vprot( (char *)view
->base
+ page_size
, page_size
,
3509 VPROT_READ
| VPROT_WRITE
| VPROT_COMMITTED
| VPROT_GUARD
);
3510 mprotect_range( view
->base
, 2 * page_size
, 0, 0 );
3512 VIRTUAL_DEBUG_DUMP_VIEW( view
);
3514 /* note: limit is lower than base since the stack grows down */
3515 stack
->OldStackBase
= 0;
3516 stack
->OldStackLimit
= 0;
3517 stack
->DeallocationStack
= view
->base
;
3518 stack
->StackBase
= (char *)view
->base
+ view
->size
;
3519 stack
->StackLimit
= (char *)view
->base
+ (guard_page
? 2 * page_size
: 0);
3521 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3526 /***********************************************************************
3527 * virtual_alloc_arm64ec_map
3529 void *virtual_alloc_arm64ec_map(void)
3532 SIZE_T size
= ((ULONG_PTR
)user_space_limit
+ page_size
) >> (page_shift
+ 3); /* one bit per page */
3533 unsigned int status
= NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&arm64ec_map
, 0, &size
,
3534 MEM_COMMIT
, PAGE_READWRITE
);
3537 ERR( "failed to allocate ARM64EC map: %08x\n", status
);
3545 /***********************************************************************
3546 * virtual_map_user_shared_data
3548 void virtual_map_user_shared_data(void)
3550 static const WCHAR nameW
[] = {'\\','K','e','r','n','e','l','O','b','j','e','c','t','s',
3551 '\\','_','_','w','i','n','e','_','u','s','e','r','_','s','h','a','r','e','d','_','d','a','t','a',0};
3552 UNICODE_STRING name_str
= RTL_CONSTANT_STRING( nameW
);
3553 OBJECT_ATTRIBUTES attr
= { sizeof(attr
), 0, &name_str
};
3554 unsigned int status
;
3556 int res
, fd
, needs_close
;
3558 if ((status
= NtOpenSection( §ion
, SECTION_ALL_ACCESS
, &attr
)))
3560 ERR( "failed to open the USD section: %08x\n", status
);
3563 if ((res
= server_get_unix_fd( section
, 0, &fd
, &needs_close
, NULL
, NULL
)) ||
3564 (user_shared_data
!= mmap( user_shared_data
, page_size
, PROT_READ
, MAP_SHARED
|MAP_FIXED
, fd
, 0 )))
3566 ERR( "failed to remap the process USD: %d\n", res
);
3569 if (needs_close
) close( fd
);
3574 struct thread_stack_info
3583 /***********************************************************************
3584 * is_inside_thread_stack
3586 static BOOL
is_inside_thread_stack( void *ptr
, struct thread_stack_info
*stack
)
3588 TEB
*teb
= NtCurrentTeb();
3589 WOW_TEB
*wow_teb
= get_wow_teb( teb
);
3591 stack
->start
= teb
->DeallocationStack
;
3592 stack
->limit
= teb
->Tib
.StackLimit
;
3593 stack
->end
= teb
->Tib
.StackBase
;
3594 stack
->guaranteed
= max( teb
->GuaranteedStackBytes
, page_size
* (is_win64
? 2 : 1) );
3595 stack
->is_wow
= FALSE
;
3596 if ((char *)ptr
> stack
->start
&& (char *)ptr
<= stack
->end
) return TRUE
;
3598 if (!wow_teb
) return FALSE
;
3599 stack
->start
= ULongToPtr( wow_teb
->DeallocationStack
);
3600 stack
->limit
= ULongToPtr( wow_teb
->Tib
.StackLimit
);
3601 stack
->end
= ULongToPtr( wow_teb
->Tib
.StackBase
);
3602 stack
->guaranteed
= max( wow_teb
->GuaranteedStackBytes
, page_size
* (is_win64
? 1 : 2) );
3603 stack
->is_wow
= TRUE
;
3604 return ((char *)ptr
> stack
->start
&& (char *)ptr
<= stack
->end
);
3608 /***********************************************************************
3611 static NTSTATUS
grow_thread_stack( char *page
, struct thread_stack_info
*stack_info
)
3615 set_page_vprot_bits( page
, page_size
, 0, VPROT_GUARD
);
3616 mprotect_range( page
, page_size
, 0, 0 );
3617 if (page
>= stack_info
->start
+ page_size
+ stack_info
->guaranteed
)
3619 set_page_vprot_bits( page
- page_size
, page_size
, VPROT_COMMITTED
| VPROT_GUARD
, 0 );
3620 mprotect_range( page
- page_size
, page_size
, 0, 0 );
3622 else /* inside guaranteed space -> overflow exception */
3624 page
= stack_info
->start
+ page_size
;
3625 set_page_vprot_bits( page
, stack_info
->guaranteed
, VPROT_COMMITTED
, VPROT_GUARD
);
3626 mprotect_range( page
, stack_info
->guaranteed
, 0, 0 );
3627 ret
= STATUS_STACK_OVERFLOW
;
3629 if (stack_info
->is_wow
)
3631 WOW_TEB
*wow_teb
= get_wow_teb( NtCurrentTeb() );
3632 wow_teb
->Tib
.StackLimit
= PtrToUlong( page
);
3634 else NtCurrentTeb()->Tib
.StackLimit
= page
;
3639 /***********************************************************************
3640 * virtual_handle_fault
3642 NTSTATUS
virtual_handle_fault( void *addr
, DWORD err
, void *stack
)
3644 NTSTATUS ret
= STATUS_ACCESS_VIOLATION
;
3645 char *page
= ROUND_ADDR( addr
, page_mask
);
3648 mutex_lock( &virtual_mutex
); /* no need for signal masking inside signal handler */
3649 vprot
= get_page_vprot( page
);
3650 if (!is_inside_signal_stack( stack
) && (vprot
& VPROT_GUARD
))
3652 struct thread_stack_info stack_info
;
3653 if (!is_inside_thread_stack( page
, &stack_info
))
3655 set_page_vprot_bits( page
, page_size
, 0, VPROT_GUARD
);
3656 mprotect_range( page
, page_size
, 0, 0 );
3657 ret
= STATUS_GUARD_PAGE_VIOLATION
;
3659 else ret
= grow_thread_stack( page
, &stack_info
);
3661 else if (err
& EXCEPTION_WRITE_FAULT
)
3663 if (vprot
& VPROT_WRITEWATCH
)
3665 set_page_vprot_bits( page
, page_size
, 0, VPROT_WRITEWATCH
);
3666 mprotect_range( page
, page_size
, 0, 0 );
3668 /* ignore fault if page is writable now */
3669 if (get_unix_prot( get_page_vprot( page
)) & PROT_WRITE
)
3671 if ((vprot
& VPROT_WRITEWATCH
) || is_write_watch_range( page
, page_size
))
3672 ret
= STATUS_SUCCESS
;
3675 mutex_unlock( &virtual_mutex
);
3680 /***********************************************************************
3681 * virtual_setup_exception
3683 void *virtual_setup_exception( void *stack_ptr
, size_t size
, EXCEPTION_RECORD
*rec
)
3685 char *stack
= stack_ptr
;
3686 struct thread_stack_info stack_info
;
3688 if (!is_inside_thread_stack( stack
, &stack_info
))
3690 if (is_inside_signal_stack( stack
))
3692 ERR( "nested exception on signal stack addr %p stack %p\n", rec
->ExceptionAddress
, stack
);
3695 WARN( "exception outside of stack limits addr %p stack %p (%p-%p-%p)\n",
3696 rec
->ExceptionAddress
, stack
, NtCurrentTeb()->DeallocationStack
,
3697 NtCurrentTeb()->Tib
.StackLimit
, NtCurrentTeb()->Tib
.StackBase
);
3698 return stack
- size
;
3703 if (stack
< stack_info
.start
+ 4096)
3705 /* stack overflow on last page, unrecoverable */
3706 UINT diff
= stack_info
.start
+ 4096 - stack
;
3707 ERR( "stack overflow %u bytes addr %p stack %p (%p-%p-%p)\n",
3708 diff
, rec
->ExceptionAddress
, stack
, stack_info
.start
, stack_info
.limit
, stack_info
.end
);
3711 else if (stack
< stack_info
.limit
)
3713 mutex_lock( &virtual_mutex
); /* no need for signal masking inside signal handler */
3714 if ((get_page_vprot( stack
) & VPROT_GUARD
) &&
3715 grow_thread_stack( ROUND_ADDR( stack
, page_mask
), &stack_info
))
3717 rec
->ExceptionCode
= STATUS_STACK_OVERFLOW
;
3718 rec
->NumberParameters
= 0;
3720 mutex_unlock( &virtual_mutex
);
3722 #if defined(VALGRIND_MAKE_MEM_UNDEFINED)
3723 VALGRIND_MAKE_MEM_UNDEFINED( stack
, size
);
3724 #elif defined(VALGRIND_MAKE_WRITABLE)
3725 VALGRIND_MAKE_WRITABLE( stack
, size
);
3731 /***********************************************************************
3732 * check_write_access
3734 * Check if the memory range is writable, temporarily disabling write watches if necessary.
3736 static NTSTATUS
check_write_access( void *base
, size_t size
, BOOL
*has_write_watch
)
3739 char *addr
= ROUND_ADDR( base
, page_mask
);
3741 size
= ROUND_SIZE( base
, size
);
3742 for (i
= 0; i
< size
; i
+= page_size
)
3744 BYTE vprot
= get_page_vprot( addr
+ i
);
3745 if (vprot
& VPROT_WRITEWATCH
) *has_write_watch
= TRUE
;
3746 if (!(get_unix_prot( vprot
& ~VPROT_WRITEWATCH
) & PROT_WRITE
))
3747 return STATUS_INVALID_USER_BUFFER
;
3749 if (*has_write_watch
)
3750 mprotect_range( addr
, size
, 0, VPROT_WRITEWATCH
); /* temporarily enable write access */
3751 return STATUS_SUCCESS
;
3755 /***********************************************************************
3756 * virtual_locked_server_call
3758 unsigned int virtual_locked_server_call( void *req_ptr
)
3760 struct __server_request_info
* const req
= req_ptr
;
3762 void *addr
= req
->reply_data
;
3763 data_size_t size
= req
->u
.req
.request_header
.reply_size
;
3764 BOOL has_write_watch
= FALSE
;
3765 unsigned int ret
= STATUS_ACCESS_VIOLATION
;
3767 if (!size
) return wine_server_call( req_ptr
);
3769 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3770 if (!(ret
= check_write_access( addr
, size
, &has_write_watch
)))
3772 ret
= server_call_unlocked( req
);
3773 if (has_write_watch
) update_write_watches( addr
, size
, wine_server_reply_size( req
));
3775 else memset( &req
->u
.reply
, 0, sizeof(req
->u
.reply
) );
3776 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3781 /***********************************************************************
3782 * virtual_locked_read
3784 ssize_t
virtual_locked_read( int fd
, void *addr
, size_t size
)
3787 BOOL has_write_watch
= FALSE
;
3790 ssize_t ret
= read( fd
, addr
, size
);
3791 if (ret
!= -1 || errno
!= EFAULT
) return ret
;
3793 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3794 if (!check_write_access( addr
, size
, &has_write_watch
))
3796 ret
= read( fd
, addr
, size
);
3798 if (has_write_watch
) update_write_watches( addr
, size
, max( 0, ret
));
3800 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3806 /***********************************************************************
3807 * virtual_locked_pread
3809 ssize_t
virtual_locked_pread( int fd
, void *addr
, size_t size
, off_t offset
)
3812 BOOL has_write_watch
= FALSE
;
3815 ssize_t ret
= pread( fd
, addr
, size
, offset
);
3816 if (ret
!= -1 || errno
!= EFAULT
) return ret
;
3818 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3819 if (!check_write_access( addr
, size
, &has_write_watch
))
3821 ret
= pread( fd
, addr
, size
, offset
);
3823 if (has_write_watch
) update_write_watches( addr
, size
, max( 0, ret
));
3825 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3831 /***********************************************************************
3832 * virtual_locked_recvmsg
3834 ssize_t
virtual_locked_recvmsg( int fd
, struct msghdr
*hdr
, int flags
)
3838 BOOL has_write_watch
= FALSE
;
3841 ssize_t ret
= recvmsg( fd
, hdr
, flags
);
3842 if (ret
!= -1 || errno
!= EFAULT
) return ret
;
3844 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3845 for (i
= 0; i
< hdr
->msg_iovlen
; i
++)
3846 if (check_write_access( hdr
->msg_iov
[i
].iov_base
, hdr
->msg_iov
[i
].iov_len
, &has_write_watch
))
3848 if (i
== hdr
->msg_iovlen
)
3850 ret
= recvmsg( fd
, hdr
, flags
);
3853 if (has_write_watch
)
3854 while (i
--) update_write_watches( hdr
->msg_iov
[i
].iov_base
, hdr
->msg_iov
[i
].iov_len
, 0 );
3856 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3862 /***********************************************************************
3863 * virtual_is_valid_code_address
3865 BOOL
virtual_is_valid_code_address( const void *addr
, SIZE_T size
)
3867 struct file_view
*view
;
3871 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3872 if ((view
= find_view( addr
, size
)))
3873 ret
= !(view
->protect
& VPROT_SYSTEM
); /* system views are not visible to the app */
3874 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3879 /***********************************************************************
3880 * virtual_check_buffer_for_read
3882 * Check if a memory buffer can be read, triggering page faults if needed for DIB section access.
3884 BOOL
virtual_check_buffer_for_read( const void *ptr
, SIZE_T size
)
3886 if (!size
) return TRUE
;
3887 if (!ptr
) return FALSE
;
3891 volatile const char *p
= ptr
;
3892 char dummy
__attribute__((unused
));
3893 SIZE_T count
= size
;
3895 while (count
> page_size
)
3902 dummy
= p
[count
- 1];
3913 /***********************************************************************
3914 * virtual_check_buffer_for_write
3916 * Check if a memory buffer can be written to, triggering page faults if needed for write watches.
3918 BOOL
virtual_check_buffer_for_write( void *ptr
, SIZE_T size
)
3920 if (!size
) return TRUE
;
3921 if (!ptr
) return FALSE
;
3925 volatile char *p
= ptr
;
3926 SIZE_T count
= size
;
3928 while (count
> page_size
)
3946 /***********************************************************************
3947 * virtual_uninterrupted_read_memory
3949 * Similar to NtReadVirtualMemory, but without wineserver calls. Moreover
3950 * permissions are checked before accessing each page, to ensure that no
3951 * exceptions can happen.
3953 SIZE_T
virtual_uninterrupted_read_memory( const void *addr
, void *buffer
, SIZE_T size
)
3955 struct file_view
*view
;
3957 SIZE_T bytes_read
= 0;
3959 if (!size
) return 0;
3961 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3962 if ((view
= find_view( addr
, size
)))
3964 if (!(view
->protect
& VPROT_SYSTEM
))
3966 while (bytes_read
< size
&& (get_unix_prot( get_page_vprot( addr
)) & PROT_READ
))
3968 SIZE_T block_size
= min( size
- bytes_read
, page_size
- ((UINT_PTR
)addr
& page_mask
) );
3969 memcpy( buffer
, addr
, block_size
);
3971 addr
= (const void *)((const char *)addr
+ block_size
);
3972 buffer
= (void *)((char *)buffer
+ block_size
);
3973 bytes_read
+= block_size
;
3977 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3982 /***********************************************************************
3983 * virtual_uninterrupted_write_memory
3985 * Similar to NtWriteVirtualMemory, but without wineserver calls. Moreover
3986 * permissions are checked before accessing each page, to ensure that no
3987 * exceptions can happen.
3989 NTSTATUS
virtual_uninterrupted_write_memory( void *addr
, const void *buffer
, SIZE_T size
)
3991 BOOL has_write_watch
= FALSE
;
3995 if (!size
) return STATUS_SUCCESS
;
3997 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3998 if (!(ret
= check_write_access( addr
, size
, &has_write_watch
)))
4000 memcpy( addr
, buffer
, size
);
4001 if (has_write_watch
) update_write_watches( addr
, size
, size
);
4003 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4008 /***********************************************************************
4009 * virtual_set_force_exec
4011 * Whether to force exec prot on all views.
4013 void virtual_set_force_exec( BOOL enable
)
4015 struct file_view
*view
;
4018 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4019 if (!force_exec_prot
!= !enable
) /* change all existing views */
4021 force_exec_prot
= enable
;
4023 WINE_RB_FOR_EACH_ENTRY( view
, &views_tree
, struct file_view
, entry
)
4025 /* file mappings are always accessible */
4026 BYTE commit
= is_view_valloc( view
) ? 0 : VPROT_COMMITTED
;
4028 mprotect_range( view
->base
, view
->size
, commit
, 0 );
4031 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4040 /* free reserved areas above the limit; callback for mmap_enum_reserved_areas */
4041 static int free_reserved_memory( void *base
, SIZE_T size
, void *arg
)
4043 struct free_range
*range
= arg
;
4045 if ((char *)base
>= range
->limit
) return 0;
4046 if ((char *)base
+ size
<= range
->base
) return 0;
4047 if ((char *)base
< range
->base
)
4049 size
-= range
->base
- (char *)base
;
4052 if ((char *)base
+ size
> range
->limit
) size
= range
->limit
- (char *)base
;
4053 remove_reserved_area( base
, size
);
4054 return 1; /* stop enumeration since the list has changed */
4057 /***********************************************************************
4058 * virtual_release_address_space
4060 * Release some address space once we have loaded and initialized the app.
4062 static void virtual_release_address_space(void)
4064 struct free_range range
;
4066 range
.base
= (char *)0x82000000;
4067 range
.limit
= get_wow_user_space_limit();
4069 if (range
.limit
> (char *)0xfffff000) return; /* 64-bit limit, nothing to do */
4071 if (range
.limit
> range
.base
)
4073 while (mmap_enum_reserved_areas( free_reserved_memory
, &range
, 1 )) /* nothing */;
4075 /* On macOS, we still want to free some of low memory, for OpenGL resources */
4076 range
.base
= (char *)0x40000000;
4081 else range
.base
= (char *)0x20000000;
4083 range
.limit
= (char *)0x7f000000;
4084 while (mmap_enum_reserved_areas( free_reserved_memory
, &range
, 0 )) /* nothing */;
4088 /***********************************************************************
4089 * virtual_set_large_address_space
4091 * Enable use of a large address space when allowed by the application.
4093 void virtual_set_large_address_space(void)
4095 /* no large address space on win9x */
4096 if (peb
->OSPlatformId
!= VER_PLATFORM_WIN32_NT
) return;
4098 user_space_limit
= working_set_limit
= address_space_limit
;
4102 /***********************************************************************
4103 * allocate_virtual_memory
4105 * NtAllocateVirtualMemory[Ex] implementation.
4107 static NTSTATUS
allocate_virtual_memory( void **ret
, SIZE_T
*size_ptr
, ULONG type
, ULONG protect
,
4108 ULONG_PTR limit
, ULONG_PTR align
, ULONG attributes
)
4112 BOOL is_dos_memory
= FALSE
;
4113 struct file_view
*view
;
4115 SIZE_T size
= *size_ptr
;
4116 NTSTATUS status
= STATUS_SUCCESS
;
4118 /* Round parameters to a page boundary */
4120 if (is_beyond_limit( 0, size
, working_set_limit
)) return STATUS_WORKING_SET_LIMIT_RANGE
;
4124 if (type
& MEM_RESERVE
&& !(type
& MEM_REPLACE_PLACEHOLDER
)) /* Round down to 64k boundary */
4125 base
= ROUND_ADDR( *ret
, granularity_mask
);
4127 base
= ROUND_ADDR( *ret
, page_mask
);
4128 size
= (((UINT_PTR
)*ret
+ size
+ page_mask
) & ~page_mask
) - (UINT_PTR
)base
;
4130 /* disallow low 64k, wrap-around and kernel space */
4131 if (((char *)base
< (char *)0x10000) ||
4132 ((char *)base
+ size
< (char *)base
) ||
4133 is_beyond_limit( base
, size
, address_space_limit
))
4135 /* address 1 is magic to mean DOS area */
4136 if (!base
&& *ret
== (void *)1 && size
== 0x110000) is_dos_memory
= TRUE
;
4137 else return STATUS_INVALID_PARAMETER
;
4143 size
= (size
+ page_mask
) & ~page_mask
;
4146 /* Compute the alloc type flags */
4148 if (!(type
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
))
4149 || (type
& MEM_REPLACE_PLACEHOLDER
&& !(type
& MEM_RESERVE
)))
4151 WARN("called with wrong alloc type flags (%08x) !\n", (int)type
);
4152 return STATUS_INVALID_PARAMETER
;
4155 if (type
& MEM_RESERVE_PLACEHOLDER
&& (protect
!= PAGE_NOACCESS
)) return STATUS_INVALID_PARAMETER
;
4156 if (!arm64ec_map
&& (attributes
& MEM_EXTENDED_PARAMETER_EC_CODE
)) return STATUS_INVALID_PARAMETER
;
4158 /* Reserve the memory */
4160 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4162 if ((type
& MEM_RESERVE
) || !base
)
4164 if (!(status
= get_vprot_flags( protect
, &vprot
, FALSE
)))
4166 if (type
& MEM_COMMIT
) vprot
|= VPROT_COMMITTED
;
4167 if (type
& MEM_WRITE_WATCH
) vprot
|= VPROT_WRITEWATCH
;
4168 if (type
& MEM_RESERVE_PLACEHOLDER
) vprot
|= VPROT_PLACEHOLDER
| VPROT_FREE_PLACEHOLDER
;
4169 if (protect
& PAGE_NOCACHE
) vprot
|= SEC_NOCACHE
;
4171 if (vprot
& VPROT_WRITECOPY
) status
= STATUS_INVALID_PAGE_PROTECTION
;
4172 else if (is_dos_memory
) status
= allocate_dos_memory( &view
, vprot
);
4173 else status
= map_view( &view
, base
, size
, type
, vprot
, limit
,
4174 align
? align
- 1 : granularity_mask
);
4176 if (status
== STATUS_SUCCESS
) base
= view
->base
;
4179 else if (type
& MEM_RESET
)
4181 if (!(view
= find_view( base
, size
))) status
= STATUS_NOT_MAPPED_VIEW
;
4182 else madvise( base
, size
, MADV_DONTNEED
);
4184 else /* commit the pages */
4186 if (!(view
= find_view( base
, size
))) status
= STATUS_NOT_MAPPED_VIEW
;
4187 else if (view
->protect
& SEC_FILE
) status
= STATUS_ALREADY_COMMITTED
;
4188 else if (view
->protect
& VPROT_FREE_PLACEHOLDER
) status
= STATUS_CONFLICTING_ADDRESSES
;
4189 else if (!(status
= set_protection( view
, base
, size
, protect
)) && (view
->protect
& SEC_RESERVE
))
4191 SERVER_START_REQ( add_mapping_committed_range
)
4193 req
->base
= wine_server_client_ptr( view
->base
);
4194 req
->offset
= (char *)base
- (char *)view
->base
;
4196 wine_server_call( req
);
4202 if (!status
&& (attributes
& MEM_EXTENDED_PARAMETER_EC_CODE
)) set_arm64ec_range( base
, size
);
4204 if (!status
) VIRTUAL_DEBUG_DUMP_VIEW( view
);
4206 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4208 if (status
== STATUS_SUCCESS
)
4217 /***********************************************************************
4218 * NtAllocateVirtualMemory (NTDLL.@)
4219 * ZwAllocateVirtualMemory (NTDLL.@)
4221 NTSTATUS WINAPI
NtAllocateVirtualMemory( HANDLE process
, PVOID
*ret
, ULONG_PTR zero_bits
,
4222 SIZE_T
*size_ptr
, ULONG type
, ULONG protect
)
4224 static const ULONG type_mask
= MEM_COMMIT
| MEM_RESERVE
| MEM_TOP_DOWN
| MEM_WRITE_WATCH
| MEM_RESET
;
4227 TRACE("%p %p %08lx %x %08x\n", process
, *ret
, *size_ptr
, (int)type
, (int)protect
);
4229 if (!*size_ptr
) return STATUS_INVALID_PARAMETER
;
4230 if (zero_bits
> 21 && zero_bits
< 32) return STATUS_INVALID_PARAMETER_3
;
4231 if (zero_bits
> 32 && zero_bits
< granularity_mask
) return STATUS_INVALID_PARAMETER_3
;
4233 if (!is_old_wow64() && zero_bits
>= 32) return STATUS_INVALID_PARAMETER_3
;
4235 if (type
& ~type_mask
) return STATUS_INVALID_PARAMETER
;
4237 if (process
!= NtCurrentProcess())
4240 apc_result_t result
;
4241 unsigned int status
;
4243 memset( &call
, 0, sizeof(call
) );
4245 call
.virtual_alloc
.type
= APC_VIRTUAL_ALLOC
;
4246 call
.virtual_alloc
.addr
= wine_server_client_ptr( *ret
);
4247 call
.virtual_alloc
.size
= *size_ptr
;
4248 call
.virtual_alloc
.zero_bits
= zero_bits
;
4249 call
.virtual_alloc
.op_type
= type
;
4250 call
.virtual_alloc
.prot
= protect
;
4251 status
= server_queue_process_apc( process
, &call
, &result
);
4252 if (status
!= STATUS_SUCCESS
) return status
;
4254 if (result
.virtual_alloc
.status
== STATUS_SUCCESS
)
4256 *ret
= wine_server_get_ptr( result
.virtual_alloc
.addr
);
4257 *size_ptr
= result
.virtual_alloc
.size
;
4259 return result
.virtual_alloc
.status
;
4263 limit
= get_zero_bits_limit( zero_bits
);
4267 return allocate_virtual_memory( ret
, size_ptr
, type
, protect
, limit
, 0, 0 );
4271 static NTSTATUS
get_extended_params( const MEM_EXTENDED_PARAMETER
*parameters
, ULONG count
,
4272 ULONG_PTR
*limit
, ULONG_PTR
*align
, ULONG
*attributes
,
4275 ULONG i
, present
= 0;
4277 if (count
&& !parameters
) return STATUS_INVALID_PARAMETER
;
4279 for (i
= 0; i
< count
; ++i
)
4281 if (parameters
[i
].Type
>= 32) return STATUS_INVALID_PARAMETER
;
4282 if (present
& (1u << parameters
[i
].Type
)) return STATUS_INVALID_PARAMETER
;
4283 present
|= 1u << parameters
[i
].Type
;
4285 switch (parameters
[i
].Type
)
4287 case MemExtendedParameterAddressRequirements
:
4289 MEM_ADDRESS_REQUIREMENTS
*r
= parameters
[i
].Pointer
;
4291 if (r
->LowestStartingAddress
)
4292 FIXME( "Not supported requirements LowestStartingAddress %p, Alignment %p.\n",
4293 r
->LowestStartingAddress
, (void *)r
->Alignment
);
4297 if ((r
->Alignment
& (r
->Alignment
- 1)) || r
->Alignment
- 1 < granularity_mask
)
4299 WARN( "Invalid alignment %lu.\n", r
->Alignment
);
4300 return STATUS_INVALID_PARAMETER
;
4302 *align
= r
->Alignment
;
4304 if (r
->HighestEndingAddress
)
4306 *limit
= (ULONG_PTR
)r
->HighestEndingAddress
;
4307 if (*limit
> (ULONG_PTR
)user_space_limit
|| ((*limit
+ 1) & (page_mask
- 1)))
4309 WARN( "Invalid limit %p.\n", r
->HighestEndingAddress
);
4310 return STATUS_INVALID_PARAMETER
;
4316 case MemExtendedParameterAttributeFlags
:
4317 *attributes
= parameters
[i
].ULong
;
4320 case MemExtendedParameterImageMachine
:
4321 *machine
= parameters
[i
].ULong
;
4324 case MemExtendedParameterNumaNode
:
4325 case MemExtendedParameterPartitionHandle
:
4326 case MemExtendedParameterUserPhysicalHandle
:
4327 FIXME( "Parameter type %d is not supported.\n", parameters
[i
].Type
);
4331 WARN( "Invalid parameter type %u\n", parameters
[i
].Type
);
4332 return STATUS_INVALID_PARAMETER
;
4335 return STATUS_SUCCESS
;
4339 /***********************************************************************
4340 * NtAllocateVirtualMemoryEx (NTDLL.@)
4341 * ZwAllocateVirtualMemoryEx (NTDLL.@)
4343 NTSTATUS WINAPI
NtAllocateVirtualMemoryEx( HANDLE process
, PVOID
*ret
, SIZE_T
*size_ptr
, ULONG type
,
4344 ULONG protect
, MEM_EXTENDED_PARAMETER
*parameters
,
4347 static const ULONG type_mask
= MEM_COMMIT
| MEM_RESERVE
| MEM_TOP_DOWN
| MEM_WRITE_WATCH
4348 | MEM_RESET
| MEM_RESERVE_PLACEHOLDER
| MEM_REPLACE_PLACEHOLDER
;
4349 ULONG_PTR limit
= 0;
4350 ULONG_PTR align
= 0;
4351 ULONG attributes
= 0;
4353 unsigned int status
;
4355 TRACE( "%p %p %08lx %x %08x %p %u\n",
4356 process
, *ret
, *size_ptr
, (int)type
, (int)protect
, parameters
, (int)count
);
4358 status
= get_extended_params( parameters
, count
, &limit
, &align
, &attributes
, &machine
);
4359 if (status
) return status
;
4361 if (type
& ~type_mask
) return STATUS_INVALID_PARAMETER
;
4362 if (*ret
&& (align
|| limit
)) return STATUS_INVALID_PARAMETER
;
4363 if (!*size_ptr
) return STATUS_INVALID_PARAMETER
;
4365 if (process
!= NtCurrentProcess())
4368 apc_result_t result
;
4370 memset( &call
, 0, sizeof(call
) );
4372 call
.virtual_alloc_ex
.type
= APC_VIRTUAL_ALLOC_EX
;
4373 call
.virtual_alloc_ex
.addr
= wine_server_client_ptr( *ret
);
4374 call
.virtual_alloc_ex
.size
= *size_ptr
;
4375 call
.virtual_alloc_ex
.limit
= limit
;
4376 call
.virtual_alloc_ex
.align
= align
;
4377 call
.virtual_alloc_ex
.op_type
= type
;
4378 call
.virtual_alloc_ex
.prot
= protect
;
4379 call
.virtual_alloc_ex
.attributes
= attributes
;
4380 status
= server_queue_process_apc( process
, &call
, &result
);
4381 if (status
!= STATUS_SUCCESS
) return status
;
4383 if (result
.virtual_alloc_ex
.status
== STATUS_SUCCESS
)
4385 *ret
= wine_server_get_ptr( result
.virtual_alloc_ex
.addr
);
4386 *size_ptr
= result
.virtual_alloc_ex
.size
;
4388 return result
.virtual_alloc_ex
.status
;
4391 return allocate_virtual_memory( ret
, size_ptr
, type
, protect
, limit
, align
, attributes
);
4395 /***********************************************************************
4396 * NtFreeVirtualMemory (NTDLL.@)
4397 * ZwFreeVirtualMemory (NTDLL.@)
4399 NTSTATUS WINAPI
NtFreeVirtualMemory( HANDLE process
, PVOID
*addr_ptr
, SIZE_T
*size_ptr
, ULONG type
)
4401 struct file_view
*view
;
4404 unsigned int status
= STATUS_SUCCESS
;
4405 LPVOID addr
= *addr_ptr
;
4406 SIZE_T size
= *size_ptr
;
4408 TRACE("%p %p %08lx %x\n", process
, addr
, size
, (int)type
);
4410 if (process
!= NtCurrentProcess())
4413 apc_result_t result
;
4415 memset( &call
, 0, sizeof(call
) );
4417 call
.virtual_free
.type
= APC_VIRTUAL_FREE
;
4418 call
.virtual_free
.addr
= wine_server_client_ptr( addr
);
4419 call
.virtual_free
.size
= size
;
4420 call
.virtual_free
.op_type
= type
;
4421 status
= server_queue_process_apc( process
, &call
, &result
);
4422 if (status
!= STATUS_SUCCESS
) return status
;
4424 if (result
.virtual_free
.status
== STATUS_SUCCESS
)
4426 *addr_ptr
= wine_server_get_ptr( result
.virtual_free
.addr
);
4427 *size_ptr
= result
.virtual_free
.size
;
4429 return result
.virtual_free
.status
;
4432 /* Fix the parameters */
4434 if (size
) size
= ROUND_SIZE( addr
, size
);
4435 base
= ROUND_ADDR( addr
, page_mask
);
4437 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4439 /* avoid freeing the DOS area when a broken app passes a NULL pointer */
4442 /* address 1 is magic to mean release reserved space */
4443 if (addr
== (void *)1 && !size
&& type
== MEM_RELEASE
) virtual_release_address_space();
4444 else status
= STATUS_INVALID_PARAMETER
;
4446 else if (!(view
= find_view( base
, 0 ))) status
= STATUS_MEMORY_NOT_ALLOCATED
;
4447 else if (!is_view_valloc( view
)) status
= STATUS_INVALID_PARAMETER
;
4448 else if (!size
&& base
!= view
->base
) status
= STATUS_FREE_VM_NOT_AT_BASE
;
4449 else if ((char *)view
->base
+ view
->size
- base
< size
) status
= STATUS_UNABLE_TO_FREE_VM
;
4453 status
= decommit_pages( view
, base
- (char *)view
->base
, size
);
4456 if (!size
) size
= view
->size
;
4457 status
= free_pages( view
, base
, size
);
4459 case MEM_RELEASE
| MEM_PRESERVE_PLACEHOLDER
:
4460 status
= free_pages_preserve_placeholder( view
, base
, size
);
4463 status
= STATUS_INVALID_PARAMETER
;
4467 if (status
== STATUS_SUCCESS
)
4472 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4477 /***********************************************************************
4478 * NtProtectVirtualMemory (NTDLL.@)
4479 * ZwProtectVirtualMemory (NTDLL.@)
4481 NTSTATUS WINAPI
NtProtectVirtualMemory( HANDLE process
, PVOID
*addr_ptr
, SIZE_T
*size_ptr
,
4482 ULONG new_prot
, ULONG
*old_prot
)
4484 struct file_view
*view
;
4486 unsigned int status
= STATUS_SUCCESS
;
4489 SIZE_T size
= *size_ptr
;
4490 LPVOID addr
= *addr_ptr
;
4493 TRACE("%p %p %08lx %08x\n", process
, addr
, size
, (int)new_prot
);
4496 return STATUS_ACCESS_VIOLATION
;
4498 if (process
!= NtCurrentProcess())
4501 apc_result_t result
;
4503 memset( &call
, 0, sizeof(call
) );
4505 call
.virtual_protect
.type
= APC_VIRTUAL_PROTECT
;
4506 call
.virtual_protect
.addr
= wine_server_client_ptr( addr
);
4507 call
.virtual_protect
.size
= size
;
4508 call
.virtual_protect
.prot
= new_prot
;
4509 status
= server_queue_process_apc( process
, &call
, &result
);
4510 if (status
!= STATUS_SUCCESS
) return status
;
4512 if (result
.virtual_protect
.status
== STATUS_SUCCESS
)
4514 *addr_ptr
= wine_server_get_ptr( result
.virtual_protect
.addr
);
4515 *size_ptr
= result
.virtual_protect
.size
;
4516 *old_prot
= result
.virtual_protect
.prot
;
4518 return result
.virtual_protect
.status
;
4521 /* Fix the parameters */
4523 size
= ROUND_SIZE( addr
, size
);
4524 base
= ROUND_ADDR( addr
, page_mask
);
4526 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4528 if ((view
= find_view( base
, size
)))
4530 /* Make sure all the pages are committed */
4531 if (get_committed_size( view
, base
, &vprot
, VPROT_COMMITTED
) >= size
&& (vprot
& VPROT_COMMITTED
))
4533 old
= get_win32_prot( vprot
, view
->protect
);
4534 status
= set_protection( view
, base
, size
, new_prot
);
4536 else status
= STATUS_NOT_COMMITTED
;
4538 else status
= STATUS_INVALID_PARAMETER
;
4540 if (!status
) VIRTUAL_DEBUG_DUMP_VIEW( view
);
4542 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4544 if (status
== STATUS_SUCCESS
)
4554 /* retrieve state for a free memory area; callback for mmap_enum_reserved_areas */
4555 static int get_free_mem_state_callback( void *start
, SIZE_T size
, void *arg
)
4557 MEMORY_BASIC_INFORMATION
*info
= arg
;
4558 void *end
= (char *)start
+ size
;
4560 if ((char *)info
->BaseAddress
+ info
->RegionSize
<= (char *)start
) return 0;
4562 if (info
->BaseAddress
>= end
)
4564 if (info
->AllocationBase
< end
) info
->AllocationBase
= end
;
4568 if (info
->BaseAddress
>= start
|| start
<= address_space_start
)
4570 /* it's a real free area */
4571 info
->State
= MEM_FREE
;
4572 info
->Protect
= PAGE_NOACCESS
;
4573 info
->AllocationBase
= 0;
4574 info
->AllocationProtect
= 0;
4576 if ((char *)info
->BaseAddress
+ info
->RegionSize
> (char *)end
)
4577 info
->RegionSize
= (char *)end
- (char *)info
->BaseAddress
;
4579 else /* outside of the reserved area, pretend it's allocated */
4581 info
->RegionSize
= (char *)start
- (char *)info
->BaseAddress
;
4583 info
->State
= MEM_RESERVE
;
4584 info
->Protect
= PAGE_NOACCESS
;
4585 info
->AllocationProtect
= PAGE_NOACCESS
;
4586 info
->Type
= MEM_PRIVATE
;
4588 info
->State
= MEM_FREE
;
4589 info
->Protect
= PAGE_NOACCESS
;
4590 info
->AllocationBase
= 0;
4591 info
->AllocationProtect
= 0;
4598 static unsigned int fill_basic_memory_info( const void *addr
, MEMORY_BASIC_INFORMATION
*info
)
4600 char *base
, *alloc_base
= 0, *alloc_end
= working_set_limit
;
4601 struct wine_rb_entry
*ptr
;
4602 struct file_view
*view
;
4605 base
= ROUND_ADDR( addr
, page_mask
);
4607 if (is_beyond_limit( base
, 1, working_set_limit
)) return STATUS_INVALID_PARAMETER
;
4609 /* Find the view containing the address */
4611 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4612 ptr
= views_tree
.root
;
4615 view
= WINE_RB_ENTRY_VALUE( ptr
, struct file_view
, entry
);
4616 if ((char *)view
->base
> base
)
4618 alloc_end
= view
->base
;
4621 else if ((char *)view
->base
+ view
->size
<= base
)
4623 alloc_base
= (char *)view
->base
+ view
->size
;
4628 alloc_base
= view
->base
;
4629 alloc_end
= (char *)view
->base
+ view
->size
;
4634 /* Fill the info structure */
4636 info
->AllocationBase
= alloc_base
;
4637 info
->BaseAddress
= base
;
4638 info
->RegionSize
= alloc_end
- base
;
4642 if (!mmap_enum_reserved_areas( get_free_mem_state_callback
, info
, 0 ))
4644 /* not in a reserved area at all, pretend it's allocated */
4646 if (base
>= (char *)address_space_start
)
4648 info
->State
= MEM_RESERVE
;
4649 info
->Protect
= PAGE_NOACCESS
;
4650 info
->AllocationProtect
= PAGE_NOACCESS
;
4651 info
->Type
= MEM_PRIVATE
;
4656 info
->State
= MEM_FREE
;
4657 info
->Protect
= PAGE_NOACCESS
;
4658 info
->AllocationBase
= 0;
4659 info
->AllocationProtect
= 0;
4668 info
->RegionSize
= get_committed_size( view
, base
, &vprot
, ~VPROT_WRITEWATCH
);
4669 info
->State
= (vprot
& VPROT_COMMITTED
) ? MEM_COMMIT
: MEM_RESERVE
;
4670 info
->Protect
= (vprot
& VPROT_COMMITTED
) ? get_win32_prot( vprot
, view
->protect
) : 0;
4671 info
->AllocationProtect
= get_win32_prot( view
->protect
, view
->protect
);
4672 if (view
->protect
& SEC_IMAGE
) info
->Type
= MEM_IMAGE
;
4673 else if (view
->protect
& (SEC_FILE
| SEC_RESERVE
| SEC_COMMIT
)) info
->Type
= MEM_MAPPED
;
4674 else info
->Type
= MEM_PRIVATE
;
4676 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4678 return STATUS_SUCCESS
;
4681 /* get basic information about a memory block */
4682 static unsigned int get_basic_memory_info( HANDLE process
, LPCVOID addr
,
4683 MEMORY_BASIC_INFORMATION
*info
,
4684 SIZE_T len
, SIZE_T
*res_len
)
4686 unsigned int status
;
4688 if (len
< sizeof(*info
))
4689 return STATUS_INFO_LENGTH_MISMATCH
;
4691 if (process
!= NtCurrentProcess())
4694 apc_result_t result
;
4696 memset( &call
, 0, sizeof(call
) );
4698 call
.virtual_query
.type
= APC_VIRTUAL_QUERY
;
4699 call
.virtual_query
.addr
= wine_server_client_ptr( addr
);
4700 status
= server_queue_process_apc( process
, &call
, &result
);
4701 if (status
!= STATUS_SUCCESS
) return status
;
4703 if (result
.virtual_query
.status
== STATUS_SUCCESS
)
4705 info
->BaseAddress
= wine_server_get_ptr( result
.virtual_query
.base
);
4706 info
->AllocationBase
= wine_server_get_ptr( result
.virtual_query
.alloc_base
);
4707 info
->RegionSize
= result
.virtual_query
.size
;
4708 info
->Protect
= result
.virtual_query
.prot
;
4709 info
->AllocationProtect
= result
.virtual_query
.alloc_prot
;
4710 info
->State
= (DWORD
)result
.virtual_query
.state
<< 12;
4711 info
->Type
= (DWORD
)result
.virtual_query
.alloc_type
<< 16;
4712 if (info
->RegionSize
!= result
.virtual_query
.size
) /* truncated */
4713 return STATUS_INVALID_PARAMETER
; /* FIXME */
4714 if (res_len
) *res_len
= sizeof(*info
);
4716 return result
.virtual_query
.status
;
4719 if ((status
= fill_basic_memory_info( addr
, info
))) return status
;
4721 if (res_len
) *res_len
= sizeof(*info
);
4722 return STATUS_SUCCESS
;
4725 static unsigned int get_memory_region_info( HANDLE process
, LPCVOID addr
, MEMORY_REGION_INFORMATION
*info
,
4726 SIZE_T len
, SIZE_T
*res_len
)
4728 MEMORY_BASIC_INFORMATION basic_info
;
4729 unsigned int status
;
4731 if (len
< FIELD_OFFSET(MEMORY_REGION_INFORMATION
, CommitSize
))
4732 return STATUS_INFO_LENGTH_MISMATCH
;
4734 if (process
!= NtCurrentProcess())
4736 FIXME("Unimplemented for other processes.\n");
4737 return STATUS_NOT_IMPLEMENTED
;
4740 if ((status
= fill_basic_memory_info( addr
, &basic_info
))) return status
;
4742 info
->AllocationBase
= basic_info
.AllocationBase
;
4743 info
->AllocationProtect
= basic_info
.AllocationProtect
;
4744 info
->RegionType
= 0; /* FIXME */
4745 if (len
>= FIELD_OFFSET(MEMORY_REGION_INFORMATION
, CommitSize
))
4746 info
->RegionSize
= basic_info
.RegionSize
;
4747 if (len
>= FIELD_OFFSET(MEMORY_REGION_INFORMATION
, PartitionId
))
4748 info
->CommitSize
= basic_info
.State
== MEM_COMMIT
? basic_info
.RegionSize
: 0;
4750 if (res_len
) *res_len
= sizeof(*info
);
4751 return STATUS_SUCCESS
;
4754 static NTSTATUS
get_working_set_ex( HANDLE process
, LPCVOID addr
,
4755 MEMORY_WORKING_SET_EX_INFORMATION
*info
,
4756 SIZE_T len
, SIZE_T
*res_len
)
4758 #if !defined(HAVE_LIBPROCSTAT)
4759 static int pagemap_fd
= -2;
4761 MEMORY_WORKING_SET_EX_INFORMATION
*p
;
4764 if (process
!= NtCurrentProcess())
4766 FIXME( "(process=%p,addr=%p) Unimplemented information class: MemoryWorkingSetExInformation\n", process
, addr
);
4767 return STATUS_INVALID_INFO_CLASS
;
4770 #if defined(HAVE_LIBPROCSTAT)
4772 struct procstat
*pstat
;
4773 unsigned int proc_count
;
4774 struct kinfo_proc
*kip
= NULL
;
4775 unsigned int vmentry_count
= 0;
4776 struct kinfo_vmentry
*vmentries
= NULL
;
4778 pstat
= procstat_open_sysctl();
4780 kip
= procstat_getprocs( pstat
, KERN_PROC_PID
, getpid(), &proc_count
);
4782 vmentries
= procstat_getvmmap( pstat
, kip
, &vmentry_count
);
4783 if (vmentries
== NULL
)
4784 WARN( "couldn't get process vmmap, errno %d\n", errno
);
4786 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4787 for (p
= info
; (UINT_PTR
)(p
+ 1) <= (UINT_PTR
)info
+ len
; p
++)
4790 struct kinfo_vmentry
*entry
= NULL
;
4792 struct file_view
*view
;
4794 memset( &p
->VirtualAttributes
, 0, sizeof(p
->VirtualAttributes
) );
4795 if ((view
= find_view( p
->VirtualAddress
, 0 )) &&
4796 get_committed_size( view
, p
->VirtualAddress
, &vprot
, VPROT_COMMITTED
) &&
4797 (vprot
& VPROT_COMMITTED
))
4799 for (i
= 0; i
< vmentry_count
&& entry
== NULL
; i
++)
4801 if (vmentries
[i
].kve_start
<= (ULONG_PTR
)p
->VirtualAddress
&& (ULONG_PTR
)p
->VirtualAddress
<= vmentries
[i
].kve_end
)
4802 entry
= &vmentries
[i
];
4805 p
->VirtualAttributes
.Valid
= !(vprot
& VPROT_GUARD
) && (vprot
& 0x0f) && entry
&& entry
->kve_type
!= KVME_TYPE_SWAP
;
4806 p
->VirtualAttributes
.Shared
= !is_view_valloc( view
);
4807 if (p
->VirtualAttributes
.Shared
&& p
->VirtualAttributes
.Valid
)
4808 p
->VirtualAttributes
.ShareCount
= 1; /* FIXME */
4809 if (p
->VirtualAttributes
.Valid
)
4810 p
->VirtualAttributes
.Win32Protection
= get_win32_prot( vprot
, view
->protect
);
4813 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4816 procstat_freevmmap( pstat
, vmentries
);
4818 procstat_freeprocs( pstat
, kip
);
4820 procstat_close( pstat
);
4823 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4824 if (pagemap_fd
== -2)
4827 if ((pagemap_fd
= open( "/proc/self/pagemap", O_RDONLY
| O_CLOEXEC
, 0 )) == -1 && errno
== EINVAL
)
4829 pagemap_fd
= open( "/proc/self/pagemap", O_RDONLY
, 0 );
4831 if (pagemap_fd
== -1) WARN( "unable to open /proc/self/pagemap\n" );
4832 else fcntl(pagemap_fd
, F_SETFD
, FD_CLOEXEC
); /* in case O_CLOEXEC isn't supported */
4835 for (p
= info
; (UINT_PTR
)(p
+ 1) <= (UINT_PTR
)info
+ len
; p
++)
4839 struct file_view
*view
;
4841 memset( &p
->VirtualAttributes
, 0, sizeof(p
->VirtualAttributes
) );
4843 if ((view
= find_view( p
->VirtualAddress
, 0 )) &&
4844 get_committed_size( view
, p
->VirtualAddress
, &vprot
, VPROT_COMMITTED
) &&
4845 (vprot
& VPROT_COMMITTED
))
4847 if (pagemap_fd
== -1 ||
4848 pread( pagemap_fd
, &pagemap
, sizeof(pagemap
), ((UINT_PTR
)p
->VirtualAddress
>> page_shift
) * sizeof(pagemap
) ) != sizeof(pagemap
))
4850 /* If we don't have pagemap information, default to invalid. */
4854 p
->VirtualAttributes
.Valid
= !(vprot
& VPROT_GUARD
) && (vprot
& 0x0f) && (pagemap
>> 63);
4855 p
->VirtualAttributes
.Shared
= !is_view_valloc( view
) && ((pagemap
>> 61) & 1);
4856 if (p
->VirtualAttributes
.Shared
&& p
->VirtualAttributes
.Valid
)
4857 p
->VirtualAttributes
.ShareCount
= 1; /* FIXME */
4858 if (p
->VirtualAttributes
.Valid
)
4859 p
->VirtualAttributes
.Win32Protection
= get_win32_prot( vprot
, view
->protect
);
4862 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4866 *res_len
= (UINT_PTR
)p
- (UINT_PTR
)info
;
4867 return STATUS_SUCCESS
;
4870 static unsigned int get_memory_section_name( HANDLE process
, LPCVOID addr
,
4871 MEMORY_SECTION_NAME
*info
, SIZE_T len
, SIZE_T
*ret_len
)
4873 unsigned int status
;
4875 if (!info
) return STATUS_ACCESS_VIOLATION
;
4877 SERVER_START_REQ( get_mapping_filename
)
4879 req
->process
= wine_server_obj_handle( process
);
4880 req
->addr
= wine_server_client_ptr( addr
);
4881 if (len
> sizeof(*info
) + sizeof(WCHAR
))
4882 wine_server_set_reply( req
, info
+ 1, len
- sizeof(*info
) - sizeof(WCHAR
) );
4883 status
= wine_server_call( req
);
4884 if (!status
|| status
== STATUS_BUFFER_OVERFLOW
)
4886 if (ret_len
) *ret_len
= sizeof(*info
) + reply
->len
+ sizeof(WCHAR
);
4887 if (len
< sizeof(*info
)) status
= STATUS_INFO_LENGTH_MISMATCH
;
4890 info
->SectionFileName
.Buffer
= (WCHAR
*)(info
+ 1);
4891 info
->SectionFileName
.Length
= reply
->len
;
4892 info
->SectionFileName
.MaximumLength
= reply
->len
+ sizeof(WCHAR
);
4893 info
->SectionFileName
.Buffer
[reply
->len
/ sizeof(WCHAR
)] = 0;
4902 /***********************************************************************
4903 * NtQueryVirtualMemory (NTDLL.@)
4904 * ZwQueryVirtualMemory (NTDLL.@)
4906 NTSTATUS WINAPI
NtQueryVirtualMemory( HANDLE process
, LPCVOID addr
,
4907 MEMORY_INFORMATION_CLASS info_class
,
4908 PVOID buffer
, SIZE_T len
, SIZE_T
*res_len
)
4912 TRACE("(%p, %p, info_class=%d, %p, %ld, %p)\n",
4913 process
, addr
, info_class
, buffer
, len
, res_len
);
4917 case MemoryBasicInformation
:
4918 return get_basic_memory_info( process
, addr
, buffer
, len
, res_len
);
4920 case MemoryWorkingSetExInformation
:
4921 return get_working_set_ex( process
, addr
, buffer
, len
, res_len
);
4923 case MemoryMappedFilenameInformation
:
4924 return get_memory_section_name( process
, addr
, buffer
, len
, res_len
);
4926 case MemoryRegionInformation
:
4927 return get_memory_region_info( process
, addr
, buffer
, len
, res_len
);
4929 case MemoryWineUnixFuncs
:
4930 case MemoryWineUnixWow64Funcs
:
4931 if (len
!= sizeof(unixlib_handle_t
)) return STATUS_INFO_LENGTH_MISMATCH
;
4932 if (process
== GetCurrentProcess())
4934 void *module
= (void *)addr
;
4935 const void *funcs
= NULL
;
4937 status
= get_builtin_unix_funcs( module
, info_class
== MemoryWineUnixWow64Funcs
, &funcs
);
4938 if (!status
) *(unixlib_handle_t
*)buffer
= (UINT_PTR
)funcs
;
4941 return STATUS_INVALID_HANDLE
;
4944 FIXME("(%p,%p,info_class=%d,%p,%ld,%p) Unknown information class\n",
4945 process
, addr
, info_class
, buffer
, len
, res_len
);
4946 return STATUS_INVALID_INFO_CLASS
;
4951 /***********************************************************************
4952 * NtLockVirtualMemory (NTDLL.@)
4953 * ZwLockVirtualMemory (NTDLL.@)
4955 NTSTATUS WINAPI
NtLockVirtualMemory( HANDLE process
, PVOID
*addr
, SIZE_T
*size
, ULONG unknown
)
4957 unsigned int status
= STATUS_SUCCESS
;
4959 if (process
!= NtCurrentProcess())
4962 apc_result_t result
;
4964 memset( &call
, 0, sizeof(call
) );
4966 call
.virtual_lock
.type
= APC_VIRTUAL_LOCK
;
4967 call
.virtual_lock
.addr
= wine_server_client_ptr( *addr
);
4968 call
.virtual_lock
.size
= *size
;
4969 status
= server_queue_process_apc( process
, &call
, &result
);
4970 if (status
!= STATUS_SUCCESS
) return status
;
4972 if (result
.virtual_lock
.status
== STATUS_SUCCESS
)
4974 *addr
= wine_server_get_ptr( result
.virtual_lock
.addr
);
4975 *size
= result
.virtual_lock
.size
;
4977 return result
.virtual_lock
.status
;
4980 *size
= ROUND_SIZE( *addr
, *size
);
4981 *addr
= ROUND_ADDR( *addr
, page_mask
);
4983 if (mlock( *addr
, *size
)) status
= STATUS_ACCESS_DENIED
;
4988 /***********************************************************************
4989 * NtUnlockVirtualMemory (NTDLL.@)
4990 * ZwUnlockVirtualMemory (NTDLL.@)
4992 NTSTATUS WINAPI
NtUnlockVirtualMemory( HANDLE process
, PVOID
*addr
, SIZE_T
*size
, ULONG unknown
)
4994 unsigned int status
= STATUS_SUCCESS
;
4996 if (process
!= NtCurrentProcess())
4999 apc_result_t result
;
5001 memset( &call
, 0, sizeof(call
) );
5003 call
.virtual_unlock
.type
= APC_VIRTUAL_UNLOCK
;
5004 call
.virtual_unlock
.addr
= wine_server_client_ptr( *addr
);
5005 call
.virtual_unlock
.size
= *size
;
5006 status
= server_queue_process_apc( process
, &call
, &result
);
5007 if (status
!= STATUS_SUCCESS
) return status
;
5009 if (result
.virtual_unlock
.status
== STATUS_SUCCESS
)
5011 *addr
= wine_server_get_ptr( result
.virtual_unlock
.addr
);
5012 *size
= result
.virtual_unlock
.size
;
5014 return result
.virtual_unlock
.status
;
5017 *size
= ROUND_SIZE( *addr
, *size
);
5018 *addr
= ROUND_ADDR( *addr
, page_mask
);
5020 if (munlock( *addr
, *size
)) status
= STATUS_ACCESS_DENIED
;
5025 /***********************************************************************
5026 * NtMapViewOfSection (NTDLL.@)
5027 * ZwMapViewOfSection (NTDLL.@)
5029 NTSTATUS WINAPI
NtMapViewOfSection( HANDLE handle
, HANDLE process
, PVOID
*addr_ptr
, ULONG_PTR zero_bits
,
5030 SIZE_T commit_size
, const LARGE_INTEGER
*offset_ptr
, SIZE_T
*size_ptr
,
5031 SECTION_INHERIT inherit
, ULONG alloc_type
, ULONG protect
)
5034 SIZE_T mask
= granularity_mask
;
5035 LARGE_INTEGER offset
;
5037 offset
.QuadPart
= offset_ptr
? offset_ptr
->QuadPart
: 0;
5039 TRACE("handle=%p process=%p addr=%p off=%s size=%lx access=%x\n",
5040 handle
, process
, *addr_ptr
, wine_dbgstr_longlong(offset
.QuadPart
), *size_ptr
, (int)protect
);
5042 /* Check parameters */
5043 if (zero_bits
> 21 && zero_bits
< 32)
5044 return STATUS_INVALID_PARAMETER_4
;
5046 /* If both addr_ptr and zero_bits are passed, they have match */
5047 if (*addr_ptr
&& zero_bits
&& zero_bits
< 32 &&
5048 (((UINT_PTR
)*addr_ptr
) >> (32 - zero_bits
)))
5049 return STATUS_INVALID_PARAMETER_4
;
5050 if (*addr_ptr
&& zero_bits
>= 32 &&
5051 (((UINT_PTR
)*addr_ptr
) & ~zero_bits
))
5052 return STATUS_INVALID_PARAMETER_4
;
5055 if (!is_old_wow64())
5057 if (zero_bits
>= 32) return STATUS_INVALID_PARAMETER_4
;
5058 if (alloc_type
& AT_ROUND_TO_PAGE
)
5060 *addr_ptr
= ROUND_ADDR( *addr_ptr
, page_mask
);
5066 if ((offset
.u
.LowPart
& mask
) || (*addr_ptr
&& ((UINT_PTR
)*addr_ptr
& mask
)))
5067 return STATUS_MAPPED_ALIGNMENT
;
5069 if (process
!= NtCurrentProcess())
5072 apc_result_t result
;
5074 memset( &call
, 0, sizeof(call
) );
5076 call
.map_view
.type
= APC_MAP_VIEW
;
5077 call
.map_view
.handle
= wine_server_obj_handle( handle
);
5078 call
.map_view
.addr
= wine_server_client_ptr( *addr_ptr
);
5079 call
.map_view
.size
= *size_ptr
;
5080 call
.map_view
.offset
= offset
.QuadPart
;
5081 call
.map_view
.zero_bits
= zero_bits
;
5082 call
.map_view
.alloc_type
= alloc_type
;
5083 call
.map_view
.prot
= protect
;
5084 res
= server_queue_process_apc( process
, &call
, &result
);
5085 if (res
!= STATUS_SUCCESS
) return res
;
5087 if (NT_SUCCESS(result
.map_view
.status
))
5089 *addr_ptr
= wine_server_get_ptr( result
.map_view
.addr
);
5090 *size_ptr
= result
.map_view
.size
;
5092 return result
.map_view
.status
;
5095 return virtual_map_section( handle
, addr_ptr
, get_zero_bits_limit( zero_bits
), commit_size
,
5096 offset_ptr
, size_ptr
, alloc_type
, protect
, 0 );
5099 /***********************************************************************
5100 * NtMapViewOfSectionEx (NTDLL.@)
5101 * ZwMapViewOfSectionEx (NTDLL.@)
5103 NTSTATUS WINAPI
NtMapViewOfSectionEx( HANDLE handle
, HANDLE process
, PVOID
*addr_ptr
,
5104 const LARGE_INTEGER
*offset_ptr
, SIZE_T
*size_ptr
,
5105 ULONG alloc_type
, ULONG protect
,
5106 MEM_EXTENDED_PARAMETER
*parameters
, ULONG count
)
5108 ULONG_PTR limit
= 0, align
= 0;
5109 ULONG attributes
= 0;
5111 unsigned int status
;
5112 SIZE_T mask
= granularity_mask
;
5113 LARGE_INTEGER offset
;
5115 offset
.QuadPart
= offset_ptr
? offset_ptr
->QuadPart
: 0;
5117 TRACE( "handle=%p process=%p addr=%p off=%s size=%lx access=%x\n",
5118 handle
, process
, *addr_ptr
, wine_dbgstr_longlong(offset
.QuadPart
), *size_ptr
, (int)protect
);
5120 status
= get_extended_params( parameters
, count
, &limit
, &align
, &attributes
, &machine
);
5121 if (status
) return status
;
5123 if (align
) return STATUS_INVALID_PARAMETER
;
5124 if (*addr_ptr
&& limit
) return STATUS_INVALID_PARAMETER
;
5127 if (!is_old_wow64() && (alloc_type
& AT_ROUND_TO_PAGE
))
5129 *addr_ptr
= ROUND_ADDR( *addr_ptr
, page_mask
);
5134 if ((offset
.u
.LowPart
& mask
) || (*addr_ptr
&& ((UINT_PTR
)*addr_ptr
& mask
)))
5135 return STATUS_MAPPED_ALIGNMENT
;
5137 if (process
!= NtCurrentProcess())
5140 apc_result_t result
;
5142 memset( &call
, 0, sizeof(call
) );
5144 call
.map_view_ex
.type
= APC_MAP_VIEW_EX
;
5145 call
.map_view_ex
.handle
= wine_server_obj_handle( handle
);
5146 call
.map_view_ex
.addr
= wine_server_client_ptr( *addr_ptr
);
5147 call
.map_view_ex
.size
= *size_ptr
;
5148 call
.map_view_ex
.offset
= offset
.QuadPart
;
5149 call
.map_view_ex
.limit
= limit
;
5150 call
.map_view_ex
.alloc_type
= alloc_type
;
5151 call
.map_view_ex
.prot
= protect
;
5152 call
.map_view_ex
.machine
= machine
;
5153 if (call
.map_view_ex
.prot
!= protect
) return STATUS_INVALID_PARAMETER
;
5154 status
= server_queue_process_apc( process
, &call
, &result
);
5155 if (status
!= STATUS_SUCCESS
) return status
;
5157 if (NT_SUCCESS(result
.map_view_ex
.status
))
5159 *addr_ptr
= wine_server_get_ptr( result
.map_view_ex
.addr
);
5160 *size_ptr
= result
.map_view_ex
.size
;
5162 return result
.map_view_ex
.status
;
5165 return virtual_map_section( handle
, addr_ptr
, limit
, 0, offset_ptr
, size_ptr
,
5166 alloc_type
, protect
, machine
);
5169 /***********************************************************************
5170 * NtUnmapViewOfSection (NTDLL.@)
5171 * ZwUnmapViewOfSection (NTDLL.@)
5173 NTSTATUS WINAPI
NtUnmapViewOfSection( HANDLE process
, PVOID addr
)
5175 struct file_view
*view
;
5176 unsigned int status
= STATUS_NOT_MAPPED_VIEW
;
5179 if (process
!= NtCurrentProcess())
5182 apc_result_t result
;
5184 memset( &call
, 0, sizeof(call
) );
5186 call
.unmap_view
.type
= APC_UNMAP_VIEW
;
5187 call
.unmap_view
.addr
= wine_server_client_ptr( addr
);
5188 status
= server_queue_process_apc( process
, &call
, &result
);
5189 if (status
== STATUS_SUCCESS
) status
= result
.unmap_view
.status
;
5193 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
5194 if ((view
= find_view( addr
, 0 )) && !is_view_valloc( view
))
5196 if (view
->protect
& VPROT_SYSTEM
)
5198 struct builtin_module
*builtin
;
5200 LIST_FOR_EACH_ENTRY( builtin
, &builtin_modules
, struct builtin_module
, entry
)
5202 if (builtin
->module
!= view
->base
) continue;
5203 if (builtin
->refcount
> 1)
5205 TRACE( "not freeing in-use builtin %p\n", view
->base
);
5206 builtin
->refcount
--;
5207 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
5208 return STATUS_SUCCESS
;
5213 SERVER_START_REQ( unmap_view
)
5215 req
->base
= wine_server_client_ptr( view
->base
);
5216 status
= wine_server_call( req
);
5221 if (view
->protect
& SEC_IMAGE
) release_builtin_module( view
->base
);
5222 delete_view( view
);
5224 else FIXME( "failed to unmap %p %x\n", view
->base
, status
);
5226 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
5230 /***********************************************************************
5231 * NtUnmapViewOfSectionEx (NTDLL.@)
5232 * ZwUnmapViewOfSectionEx (NTDLL.@)
5234 NTSTATUS WINAPI
NtUnmapViewOfSectionEx( HANDLE process
, PVOID addr
, ULONG flags
)
5236 if (flags
) FIXME("Ignoring flags %#x.\n", (int)flags
);
5237 return NtUnmapViewOfSection( process
, addr
);
5240 /******************************************************************************
5241 * virtual_fill_image_information
5243 * Helper for NtQuerySection.
5245 void virtual_fill_image_information( const pe_image_info_t
*pe_info
, SECTION_IMAGE_INFORMATION
*info
)
5247 info
->TransferAddress
= wine_server_get_ptr( pe_info
->base
+ pe_info
->entry_point
);
5248 info
->ZeroBits
= pe_info
->zerobits
;
5249 info
->MaximumStackSize
= pe_info
->stack_size
;
5250 info
->CommittedStackSize
= pe_info
->stack_commit
;
5251 info
->SubSystemType
= pe_info
->subsystem
;
5252 info
->MinorSubsystemVersion
= pe_info
->subsystem_minor
;
5253 info
->MajorSubsystemVersion
= pe_info
->subsystem_major
;
5254 info
->MajorOperatingSystemVersion
= pe_info
->osversion_major
;
5255 info
->MinorOperatingSystemVersion
= pe_info
->osversion_minor
;
5256 info
->ImageCharacteristics
= pe_info
->image_charact
;
5257 info
->DllCharacteristics
= pe_info
->dll_charact
;
5258 info
->Machine
= pe_info
->machine
;
5259 info
->ImageContainsCode
= pe_info
->contains_code
;
5260 info
->ImageFlags
= pe_info
->image_flags
;
5261 info
->LoaderFlags
= pe_info
->loader_flags
;
5262 info
->ImageFileSize
= pe_info
->file_size
;
5263 info
->CheckSum
= pe_info
->checksum
;
5264 #ifndef _WIN64 /* don't return 64-bit values to 32-bit processes */
5265 if (is_machine_64bit( pe_info
->machine
))
5267 info
->TransferAddress
= (void *)0x81231234; /* sic */
5268 info
->MaximumStackSize
= 0x100000;
5269 info
->CommittedStackSize
= 0x10000;
5274 /******************************************************************************
5275 * NtQuerySection (NTDLL.@)
5276 * ZwQuerySection (NTDLL.@)
5278 NTSTATUS WINAPI
NtQuerySection( HANDLE handle
, SECTION_INFORMATION_CLASS
class, void *ptr
,
5279 SIZE_T size
, SIZE_T
*ret_size
)
5281 unsigned int status
;
5282 pe_image_info_t image_info
;
5286 case SectionBasicInformation
:
5287 if (size
< sizeof(SECTION_BASIC_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
5289 case SectionImageInformation
:
5290 if (size
< sizeof(SECTION_IMAGE_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
5293 FIXME( "class %u not implemented\n", class );
5294 return STATUS_NOT_IMPLEMENTED
;
5296 if (!ptr
) return STATUS_ACCESS_VIOLATION
;
5298 SERVER_START_REQ( get_mapping_info
)
5300 req
->handle
= wine_server_obj_handle( handle
);
5301 req
->access
= SECTION_QUERY
;
5302 wine_server_set_reply( req
, &image_info
, sizeof(image_info
) );
5303 if (!(status
= wine_server_call( req
)))
5305 if (class == SectionBasicInformation
)
5307 SECTION_BASIC_INFORMATION
*info
= ptr
;
5308 info
->Attributes
= reply
->flags
;
5309 info
->BaseAddress
= NULL
;
5310 info
->Size
.QuadPart
= reply
->size
;
5311 if (ret_size
) *ret_size
= sizeof(*info
);
5313 else if (reply
->flags
& SEC_IMAGE
)
5315 SECTION_IMAGE_INFORMATION
*info
= ptr
;
5316 virtual_fill_image_information( &image_info
, info
);
5317 if (ret_size
) *ret_size
= sizeof(*info
);
5319 else status
= STATUS_SECTION_NOT_IMAGE
;
5328 /***********************************************************************
5329 * NtFlushVirtualMemory (NTDLL.@)
5330 * ZwFlushVirtualMemory (NTDLL.@)
5332 NTSTATUS WINAPI
NtFlushVirtualMemory( HANDLE process
, LPCVOID
*addr_ptr
,
5333 SIZE_T
*size_ptr
, ULONG unknown
)
5335 struct file_view
*view
;
5336 unsigned int status
= STATUS_SUCCESS
;
5338 void *addr
= ROUND_ADDR( *addr_ptr
, page_mask
);
5340 if (process
!= NtCurrentProcess())
5343 apc_result_t result
;
5345 memset( &call
, 0, sizeof(call
) );
5347 call
.virtual_flush
.type
= APC_VIRTUAL_FLUSH
;
5348 call
.virtual_flush
.addr
= wine_server_client_ptr( addr
);
5349 call
.virtual_flush
.size
= *size_ptr
;
5350 status
= server_queue_process_apc( process
, &call
, &result
);
5351 if (status
!= STATUS_SUCCESS
) return status
;
5353 if (result
.virtual_flush
.status
== STATUS_SUCCESS
)
5355 *addr_ptr
= wine_server_get_ptr( result
.virtual_flush
.addr
);
5356 *size_ptr
= result
.virtual_flush
.size
;
5358 return result
.virtual_flush
.status
;
5361 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
5362 if (!(view
= find_view( addr
, *size_ptr
))) status
= STATUS_INVALID_PARAMETER
;
5365 if (!*size_ptr
) *size_ptr
= view
->size
;
5368 if (msync( addr
, *size_ptr
, MS_ASYNC
)) status
= STATUS_NOT_MAPPED_DATA
;
5371 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
5376 /***********************************************************************
5377 * NtGetWriteWatch (NTDLL.@)
5378 * ZwGetWriteWatch (NTDLL.@)
5380 NTSTATUS WINAPI
NtGetWriteWatch( HANDLE process
, ULONG flags
, PVOID base
, SIZE_T size
, PVOID
*addresses
,
5381 ULONG_PTR
*count
, ULONG
*granularity
)
5383 NTSTATUS status
= STATUS_SUCCESS
;
5386 size
= ROUND_SIZE( base
, size
);
5387 base
= ROUND_ADDR( base
, page_mask
);
5389 if (!count
|| !granularity
) return STATUS_ACCESS_VIOLATION
;
5390 if (!*count
|| !size
) return STATUS_INVALID_PARAMETER
;
5391 if (flags
& ~WRITE_WATCH_FLAG_RESET
) return STATUS_INVALID_PARAMETER
;
5393 if (!addresses
) return STATUS_ACCESS_VIOLATION
;
5395 TRACE( "%p %x %p-%p %p %lu\n", process
, (int)flags
, base
, (char *)base
+ size
,
5396 addresses
, *count
);
5398 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
5400 if (is_write_watch_range( base
, size
))
5404 char *end
= addr
+ size
;
5406 while (pos
< *count
&& addr
< end
)
5408 if (!(get_page_vprot( addr
) & VPROT_WRITEWATCH
)) addresses
[pos
++] = addr
;
5411 if (flags
& WRITE_WATCH_FLAG_RESET
) reset_write_watches( base
, addr
- (char *)base
);
5413 *granularity
= page_size
;
5415 else status
= STATUS_INVALID_PARAMETER
;
5417 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
5422 /***********************************************************************
5423 * NtResetWriteWatch (NTDLL.@)
5424 * ZwResetWriteWatch (NTDLL.@)
5426 NTSTATUS WINAPI
NtResetWriteWatch( HANDLE process
, PVOID base
, SIZE_T size
)
5428 NTSTATUS status
= STATUS_SUCCESS
;
5431 size
= ROUND_SIZE( base
, size
);
5432 base
= ROUND_ADDR( base
, page_mask
);
5434 TRACE( "%p %p-%p\n", process
, base
, (char *)base
+ size
);
5436 if (!size
) return STATUS_INVALID_PARAMETER
;
5438 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
5440 if (is_write_watch_range( base
, size
))
5441 reset_write_watches( base
, size
);
5443 status
= STATUS_INVALID_PARAMETER
;
5445 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
5450 /***********************************************************************
5451 * NtReadVirtualMemory (NTDLL.@)
5452 * ZwReadVirtualMemory (NTDLL.@)
5454 NTSTATUS WINAPI
NtReadVirtualMemory( HANDLE process
, const void *addr
, void *buffer
,
5455 SIZE_T size
, SIZE_T
*bytes_read
)
5457 unsigned int status
;
5459 if (virtual_check_buffer_for_write( buffer
, size
))
5461 SERVER_START_REQ( read_process_memory
)
5463 req
->handle
= wine_server_obj_handle( process
);
5464 req
->addr
= wine_server_client_ptr( addr
);
5465 wine_server_set_reply( req
, buffer
, size
);
5466 if ((status
= wine_server_call( req
))) size
= 0;
5472 status
= STATUS_ACCESS_VIOLATION
;
5475 if (bytes_read
) *bytes_read
= size
;
5480 /***********************************************************************
5481 * NtWriteVirtualMemory (NTDLL.@)
5482 * ZwWriteVirtualMemory (NTDLL.@)
5484 NTSTATUS WINAPI
NtWriteVirtualMemory( HANDLE process
, void *addr
, const void *buffer
,
5485 SIZE_T size
, SIZE_T
*bytes_written
)
5487 unsigned int status
;
5489 if (virtual_check_buffer_for_read( buffer
, size
))
5491 SERVER_START_REQ( write_process_memory
)
5493 req
->handle
= wine_server_obj_handle( process
);
5494 req
->addr
= wine_server_client_ptr( addr
);
5495 wine_server_add_data( req
, buffer
, size
);
5496 if ((status
= wine_server_call( req
))) size
= 0;
5502 status
= STATUS_PARTIAL_COPY
;
5505 if (bytes_written
) *bytes_written
= size
;
5510 /***********************************************************************
5511 * NtAreMappedFilesTheSame (NTDLL.@)
5512 * ZwAreMappedFilesTheSame (NTDLL.@)
5514 NTSTATUS WINAPI
NtAreMappedFilesTheSame(PVOID addr1
, PVOID addr2
)
5516 struct file_view
*view1
, *view2
;
5517 unsigned int status
;
5520 TRACE("%p %p\n", addr1
, addr2
);
5522 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
5524 view1
= find_view( addr1
, 0 );
5525 view2
= find_view( addr2
, 0 );
5527 if (!view1
|| !view2
)
5528 status
= STATUS_INVALID_ADDRESS
;
5529 else if (is_view_valloc( view1
) || is_view_valloc( view2
))
5530 status
= STATUS_CONFLICTING_ADDRESSES
;
5531 else if (view1
== view2
)
5532 status
= STATUS_SUCCESS
;
5533 else if ((view1
->protect
& VPROT_SYSTEM
) || (view2
->protect
& VPROT_SYSTEM
))
5534 status
= STATUS_NOT_SAME_DEVICE
;
5537 SERVER_START_REQ( is_same_mapping
)
5539 req
->base1
= wine_server_client_ptr( view1
->base
);
5540 req
->base2
= wine_server_client_ptr( view2
->base
);
5541 status
= wine_server_call( req
);
5546 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
5551 static NTSTATUS
prefetch_memory( HANDLE process
, ULONG_PTR count
,
5552 PMEMORY_RANGE_ENTRY addresses
, ULONG flags
)
5557 static unsigned int once
;
5561 FIXME( "(process=%p,flags=%u) NtSetInformationVirtualMemory(VmPrefetchInformation) partial stub\n",
5562 process
, (int)flags
);
5565 for (i
= 0; i
< count
; i
++)
5567 if (!addresses
[i
].NumberOfBytes
) return STATUS_INVALID_PARAMETER_4
;
5570 if (process
!= NtCurrentProcess()) return STATUS_SUCCESS
;
5572 for (i
= 0; i
< count
; i
++)
5574 base
= ROUND_ADDR( addresses
[i
].VirtualAddress
, page_mask
);
5575 size
= ROUND_SIZE( addresses
[i
].VirtualAddress
, addresses
[i
].NumberOfBytes
);
5576 madvise( base
, size
, MADV_WILLNEED
);
5579 return STATUS_SUCCESS
;
5582 /***********************************************************************
5583 * NtSetInformationVirtualMemory (NTDLL.@)
5584 * ZwSetInformationVirtualMemory (NTDLL.@)
5586 NTSTATUS WINAPI
NtSetInformationVirtualMemory( HANDLE process
,
5587 VIRTUAL_MEMORY_INFORMATION_CLASS info_class
,
5588 ULONG_PTR count
, PMEMORY_RANGE_ENTRY addresses
,
5589 PVOID ptr
, ULONG size
)
5591 TRACE("(%p, info_class=%d, %lu, %p, %p, %u)\n",
5592 process
, info_class
, count
, addresses
, ptr
, (int)size
);
5596 case VmPrefetchInformation
:
5597 if (!ptr
) return STATUS_INVALID_PARAMETER_5
;
5598 if (size
!= sizeof(ULONG
)) return STATUS_INVALID_PARAMETER_6
;
5599 if (!count
) return STATUS_INVALID_PARAMETER_3
;
5600 return prefetch_memory( process
, count
, addresses
, *(ULONG
*)ptr
);
5603 FIXME("(%p,info_class=%d,%lu,%p,%p,%u) Unknown information class\n",
5604 process
, info_class
, count
, addresses
, ptr
, (int)size
);
5605 return STATUS_INVALID_PARAMETER_2
;
5610 /**********************************************************************
5611 * NtFlushInstructionCache (NTDLL.@)
5613 NTSTATUS WINAPI
NtFlushInstructionCache( HANDLE handle
, const void *addr
, SIZE_T size
)
5615 #if defined(__x86_64__) || defined(__i386__)
5617 #elif defined(HAVE___CLEAR_CACHE)
5618 if (handle
== GetCurrentProcess())
5620 __clear_cache( (char *)addr
, (char *)addr
+ size
);
5625 if (!once
++) FIXME( "%p %p %ld other process not supported\n", handle
, addr
, size
);
5629 if (!once
++) FIXME( "%p %p %ld\n", handle
, addr
, size
);
5631 return STATUS_SUCCESS
;
5635 /**********************************************************************
5636 * NtFlushProcessWriteBuffers (NTDLL.@)
5638 void WINAPI
NtFlushProcessWriteBuffers(void)
5640 static int once
= 0;
5641 if (!once
++) FIXME( "stub\n" );
5645 /**********************************************************************
5646 * NtCreatePagingFile (NTDLL.@)
5648 NTSTATUS WINAPI
NtCreatePagingFile( UNICODE_STRING
*name
, LARGE_INTEGER
*min_size
,
5649 LARGE_INTEGER
*max_size
, LARGE_INTEGER
*actual_size
)
5651 FIXME( "(%s %p %p %p) stub\n", debugstr_us(name
), min_size
, max_size
, actual_size
);
5652 return STATUS_SUCCESS
;
5657 /***********************************************************************
5658 * NtWow64AllocateVirtualMemory64 (NTDLL.@)
5659 * ZwWow64AllocateVirtualMemory64 (NTDLL.@)
5661 NTSTATUS WINAPI
NtWow64AllocateVirtualMemory64( HANDLE process
, ULONG64
*ret
, ULONG64 zero_bits
,
5662 ULONG64
*size_ptr
, ULONG type
, ULONG protect
)
5666 unsigned int status
;
5668 TRACE("%p %s %s %x %08x\n", process
,
5669 wine_dbgstr_longlong(*ret
), wine_dbgstr_longlong(*size_ptr
), (int)type
, (int)protect
);
5671 if (!*size_ptr
) return STATUS_INVALID_PARAMETER_4
;
5672 if (zero_bits
> 21 && zero_bits
< 32) return STATUS_INVALID_PARAMETER_3
;
5674 if (process
!= NtCurrentProcess())
5677 apc_result_t result
;
5679 memset( &call
, 0, sizeof(call
) );
5681 call
.virtual_alloc
.type
= APC_VIRTUAL_ALLOC
;
5682 call
.virtual_alloc
.addr
= *ret
;
5683 call
.virtual_alloc
.size
= *size_ptr
;
5684 call
.virtual_alloc
.zero_bits
= zero_bits
;
5685 call
.virtual_alloc
.op_type
= type
;
5686 call
.virtual_alloc
.prot
= protect
;
5687 status
= server_queue_process_apc( process
, &call
, &result
);
5688 if (status
!= STATUS_SUCCESS
) return status
;
5690 if (result
.virtual_alloc
.status
== STATUS_SUCCESS
)
5692 *ret
= result
.virtual_alloc
.addr
;
5693 *size_ptr
= result
.virtual_alloc
.size
;
5695 return result
.virtual_alloc
.status
;
5698 base
= (void *)(ULONG_PTR
)*ret
;
5700 if ((ULONG_PTR
)base
!= *ret
) return STATUS_CONFLICTING_ADDRESSES
;
5701 if (size
!= *size_ptr
) return STATUS_WORKING_SET_LIMIT_RANGE
;
5703 status
= NtAllocateVirtualMemory( process
, &base
, zero_bits
, &size
, type
, protect
);
5706 *ret
= (ULONG_PTR
)base
;
5713 /***********************************************************************
5714 * NtWow64ReadVirtualMemory64 (NTDLL.@)
5715 * ZwWow64ReadVirtualMemory64 (NTDLL.@)
5717 NTSTATUS WINAPI
NtWow64ReadVirtualMemory64( HANDLE process
, ULONG64 addr
, void *buffer
,
5718 ULONG64 size
, ULONG64
*bytes_read
)
5720 unsigned int status
;
5722 if (size
> MAXLONG
) size
= MAXLONG
;
5724 if (virtual_check_buffer_for_write( buffer
, size
))
5726 SERVER_START_REQ( read_process_memory
)
5728 req
->handle
= wine_server_obj_handle( process
);
5730 wine_server_set_reply( req
, buffer
, size
);
5731 if ((status
= wine_server_call( req
))) size
= 0;
5737 status
= STATUS_ACCESS_VIOLATION
;
5740 if (bytes_read
) *bytes_read
= size
;
5745 /***********************************************************************
5746 * NtWow64WriteVirtualMemory64 (NTDLL.@)
5747 * ZwWow64WriteVirtualMemory64 (NTDLL.@)
5749 NTSTATUS WINAPI
NtWow64WriteVirtualMemory64( HANDLE process
, ULONG64 addr
, const void *buffer
,
5750 ULONG64 size
, ULONG64
*bytes_written
)
5752 unsigned int status
;
5754 if (size
> MAXLONG
) size
= MAXLONG
;
5756 if (virtual_check_buffer_for_read( buffer
, size
))
5758 SERVER_START_REQ( write_process_memory
)
5760 req
->handle
= wine_server_obj_handle( process
);
5762 wine_server_add_data( req
, buffer
, size
);
5763 if ((status
= wine_server_call( req
))) size
= 0;
5769 status
= STATUS_PARTIAL_COPY
;
5772 if (bytes_written
) *bytes_written
= size
;
5777 /***********************************************************************
5778 * NtWow64GetNativeSystemInformation (NTDLL.@)
5779 * ZwWow64GetNativeSystemInformation (NTDLL.@)
5781 NTSTATUS WINAPI
NtWow64GetNativeSystemInformation( SYSTEM_INFORMATION_CLASS
class, void *info
,
5782 ULONG len
, ULONG
*retlen
)
5788 case SystemCpuInformation
:
5789 status
= NtQuerySystemInformation( class, info
, len
, retlen
);
5790 if (!status
&& is_old_wow64())
5792 SYSTEM_CPU_INFORMATION
*cpu
= info
;
5794 if (cpu
->ProcessorArchitecture
== PROCESSOR_ARCHITECTURE_INTEL
)
5795 cpu
->ProcessorArchitecture
= PROCESSOR_ARCHITECTURE_AMD64
;
5798 case SystemBasicInformation
:
5799 case SystemEmulationBasicInformation
:
5800 case SystemEmulationProcessorInformation
:
5801 return NtQuerySystemInformation( class, info
, len
, retlen
);
5802 case SystemNativeBasicInformation
:
5803 return NtQuerySystemInformation( SystemBasicInformation
, info
, len
, retlen
);
5805 if (is_old_wow64()) return STATUS_INVALID_INFO_CLASS
;
5806 return NtQuerySystemInformation( class, info
, len
, retlen
);
5810 /***********************************************************************
5811 * NtWow64IsProcessorFeaturePresent (NTDLL.@)
5812 * ZwWow64IsProcessorFeaturePresent (NTDLL.@)
5814 NTSTATUS WINAPI
NtWow64IsProcessorFeaturePresent( UINT feature
)
5816 return feature
< PROCESSOR_FEATURE_MAX
&& user_shared_data
->ProcessorFeatures
[feature
];