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
);
94 static struct list reserved_areas
= LIST_INIT(reserved_areas
);
99 unsigned int refcount
;
106 static struct list builtin_modules
= LIST_INIT( builtin_modules
);
110 struct wine_rb_entry entry
; /* entry in global view tree */
111 void *base
; /* base address */
112 size_t size
; /* size in bytes */
113 unsigned int protect
; /* protection for all pages at allocation time and SEC_* flags */
116 /* per-page protection flags */
117 #define VPROT_READ 0x01
118 #define VPROT_WRITE 0x02
119 #define VPROT_EXEC 0x04
120 #define VPROT_WRITECOPY 0x08
121 #define VPROT_GUARD 0x10
122 #define VPROT_COMMITTED 0x20
123 #define VPROT_WRITEWATCH 0x40
124 /* per-mapping protection flags */
125 #define VPROT_SYSTEM 0x0200 /* system view (underlying mmap not under our control) */
127 /* Conversion from VPROT_* to Win32 flags */
128 static const BYTE VIRTUAL_Win32Flags
[16] =
130 PAGE_NOACCESS
, /* 0 */
131 PAGE_READONLY
, /* READ */
132 PAGE_READWRITE
, /* WRITE */
133 PAGE_READWRITE
, /* READ | WRITE */
134 PAGE_EXECUTE
, /* EXEC */
135 PAGE_EXECUTE_READ
, /* READ | EXEC */
136 PAGE_EXECUTE_READWRITE
, /* WRITE | EXEC */
137 PAGE_EXECUTE_READWRITE
, /* READ | WRITE | EXEC */
138 PAGE_WRITECOPY
, /* WRITECOPY */
139 PAGE_WRITECOPY
, /* READ | WRITECOPY */
140 PAGE_WRITECOPY
, /* WRITE | WRITECOPY */
141 PAGE_WRITECOPY
, /* READ | WRITE | WRITECOPY */
142 PAGE_EXECUTE_WRITECOPY
, /* EXEC | WRITECOPY */
143 PAGE_EXECUTE_WRITECOPY
, /* READ | EXEC | WRITECOPY */
144 PAGE_EXECUTE_WRITECOPY
, /* WRITE | EXEC | WRITECOPY */
145 PAGE_EXECUTE_WRITECOPY
/* READ | WRITE | EXEC | WRITECOPY */
148 static struct wine_rb_tree views_tree
;
149 static pthread_mutex_t virtual_mutex
;
151 static const UINT page_shift
= 12;
152 static const UINT_PTR page_mask
= 0xfff;
153 static const UINT_PTR granularity_mask
= 0xffff;
155 /* Note: these are Windows limits, you cannot change them. */
157 static void *address_space_start
= (void *)0x110000; /* keep DOS area clear */
159 static void *address_space_start
= (void *)0x10000;
163 static void *address_space_limit
= (void *)0xffffffff0000; /* top of the total available address space */
164 #elif defined(_WIN64)
165 static void *address_space_limit
= (void *)0x7fffffff0000;
167 static void *address_space_limit
= (void *)0xc0000000;
171 static void *user_space_limit
= (void *)0x7fffffff0000; /* top of the user address space */
172 static void *working_set_limit
= (void *)0x7fffffff0000; /* top of the current working set */
174 static void *user_space_limit
= (void *)0x7fff0000;
175 static void *working_set_limit
= (void *)0x7fff0000;
178 static UINT64
*arm64ec_map
;
180 struct _KUSER_SHARED_DATA
*user_shared_data
= (void *)0x7ffe0000;
182 /* TEB allocation blocks */
183 static void *teb_block
;
184 static void **next_free_teb
;
185 static int teb_block_pos
;
186 static struct list teb_list
= LIST_INIT( teb_list
);
188 #define ROUND_ADDR(addr,mask) ((void *)((UINT_PTR)(addr) & ~(UINT_PTR)(mask)))
189 #define ROUND_SIZE(addr,size) (((SIZE_T)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask)
191 #define VIRTUAL_DEBUG_DUMP_VIEW(view) do { if (TRACE_ON(virtual)) dump_view(view); } while (0)
193 #ifndef MAP_NORESERVE
194 #define MAP_NORESERVE 0
197 #ifdef _WIN64 /* on 64-bit the page protection bytes use a 2-level table */
198 static const size_t pages_vprot_shift
= 20;
199 static const size_t pages_vprot_mask
= (1 << 20) - 1;
200 static size_t pages_vprot_size
;
201 static BYTE
**pages_vprot
;
202 #else /* on 32-bit we use a simple array with one byte per page */
203 static BYTE
*pages_vprot
;
206 static struct file_view
*view_block_start
, *view_block_end
, *next_free_view
;
207 static const size_t view_block_size
= 0x100000;
208 static void *preload_reserve_start
;
209 static void *preload_reserve_end
;
210 static BOOL force_exec_prot
; /* whether to force PROT_EXEC on all PROT_READ mmaps */
218 static struct range_entry
*free_ranges
;
219 static struct range_entry
*free_ranges_end
;
222 static inline BOOL
is_beyond_limit( const void *addr
, size_t size
, const void *limit
)
224 return (addr
>= limit
|| (const char *)addr
+ size
> (const char *)limit
);
227 /* mmap() anonymous memory at a fixed address */
228 void *anon_mmap_fixed( void *start
, size_t size
, int prot
, int flags
)
230 return mmap( start
, size
, prot
, MAP_PRIVATE
| MAP_ANON
| MAP_FIXED
| flags
, -1, 0 );
233 /* allocate anonymous mmap() memory at any address */
234 void *anon_mmap_alloc( size_t size
, int prot
)
236 return mmap( NULL
, size
, prot
, MAP_PRIVATE
| MAP_ANON
, -1, 0 );
240 static void mmap_add_reserved_area( void *addr
, SIZE_T size
)
242 struct reserved_area
*area
;
245 if (!((intptr_t)addr
+ size
)) size
--; /* avoid wrap-around */
247 LIST_FOR_EACH( ptr
, &reserved_areas
)
249 area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
250 if (area
->base
> addr
)
252 /* try to merge with the next one */
253 if ((char *)addr
+ size
== (char *)area
->base
)
261 else if ((char *)area
->base
+ area
->size
== (char *)addr
)
263 /* merge with the previous one */
266 /* try to merge with the next one too */
267 if ((ptr
= list_next( &reserved_areas
, ptr
)))
269 struct reserved_area
*next
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
270 if ((char *)addr
+ size
== (char *)next
->base
)
272 area
->size
+= next
->size
;
273 list_remove( &next
->entry
);
281 if ((area
= malloc( sizeof(*area
) )))
285 list_add_before( ptr
, &area
->entry
);
289 static void mmap_remove_reserved_area( void *addr
, SIZE_T size
)
291 struct reserved_area
*area
;
294 if (!((intptr_t)addr
+ size
)) size
--; /* avoid wrap-around */
296 ptr
= list_head( &reserved_areas
);
297 /* find the first area covering address */
300 area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
301 if ((char *)area
->base
>= (char *)addr
+ size
) break; /* outside the range */
302 if ((char *)area
->base
+ area
->size
> (char *)addr
) /* overlaps range */
304 if (area
->base
>= addr
)
306 if ((char *)area
->base
+ area
->size
> (char *)addr
+ size
)
308 /* range overlaps beginning of area only -> shrink area */
309 area
->size
-= (char *)addr
+ size
- (char *)area
->base
;
310 area
->base
= (char *)addr
+ size
;
315 /* range contains the whole area -> remove area completely */
316 ptr
= list_next( &reserved_areas
, ptr
);
317 list_remove( &area
->entry
);
324 if ((char *)area
->base
+ area
->size
> (char *)addr
+ size
)
326 /* range is in the middle of area -> split area in two */
327 struct reserved_area
*new_area
= malloc( sizeof(*new_area
) );
330 new_area
->base
= (char *)addr
+ size
;
331 new_area
->size
= (char *)area
->base
+ area
->size
- (char *)new_area
->base
;
332 list_add_after( ptr
, &new_area
->entry
);
334 else size
= (char *)area
->base
+ area
->size
- (char *)addr
;
335 area
->size
= (char *)addr
- (char *)area
->base
;
340 /* range overlaps end of area only -> shrink area */
341 area
->size
= (char *)addr
- (char *)area
->base
;
345 ptr
= list_next( &reserved_areas
, ptr
);
349 static int mmap_is_in_reserved_area( void *addr
, SIZE_T size
)
351 struct reserved_area
*area
;
354 LIST_FOR_EACH( ptr
, &reserved_areas
)
356 area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
357 if (area
->base
> addr
) break;
358 if ((char *)area
->base
+ area
->size
<= (char *)addr
) continue;
359 /* area must contain block completely */
360 if ((char *)area
->base
+ area
->size
< (char *)addr
+ size
) return -1;
366 static int mmap_enum_reserved_areas( int (*enum_func
)(void *base
, SIZE_T size
, void *arg
),
367 void *arg
, int top_down
)
374 for (ptr
= reserved_areas
.prev
; ptr
!= &reserved_areas
; ptr
= ptr
->prev
)
376 struct reserved_area
*area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
377 if ((ret
= enum_func( area
->base
, area
->size
, arg
))) break;
382 for (ptr
= reserved_areas
.next
; ptr
!= &reserved_areas
; ptr
= ptr
->next
)
384 struct reserved_area
*area
= LIST_ENTRY( ptr
, struct reserved_area
, entry
);
385 if ((ret
= enum_func( area
->base
, area
->size
, arg
))) break;
391 static void *anon_mmap_tryfixed( void *start
, size_t size
, int prot
, int flags
)
395 #ifdef MAP_FIXED_NOREPLACE
396 ptr
= mmap( start
, size
, prot
, MAP_FIXED_NOREPLACE
| MAP_PRIVATE
| MAP_ANON
| flags
, -1, 0 );
397 #elif defined(MAP_TRYFIXED)
398 ptr
= mmap( start
, size
, prot
, MAP_TRYFIXED
| MAP_PRIVATE
| MAP_ANON
| flags
, -1, 0 );
399 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
400 ptr
= mmap( start
, size
, prot
, MAP_FIXED
| MAP_EXCL
| MAP_PRIVATE
| MAP_ANON
| flags
, -1, 0 );
401 if (ptr
== MAP_FAILED
&& errno
== EINVAL
) errno
= EEXIST
;
402 #elif defined(__APPLE__)
403 mach_vm_address_t result
= (mach_vm_address_t
)start
;
404 kern_return_t ret
= mach_vm_map( mach_task_self(), &result
, size
, 0, VM_FLAGS_FIXED
,
405 MEMORY_OBJECT_NULL
, 0, 0, prot
, VM_PROT_ALL
, VM_INHERIT_COPY
);
409 if ((ptr
= anon_mmap_fixed( start
, size
, prot
, flags
)) == MAP_FAILED
)
410 mach_vm_deallocate( mach_task_self(), result
, size
);
414 errno
= (ret
== KERN_NO_SPACE
? EEXIST
: ENOMEM
);
418 ptr
= mmap( start
, size
, prot
, MAP_PRIVATE
| MAP_ANON
| flags
, -1, 0 );
420 if (ptr
!= MAP_FAILED
&& ptr
!= start
)
422 if (is_beyond_limit( ptr
, size
, user_space_limit
))
424 anon_mmap_fixed( ptr
, size
, PROT_NONE
, MAP_NORESERVE
);
425 mmap_add_reserved_area( ptr
, size
);
427 else munmap( ptr
, size
);
434 static void reserve_area( void *addr
, void *end
)
439 static const mach_vm_address_t max_address
= VM_MAX_ADDRESS
;
441 static const mach_vm_address_t max_address
= MACH_VM_MAX_ADDRESS
;
443 mach_vm_address_t address
= (mach_vm_address_t
)addr
;
444 mach_vm_address_t end_address
= (mach_vm_address_t
)end
;
446 if (!end_address
|| max_address
< end_address
)
447 end_address
= max_address
;
449 while (address
< end_address
)
451 mach_vm_address_t hole_address
= address
;
454 vm_region_basic_info_data_64_t info
;
455 mach_msg_type_number_t count
= VM_REGION_BASIC_INFO_COUNT_64
;
456 mach_port_t dummy_object_name
= MACH_PORT_NULL
;
458 /* find the mapped region at or above the current address. */
459 ret
= mach_vm_region(mach_task_self(), &address
, &size
, VM_REGION_BASIC_INFO_64
,
460 (vm_region_info_t
)&info
, &count
, &dummy_object_name
);
461 if (ret
!= KERN_SUCCESS
)
463 address
= max_address
;
467 if (end_address
< address
)
468 address
= end_address
;
469 if (hole_address
< address
)
471 /* found a hole, attempt to reserve it. */
472 size_t hole_size
= address
- hole_address
;
473 mach_vm_address_t alloc_address
= hole_address
;
475 ret
= mach_vm_map( mach_task_self(), &alloc_address
, hole_size
, 0, VM_FLAGS_FIXED
,
476 MEMORY_OBJECT_NULL
, 0, 0, PROT_NONE
, VM_PROT_ALL
, VM_INHERIT_COPY
);
477 if (!ret
) mmap_add_reserved_area( (void*)hole_address
, hole_size
);
478 else if (ret
== KERN_NO_SPACE
)
480 /* something filled (part of) the hole before we could.
481 go back and look again. */
482 address
= hole_address
;
490 size_t size
= (char *)end
- (char *)addr
;
494 if ((ptr
= anon_mmap_tryfixed( addr
, size
, PROT_NONE
, MAP_NORESERVE
)) != MAP_FAILED
)
496 mmap_add_reserved_area( addr
, size
);
499 size
= (size
/ 2) & ~granularity_mask
;
502 reserve_area( addr
, (char *)addr
+ size
);
503 reserve_area( (char *)addr
+ size
, end
);
505 #endif /* __APPLE__ */
509 static void mmap_init( const struct preload_info
*preload_info
)
514 char * const stack_ptr
= &stack
;
516 char *user_space_limit
= (char *)0x7ffe0000;
521 /* check for a reserved area starting at the user space limit */
522 /* to avoid wasting time trying to allocate it again */
523 for (i
= 0; preload_info
[i
].size
; i
++)
525 if ((char *)preload_info
[i
].addr
> user_space_limit
) break;
526 if ((char *)preload_info
[i
].addr
+ preload_info
[i
].size
> user_space_limit
)
528 user_space_limit
= (char *)preload_info
[i
].addr
+ preload_info
[i
].size
;
533 else reserve_area( (void *)0x00010000, (void *)0x40000000 );
537 if (stack_ptr
>= user_space_limit
)
540 char *base
= stack_ptr
- ((unsigned int)stack_ptr
& granularity_mask
) - (granularity_mask
+ 1);
541 if (base
> user_space_limit
) reserve_area( user_space_limit
, base
);
542 base
= stack_ptr
- ((unsigned int)stack_ptr
& granularity_mask
) + (granularity_mask
+ 1);
543 #if defined(linux) || defined(__FreeBSD__) || defined (__FreeBSD_kernel__) || defined(__DragonFly__)
544 /* Heuristic: assume the stack is near the end of the address */
545 /* space, this avoids a lot of futile allocation attempts */
546 end
= (char *)(((unsigned long)base
+ 0x0fffffff) & 0xf0000000);
548 reserve_area( base
, end
);
552 reserve_area( user_space_limit
, 0 );
556 if (preload_info
) return;
557 /* if we don't have a preloader, try to reserve the space now */
558 reserve_area( (void *)0x000000010000, (void *)0x000068000000 );
559 reserve_area( (void *)0x00007ff00000, (void *)0x00007fff0000 );
560 reserve_area( (void *)0x7ffffe000000, (void *)0x7fffffff0000 );
566 /***********************************************************************
567 * get_wow_user_space_limit
569 static void *get_wow_user_space_limit(void)
572 if (main_image_info
.ImageCharacteristics
& IMAGE_FILE_LARGE_ADDRESS_AWARE
) return (void *)0xc0000000;
573 return (void *)0x7fff0000;
575 return user_space_limit
;
579 /***********************************************************************
582 static void add_builtin_module( void *module
, void *handle
)
584 struct builtin_module
*builtin
;
586 if (!(builtin
= malloc( sizeof(*builtin
) ))) return;
587 builtin
->handle
= handle
;
588 builtin
->module
= module
;
589 builtin
->refcount
= 1;
590 builtin
->unix_path
= NULL
;
591 builtin
->unix_handle
= NULL
;
592 list_add_tail( &builtin_modules
, &builtin
->entry
);
596 /***********************************************************************
597 * release_builtin_module
599 static void release_builtin_module( void *module
)
601 struct builtin_module
*builtin
;
603 LIST_FOR_EACH_ENTRY( builtin
, &builtin_modules
, struct builtin_module
, entry
)
605 if (builtin
->module
!= module
) continue;
606 if (!--builtin
->refcount
)
608 list_remove( &builtin
->entry
);
609 if (builtin
->handle
) dlclose( builtin
->handle
);
610 if (builtin
->unix_handle
) dlclose( builtin
->unix_handle
);
611 free( builtin
->unix_path
);
619 /***********************************************************************
620 * get_builtin_so_handle
622 void *get_builtin_so_handle( void *module
)
626 struct builtin_module
*builtin
;
628 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
629 LIST_FOR_EACH_ENTRY( builtin
, &builtin_modules
, struct builtin_module
, entry
)
631 if (builtin
->module
!= module
) continue;
632 ret
= builtin
->handle
;
633 if (ret
) builtin
->refcount
++;
636 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
641 /***********************************************************************
642 * get_builtin_unix_funcs
644 static NTSTATUS
get_builtin_unix_funcs( void *module
, BOOL wow
, const void **funcs
)
646 const char *ptr_name
= wow
? "__wine_unix_call_wow64_funcs" : "__wine_unix_call_funcs";
648 NTSTATUS status
= STATUS_DLL_NOT_FOUND
;
649 struct builtin_module
*builtin
;
651 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
652 LIST_FOR_EACH_ENTRY( builtin
, &builtin_modules
, struct builtin_module
, entry
)
654 if (builtin
->module
!= module
) continue;
655 if (builtin
->unix_path
&& !builtin
->unix_handle
)
656 builtin
->unix_handle
= dlopen( builtin
->unix_path
, RTLD_NOW
);
657 if (builtin
->unix_handle
)
659 *funcs
= dlsym( builtin
->unix_handle
, ptr_name
);
660 status
= *funcs
? STATUS_SUCCESS
: STATUS_ENTRYPOINT_NOT_FOUND
;
664 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
669 /***********************************************************************
670 * load_builtin_unixlib
672 NTSTATUS
load_builtin_unixlib( void *module
, const char *name
)
675 NTSTATUS status
= STATUS_SUCCESS
;
676 struct builtin_module
*builtin
;
678 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
679 LIST_FOR_EACH_ENTRY( builtin
, &builtin_modules
, struct builtin_module
, entry
)
681 if (builtin
->module
!= module
) continue;
682 if (!builtin
->unix_path
) builtin
->unix_path
= strdup( name
);
683 else status
= STATUS_IMAGE_ALREADY_LOADED
;
686 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
691 /***********************************************************************
692 * free_ranges_lower_bound
694 * Returns the first range whose end is not less than addr, or end if there's none.
696 static struct range_entry
*free_ranges_lower_bound( void *addr
)
698 struct range_entry
*begin
= free_ranges
;
699 struct range_entry
*end
= free_ranges_end
;
700 struct range_entry
*mid
;
704 mid
= begin
+ (end
- begin
) / 2;
715 /***********************************************************************
716 * free_ranges_insert_view
718 * Updates the free_ranges after a new view has been created.
720 static void free_ranges_insert_view( struct file_view
*view
)
722 void *view_base
= ROUND_ADDR( view
->base
, granularity_mask
);
723 void *view_end
= ROUND_ADDR( (char *)view
->base
+ view
->size
+ granularity_mask
, granularity_mask
);
724 struct range_entry
*range
= free_ranges_lower_bound( view_base
);
725 struct range_entry
*next
= range
+ 1;
727 /* free_ranges initial value is such that the view is either inside range or before another one. */
728 assert( range
!= free_ranges_end
);
729 assert( range
->end
> view_base
|| next
!= free_ranges_end
);
731 /* this happens because AT_ROUND_TO_PAGE was used with NtMapViewOfSection to force 4kB aligned mapping. */
732 if ((range
->end
> view_base
&& range
->base
>= view_end
) ||
733 (range
->end
== view_base
&& next
->base
>= view_end
))
735 /* on Win64, assert that it's correctly aligned so we're not going to be in trouble later */
737 assert( view
->base
== view_base
);
739 WARN( "range %p - %p is already mapped\n", view_base
, view_end
);
743 /* this should never happen */
744 if (range
->base
> view_base
|| range
->end
< view_end
)
745 ERR( "range %p - %p is already partially mapped\n", view_base
, view_end
);
746 assert( range
->base
<= view_base
&& range
->end
>= view_end
);
748 /* need to split the range in two */
749 if (range
->base
< view_base
&& range
->end
> view_end
)
751 memmove( next
+ 1, next
, (free_ranges_end
- next
) * sizeof(struct range_entry
) );
752 free_ranges_end
+= 1;
753 if ((char *)free_ranges_end
- (char *)free_ranges
> view_block_size
)
754 ERR( "Free range sequence is full, trouble ahead!\n" );
755 assert( (char *)free_ranges_end
- (char *)free_ranges
<= view_block_size
);
757 next
->base
= view_end
;
758 next
->end
= range
->end
;
759 range
->end
= view_base
;
763 /* otherwise we just have to shrink it */
764 if (range
->base
< view_base
)
765 range
->end
= view_base
;
767 range
->base
= view_end
;
769 if (range
->base
< range
->end
) return;
771 /* and possibly remove it if it's now empty */
772 memmove( range
, next
, (free_ranges_end
- next
) * sizeof(struct range_entry
) );
773 free_ranges_end
-= 1;
774 assert( free_ranges_end
- free_ranges
> 0 );
779 /***********************************************************************
780 * free_ranges_remove_view
782 * Updates the free_ranges after a view has been destroyed.
784 static void free_ranges_remove_view( struct file_view
*view
)
786 void *view_base
= ROUND_ADDR( view
->base
, granularity_mask
);
787 void *view_end
= ROUND_ADDR( (char *)view
->base
+ view
->size
+ granularity_mask
, granularity_mask
);
788 struct range_entry
*range
= free_ranges_lower_bound( view_base
);
789 struct range_entry
*next
= range
+ 1;
791 /* It's possible to use AT_ROUND_TO_PAGE on 32bit with NtMapViewOfSection to force 4kB alignment,
792 * and this breaks our assumptions. Look at the views around to check if the range is still in use. */
794 struct file_view
*prev_view
= RB_ENTRY_VALUE( rb_prev( &view
->entry
), struct file_view
, entry
);
795 struct file_view
*next_view
= RB_ENTRY_VALUE( rb_next( &view
->entry
), struct file_view
, entry
);
796 void *prev_view_base
= prev_view
? ROUND_ADDR( prev_view
->base
, granularity_mask
) : NULL
;
797 void *prev_view_end
= prev_view
? ROUND_ADDR( (char *)prev_view
->base
+ prev_view
->size
+ granularity_mask
, granularity_mask
) : NULL
;
798 void *next_view_base
= next_view
? ROUND_ADDR( next_view
->base
, granularity_mask
) : NULL
;
799 void *next_view_end
= next_view
? ROUND_ADDR( (char *)next_view
->base
+ next_view
->size
+ granularity_mask
, granularity_mask
) : NULL
;
801 if ((prev_view_base
< view_end
&& prev_view_end
> view_base
) ||
802 (next_view_base
< view_end
&& next_view_end
> view_base
))
804 WARN( "range %p - %p is still mapped\n", view_base
, view_end
);
809 /* free_ranges initial value is such that the view is either inside range or before another one. */
810 assert( range
!= free_ranges_end
);
811 assert( range
->end
> view_base
|| next
!= free_ranges_end
);
813 /* this should never happen, but we can safely ignore it */
814 if (range
->base
<= view_base
&& range
->end
>= view_end
)
816 WARN( "range %p - %p is already unmapped\n", view_base
, view_end
);
820 /* this should never happen */
821 if (range
->base
< view_end
&& range
->end
> view_base
)
822 ERR( "range %p - %p is already partially unmapped\n", view_base
, view_end
);
823 assert( range
->end
<= view_base
|| range
->base
>= view_end
);
825 /* merge with next if possible */
826 if (range
->end
== view_base
&& next
->base
== view_end
)
828 range
->end
= next
->end
;
829 memmove( next
, next
+ 1, (free_ranges_end
- next
- 1) * sizeof(struct range_entry
) );
830 free_ranges_end
-= 1;
831 assert( free_ranges_end
- free_ranges
> 0 );
833 /* or try growing the range */
834 else if (range
->end
== view_base
)
835 range
->end
= view_end
;
836 else if (range
->base
== view_end
)
837 range
->base
= view_base
;
838 /* otherwise create a new one */
841 memmove( range
+ 1, range
, (free_ranges_end
- range
) * sizeof(struct range_entry
) );
842 free_ranges_end
+= 1;
843 if ((char *)free_ranges_end
- (char *)free_ranges
> view_block_size
)
844 ERR( "Free range sequence is full, trouble ahead!\n" );
845 assert( (char *)free_ranges_end
- (char *)free_ranges
<= view_block_size
);
847 range
->base
= view_base
;
848 range
->end
= view_end
;
853 static inline int is_view_valloc( const struct file_view
*view
)
855 return !(view
->protect
& (SEC_FILE
| SEC_RESERVE
| SEC_COMMIT
));
858 /***********************************************************************
861 * Return the page protection byte.
863 static BYTE
get_page_vprot( const void *addr
)
865 size_t idx
= (size_t)addr
>> page_shift
;
868 if ((idx
>> pages_vprot_shift
) >= pages_vprot_size
) return 0;
869 if (!pages_vprot
[idx
>> pages_vprot_shift
]) return 0;
870 return pages_vprot
[idx
>> pages_vprot_shift
][idx
& pages_vprot_mask
];
872 return pages_vprot
[idx
];
877 /***********************************************************************
878 * get_vprot_range_size
880 * Return the size of the region with equal masked vprot byte.
881 * Also return the protections for the first page.
882 * The function assumes that base and size are page aligned,
883 * base + size does not wrap around and the range is within view so
884 * vprot bytes are allocated for the range. */
885 static SIZE_T
get_vprot_range_size( char *base
, SIZE_T size
, BYTE mask
, BYTE
*vprot
)
887 static const UINT_PTR word_from_byte
= (UINT_PTR
)0x101010101010101;
888 static const UINT_PTR index_align_mask
= sizeof(UINT_PTR
) - 1;
889 SIZE_T curr_idx
, start_idx
, end_idx
, aligned_start_idx
;
890 UINT_PTR vprot_word
, mask_word
;
891 const BYTE
*vprot_ptr
;
893 TRACE("base %p, size %p, mask %#x.\n", base
, (void *)size
, mask
);
895 curr_idx
= start_idx
= (size_t)base
>> page_shift
;
896 end_idx
= start_idx
+ (size
>> page_shift
);
898 aligned_start_idx
= (start_idx
+ index_align_mask
) & ~index_align_mask
;
899 if (aligned_start_idx
> end_idx
) aligned_start_idx
= end_idx
;
902 vprot_ptr
= pages_vprot
[curr_idx
>> pages_vprot_shift
] + (curr_idx
& pages_vprot_mask
);
904 vprot_ptr
= pages_vprot
+ curr_idx
;
908 /* Page count page table is at least the multiples of sizeof(UINT_PTR)
909 * so we don't have to worry about crossing the boundary on unaligned idx values. */
911 for (; curr_idx
< aligned_start_idx
; ++curr_idx
, ++vprot_ptr
)
912 if ((*vprot
^ *vprot_ptr
) & mask
) return (curr_idx
- start_idx
) << page_shift
;
914 vprot_word
= word_from_byte
* *vprot
;
915 mask_word
= word_from_byte
* mask
;
916 for (; curr_idx
< end_idx
; curr_idx
+= sizeof(UINT_PTR
), vprot_ptr
+= sizeof(UINT_PTR
))
919 if (!(curr_idx
& pages_vprot_mask
)) vprot_ptr
= pages_vprot
[curr_idx
>> pages_vprot_shift
];
921 if ((vprot_word
^ *(UINT_PTR
*)vprot_ptr
) & mask_word
)
923 for (; curr_idx
< end_idx
; ++curr_idx
, ++vprot_ptr
)
924 if ((*vprot
^ *vprot_ptr
) & mask
) break;
925 return (curr_idx
- start_idx
) << page_shift
;
931 /***********************************************************************
934 * Set a range of page protection bytes.
936 static void set_page_vprot( const void *addr
, size_t size
, BYTE vprot
)
938 size_t idx
= (size_t)addr
>> page_shift
;
939 size_t end
= ((size_t)addr
+ size
+ page_mask
) >> page_shift
;
942 while (idx
>> pages_vprot_shift
!= end
>> pages_vprot_shift
)
944 size_t dir_size
= pages_vprot_mask
+ 1 - (idx
& pages_vprot_mask
);
945 memset( pages_vprot
[idx
>> pages_vprot_shift
] + (idx
& pages_vprot_mask
), vprot
, dir_size
);
948 memset( pages_vprot
[idx
>> pages_vprot_shift
] + (idx
& pages_vprot_mask
), vprot
, end
- idx
);
950 memset( pages_vprot
+ idx
, vprot
, end
- idx
);
955 /***********************************************************************
956 * set_page_vprot_bits
958 * Set or clear bits in a range of page protection bytes.
960 static void set_page_vprot_bits( const void *addr
, size_t size
, BYTE set
, BYTE clear
)
962 size_t idx
= (size_t)addr
>> page_shift
;
963 size_t end
= ((size_t)addr
+ size
+ page_mask
) >> page_shift
;
966 for ( ; idx
< end
; idx
++)
968 BYTE
*ptr
= pages_vprot
[idx
>> pages_vprot_shift
] + (idx
& pages_vprot_mask
);
969 *ptr
= (*ptr
& ~clear
) | set
;
972 for ( ; idx
< end
; idx
++) pages_vprot
[idx
] = (pages_vprot
[idx
] & ~clear
) | set
;
977 /***********************************************************************
980 * Allocate the page protection bytes for a given range.
982 static BOOL
alloc_pages_vprot( const void *addr
, size_t size
)
985 size_t idx
= (size_t)addr
>> page_shift
;
986 size_t end
= ((size_t)addr
+ size
+ page_mask
) >> page_shift
;
990 assert( end
<= pages_vprot_size
<< pages_vprot_shift
);
991 for (i
= idx
>> pages_vprot_shift
; i
< (end
+ pages_vprot_mask
) >> pages_vprot_shift
; i
++)
993 if (pages_vprot
[i
]) continue;
994 if ((ptr
= anon_mmap_alloc( pages_vprot_mask
+ 1, PROT_READ
| PROT_WRITE
)) == MAP_FAILED
)
996 pages_vprot
[i
] = ptr
;
1003 static inline UINT64
maskbits( size_t idx
)
1005 return ~(UINT64
)0 << (idx
& 63);
1008 /***********************************************************************
1012 static void set_arm64ec_range( const void *addr
, size_t size
)
1014 size_t idx
= (size_t)addr
>> page_shift
;
1015 size_t end
= ((size_t)addr
+ size
+ page_mask
) >> page_shift
;
1016 size_t pos
= idx
/ 64;
1017 size_t end_pos
= end
/ 64;
1021 arm64ec_map
[pos
++] |= maskbits( idx
);
1022 while (pos
< end_pos
) arm64ec_map
[pos
++] = ~(UINT64
)0;
1023 if (end
& 63) arm64ec_map
[pos
] |= ~maskbits( end
);
1025 else arm64ec_map
[pos
] |= maskbits( idx
) & ~maskbits( end
);
1029 /***********************************************************************
1030 * clear_arm64ec_range
1032 static void clear_arm64ec_range( const void *addr
, size_t size
)
1034 size_t idx
= (size_t)addr
>> page_shift
;
1035 size_t end
= ((size_t)addr
+ size
+ page_mask
) >> page_shift
;
1036 size_t pos
= idx
/ 64;
1037 size_t end_pos
= end
/ 64;
1041 arm64ec_map
[pos
++] &= ~maskbits( idx
);
1042 while (pos
< end_pos
) arm64ec_map
[pos
++] = 0;
1043 if (end
& 63) arm64ec_map
[pos
] &= maskbits( end
);
1045 else arm64ec_map
[pos
] &= ~maskbits( idx
) | maskbits( end
);
1049 /***********************************************************************
1052 * View comparison function used for the rb tree.
1054 static int compare_view( const void *addr
, const struct wine_rb_entry
*entry
)
1056 struct file_view
*view
= WINE_RB_ENTRY_VALUE( entry
, struct file_view
, entry
);
1058 if (addr
< view
->base
) return -1;
1059 if (addr
> view
->base
) return 1;
1064 /***********************************************************************
1067 static const char *get_prot_str( BYTE prot
)
1069 static char buffer
[6];
1070 buffer
[0] = (prot
& VPROT_COMMITTED
) ? 'c' : '-';
1071 buffer
[1] = (prot
& VPROT_GUARD
) ? 'g' : ((prot
& VPROT_WRITEWATCH
) ? 'H' : '-');
1072 buffer
[2] = (prot
& VPROT_READ
) ? 'r' : '-';
1073 buffer
[3] = (prot
& VPROT_WRITECOPY
) ? 'W' : ((prot
& VPROT_WRITE
) ? 'w' : '-');
1074 buffer
[4] = (prot
& VPROT_EXEC
) ? 'x' : '-';
1080 /***********************************************************************
1083 * Convert page protections to protection for mmap/mprotect.
1085 static int get_unix_prot( BYTE vprot
)
1088 if ((vprot
& VPROT_COMMITTED
) && !(vprot
& VPROT_GUARD
))
1090 if (vprot
& VPROT_READ
) prot
|= PROT_READ
;
1091 if (vprot
& VPROT_WRITE
) prot
|= PROT_WRITE
| PROT_READ
;
1092 if (vprot
& VPROT_WRITECOPY
) prot
|= PROT_WRITE
| PROT_READ
;
1093 if (vprot
& VPROT_EXEC
) prot
|= PROT_EXEC
| PROT_READ
;
1094 if (vprot
& VPROT_WRITEWATCH
) prot
&= ~PROT_WRITE
;
1096 if (!prot
) prot
= PROT_NONE
;
1101 /***********************************************************************
1104 static void dump_view( struct file_view
*view
)
1107 char *addr
= view
->base
;
1108 BYTE prot
= get_page_vprot( addr
);
1110 TRACE( "View: %p - %p", addr
, addr
+ view
->size
- 1 );
1111 if (view
->protect
& VPROT_SYSTEM
)
1112 TRACE( " (builtin image)\n" );
1113 else if (view
->protect
& SEC_IMAGE
)
1114 TRACE( " (image)\n" );
1115 else if (view
->protect
& SEC_FILE
)
1116 TRACE( " (file)\n" );
1117 else if (view
->protect
& (SEC_RESERVE
| SEC_COMMIT
))
1118 TRACE( " (anonymous)\n" );
1120 TRACE( " (valloc)\n");
1122 for (count
= i
= 1; i
< view
->size
>> page_shift
; i
++, count
++)
1124 BYTE next
= get_page_vprot( addr
+ (count
<< page_shift
) );
1125 if (next
== prot
) continue;
1126 TRACE( " %p - %p %s\n",
1127 addr
, addr
+ (count
<< page_shift
) - 1, get_prot_str(prot
) );
1128 addr
+= (count
<< page_shift
);
1133 TRACE( " %p - %p %s\n",
1134 addr
, addr
+ (count
<< page_shift
) - 1, get_prot_str(prot
) );
1138 /***********************************************************************
1141 #ifdef WINE_VM_DEBUG
1142 static void VIRTUAL_Dump(void)
1145 struct file_view
*view
;
1147 TRACE( "Dump of all virtual memory views:\n" );
1148 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
1149 WINE_RB_FOR_EACH_ENTRY( view
, &views_tree
, struct file_view
, entry
)
1153 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
1158 /***********************************************************************
1161 * Find the view containing a given address. virtual_mutex must be held by caller.
1170 static struct file_view
*find_view( const void *addr
, size_t size
)
1172 struct wine_rb_entry
*ptr
= views_tree
.root
;
1174 if ((const char *)addr
+ size
< (const char *)addr
) return NULL
; /* overflow */
1178 struct file_view
*view
= WINE_RB_ENTRY_VALUE( ptr
, struct file_view
, entry
);
1180 if (view
->base
> addr
) ptr
= ptr
->left
;
1181 else if ((const char *)view
->base
+ view
->size
<= (const char *)addr
) ptr
= ptr
->right
;
1182 else if ((const char *)view
->base
+ view
->size
< (const char *)addr
+ size
) break; /* size too large */
1189 /***********************************************************************
1190 * get_zero_bits_mask
1192 static inline UINT_PTR
get_zero_bits_mask( ULONG_PTR zero_bits
)
1196 if (zero_bits
== 0) return 0;
1198 if (zero_bits
< 32) shift
= 32 + zero_bits
;
1203 if (zero_bits
>> 32) { shift
-= 32; zero_bits
>>= 32; }
1205 if (zero_bits
>> 16) { shift
-= 16; zero_bits
>>= 16; }
1206 if (zero_bits
>> 8) { shift
-= 8; zero_bits
>>= 8; }
1207 if (zero_bits
>> 4) { shift
-= 4; zero_bits
>>= 4; }
1208 if (zero_bits
>> 2) { shift
-= 2; zero_bits
>>= 2; }
1209 if (zero_bits
>> 1) { shift
-= 1; }
1211 return (UINT_PTR
)((~(UINT64
)0) >> shift
);
1215 /***********************************************************************
1216 * is_write_watch_range
1218 static inline BOOL
is_write_watch_range( const void *addr
, size_t size
)
1220 struct file_view
*view
= find_view( addr
, size
);
1221 return view
&& (view
->protect
& VPROT_WRITEWATCH
);
1225 /***********************************************************************
1228 * Find the first view overlapping at least part of the specified range.
1229 * virtual_mutex must be held by caller.
1231 static struct file_view
*find_view_range( const void *addr
, size_t size
)
1233 struct wine_rb_entry
*ptr
= views_tree
.root
;
1237 struct file_view
*view
= WINE_RB_ENTRY_VALUE( ptr
, struct file_view
, entry
);
1239 if ((const char *)view
->base
>= (const char *)addr
+ size
) ptr
= ptr
->left
;
1240 else if ((const char *)view
->base
+ view
->size
<= (const char *)addr
) ptr
= ptr
->right
;
1247 /***********************************************************************
1248 * find_view_inside_range
1250 * Find first (resp. last, if top_down) view inside a range.
1251 * virtual_mutex must be held by caller.
1253 static struct wine_rb_entry
*find_view_inside_range( void **base_ptr
, void **end_ptr
, int top_down
)
1255 struct wine_rb_entry
*first
= NULL
, *ptr
= views_tree
.root
;
1256 void *base
= *base_ptr
, *end
= *end_ptr
;
1258 /* find the first (resp. last) view inside the range */
1261 struct file_view
*view
= WINE_RB_ENTRY_VALUE( ptr
, struct file_view
, entry
);
1262 if ((char *)view
->base
+ view
->size
>= (char *)end
)
1264 end
= min( end
, view
->base
);
1267 else if (view
->base
<= base
)
1269 base
= max( (char *)base
, (char *)view
->base
+ view
->size
);
1275 ptr
= top_down
? ptr
->right
: ptr
->left
;
1285 /***********************************************************************
1288 * Try mmaping some expected free memory region, eventually stepping and
1289 * retrying inside it, and return where it actually succeeded, or NULL.
1291 static void* try_map_free_area( void *base
, void *end
, ptrdiff_t step
,
1292 void *start
, size_t size
, int unix_prot
)
1296 while (start
&& base
<= start
&& (char*)start
+ size
<= (char*)end
)
1298 if ((ptr
= anon_mmap_tryfixed( start
, size
, unix_prot
, 0 )) != MAP_FAILED
) return start
;
1299 TRACE( "Found free area is already mapped, start %p.\n", start
);
1300 if (errno
!= EEXIST
)
1302 ERR( "mmap() error %s, range %p-%p, unix_prot %#x.\n",
1303 strerror(errno
), start
, (char *)start
+ size
, unix_prot
);
1306 if ((step
> 0 && (char *)end
- (char *)start
< step
) ||
1307 (step
< 0 && (char *)start
- (char *)base
< -step
) ||
1310 start
= (char *)start
+ step
;
1317 /***********************************************************************
1320 * Find a free area between views inside the specified range and map it.
1321 * virtual_mutex must be held by caller.
1323 static void *map_free_area( void *base
, void *end
, size_t size
, int top_down
, int unix_prot
, size_t align_mask
)
1325 struct wine_rb_entry
*first
= find_view_inside_range( &base
, &end
, top_down
);
1326 ptrdiff_t step
= top_down
? -(align_mask
+ 1) : (align_mask
+ 1);
1331 start
= ROUND_ADDR( (char *)end
- size
, align_mask
);
1332 if (start
>= end
|| start
< base
) return NULL
;
1336 struct file_view
*view
= WINE_RB_ENTRY_VALUE( first
, struct file_view
, entry
);
1337 if ((start
= try_map_free_area( (char *)view
->base
+ view
->size
, (char *)start
+ size
, step
,
1338 start
, size
, unix_prot
))) break;
1339 start
= ROUND_ADDR( (char *)view
->base
- size
, align_mask
);
1340 /* stop if remaining space is not large enough */
1341 if (!start
|| start
>= end
|| start
< base
) return NULL
;
1342 first
= rb_prev( first
);
1347 start
= ROUND_ADDR( (char *)base
+ align_mask
, align_mask
);
1348 if (!start
|| start
>= end
|| (char *)end
- (char *)start
< size
) return NULL
;
1352 struct file_view
*view
= WINE_RB_ENTRY_VALUE( first
, struct file_view
, entry
);
1353 if ((start
= try_map_free_area( start
, view
->base
, step
,
1354 start
, size
, unix_prot
))) break;
1355 start
= ROUND_ADDR( (char *)view
->base
+ view
->size
+ align_mask
, align_mask
);
1356 /* stop if remaining space is not large enough */
1357 if (!start
|| start
>= end
|| (char *)end
- (char *)start
< size
) return NULL
;
1358 first
= rb_next( first
);
1363 return try_map_free_area( base
, end
, step
, start
, size
, unix_prot
);
1369 /***********************************************************************
1370 * find_reserved_free_area
1372 * Find a free area between views inside the specified range.
1373 * virtual_mutex must be held by caller.
1374 * The range must be inside the preloader reserved range.
1376 static void *find_reserved_free_area( void *base
, void *end
, size_t size
, int top_down
, size_t align_mask
)
1378 struct range_entry
*range
;
1381 base
= ROUND_ADDR( (char *)base
+ align_mask
, align_mask
);
1382 end
= (char *)ROUND_ADDR( (char *)end
- size
, align_mask
) + size
;
1386 start
= (char *)end
- size
;
1387 range
= free_ranges_lower_bound( start
);
1388 assert(range
!= free_ranges_end
&& range
->end
>= start
);
1390 if ((char *)range
->end
- (char *)start
< size
) start
= ROUND_ADDR( (char *)range
->end
- size
, align_mask
);
1393 if (start
>= end
|| start
< base
|| (char *)end
- (char *)start
< size
) return NULL
;
1394 if (start
< range
->end
&& start
>= range
->base
&& (char *)range
->end
- (char *)start
>= size
) break;
1395 if (--range
< free_ranges
) return NULL
;
1396 start
= ROUND_ADDR( (char *)range
->end
- size
, align_mask
);
1403 range
= free_ranges_lower_bound( start
);
1404 assert(range
!= free_ranges_end
&& range
->end
>= start
);
1406 if (start
< range
->base
) start
= ROUND_ADDR( (char *)range
->base
+ align_mask
, align_mask
);
1409 if (start
>= end
|| start
< base
|| (char *)end
- (char *)start
< size
) return NULL
;
1410 if (start
< range
->end
&& start
>= range
->base
&& (char *)range
->end
- (char *)start
>= size
) break;
1411 if (++range
== free_ranges_end
) return NULL
;
1412 start
= ROUND_ADDR( (char *)range
->base
+ align_mask
, align_mask
);
1420 /***********************************************************************
1423 * Add a reserved area to the list maintained by libwine.
1424 * virtual_mutex must be held by caller.
1426 static void add_reserved_area( void *addr
, size_t size
)
1428 TRACE( "adding %p-%p\n", addr
, (char *)addr
+ size
);
1430 if (addr
< user_space_limit
)
1432 /* unmap the part of the area that is below the limit */
1433 assert( (char *)addr
+ size
> (char *)user_space_limit
);
1434 munmap( addr
, (char *)user_space_limit
- (char *)addr
);
1435 size
-= (char *)user_space_limit
- (char *)addr
;
1436 addr
= user_space_limit
;
1438 /* blow away existing mappings */
1439 anon_mmap_fixed( addr
, size
, PROT_NONE
, MAP_NORESERVE
);
1440 mmap_add_reserved_area( addr
, size
);
1444 /***********************************************************************
1445 * remove_reserved_area
1447 * Remove a reserved area from the list maintained by libwine.
1448 * virtual_mutex must be held by caller.
1450 static void remove_reserved_area( void *addr
, size_t size
)
1452 struct file_view
*view
;
1454 TRACE( "removing %p-%p\n", addr
, (char *)addr
+ size
);
1455 mmap_remove_reserved_area( addr
, size
);
1457 /* unmap areas not covered by an existing view */
1458 WINE_RB_FOR_EACH_ENTRY( view
, &views_tree
, struct file_view
, entry
)
1460 if ((char *)view
->base
>= (char *)addr
+ size
) break;
1461 if ((char *)view
->base
+ view
->size
<= (char *)addr
) continue;
1462 if (view
->base
> addr
) munmap( addr
, (char *)view
->base
- (char *)addr
);
1463 if ((char *)view
->base
+ view
->size
> (char *)addr
+ size
) return;
1464 size
= (char *)addr
+ size
- ((char *)view
->base
+ view
->size
);
1465 addr
= (char *)view
->base
+ view
->size
;
1467 munmap( addr
, size
);
1471 struct area_boundary
1478 /***********************************************************************
1479 * get_area_boundary_callback
1481 * Get lowest boundary address between reserved area and non-reserved area
1482 * in the specified region. If no boundaries are found, result is NULL.
1483 * virtual_mutex must be held by caller.
1485 static int get_area_boundary_callback( void *start
, SIZE_T size
, void *arg
)
1487 struct area_boundary
*area
= arg
;
1488 void *end
= (char *)start
+ size
;
1490 area
->boundary
= NULL
;
1491 if (area
->base
>= end
) return 0;
1492 if ((char *)start
>= (char *)area
->base
+ area
->size
) return 1;
1493 if (area
->base
>= start
)
1495 if ((char *)area
->base
+ area
->size
> (char *)end
)
1497 area
->boundary
= end
;
1502 area
->boundary
= start
;
1507 /***********************************************************************
1510 * Unmap an area, or simply replace it by an empty mapping if it is
1511 * in a reserved area. virtual_mutex must be held by caller.
1513 static inline void unmap_area( void *addr
, size_t size
)
1515 switch (mmap_is_in_reserved_area( addr
, size
))
1517 case -1: /* partially in a reserved area */
1519 struct area_boundary area
;
1523 mmap_enum_reserved_areas( get_area_boundary_callback
, &area
, 0 );
1524 assert( area
.boundary
);
1525 lower_size
= (char *)area
.boundary
- (char *)addr
;
1526 unmap_area( addr
, lower_size
);
1527 unmap_area( area
.boundary
, size
- lower_size
);
1530 case 1: /* in a reserved area */
1531 anon_mmap_fixed( addr
, size
, PROT_NONE
, MAP_NORESERVE
);
1534 case 0: /* not in a reserved area */
1535 if (is_beyond_limit( addr
, size
, user_space_limit
))
1536 add_reserved_area( addr
, size
);
1538 munmap( addr
, size
);
1544 /***********************************************************************
1547 * Allocate a new view. virtual_mutex must be held by caller.
1549 static struct file_view
*alloc_view(void)
1553 struct file_view
*ret
= next_free_view
;
1554 next_free_view
= *(struct file_view
**)ret
;
1557 if (view_block_start
== view_block_end
)
1559 void *ptr
= anon_mmap_alloc( view_block_size
, PROT_READ
| PROT_WRITE
);
1560 if (ptr
== MAP_FAILED
) return NULL
;
1561 view_block_start
= ptr
;
1562 view_block_end
= view_block_start
+ view_block_size
/ sizeof(*view_block_start
);
1564 return view_block_start
++;
1568 /***********************************************************************
1571 * Deletes a view. virtual_mutex must be held by caller.
1573 static void delete_view( struct file_view
*view
) /* [in] View */
1575 if (!(view
->protect
& VPROT_SYSTEM
)) unmap_area( view
->base
, view
->size
);
1576 set_page_vprot( view
->base
, view
->size
, 0 );
1577 if (arm64ec_map
) clear_arm64ec_range( view
->base
, view
->size
);
1578 if (mmap_is_in_reserved_area( view
->base
, view
->size
))
1579 free_ranges_remove_view( view
);
1580 wine_rb_remove( &views_tree
, &view
->entry
);
1581 *(struct file_view
**)view
= next_free_view
;
1582 next_free_view
= view
;
1586 /***********************************************************************
1589 * Create a view. virtual_mutex must be held by caller.
1591 static NTSTATUS
create_view( struct file_view
**view_ret
, void *base
, size_t size
, unsigned int vprot
)
1593 struct file_view
*view
;
1594 int unix_prot
= get_unix_prot( vprot
);
1596 assert( !((UINT_PTR
)base
& page_mask
) );
1597 assert( !(size
& page_mask
) );
1599 /* Check for overlapping views. This can happen if the previous view
1600 * was a system view that got unmapped behind our back. In that case
1601 * we recover by simply deleting it. */
1603 while ((view
= find_view_range( base
, size
)))
1605 TRACE( "overlapping view %p-%p for %p-%p\n",
1606 view
->base
, (char *)view
->base
+ view
->size
, base
, (char *)base
+ size
);
1607 assert( view
->protect
& VPROT_SYSTEM
);
1608 delete_view( view
);
1611 if (!alloc_pages_vprot( base
, size
)) return STATUS_NO_MEMORY
;
1613 /* Create the view structure */
1615 if (!(view
= alloc_view()))
1617 FIXME( "out of memory for %p-%p\n", base
, (char *)base
+ size
);
1618 return STATUS_NO_MEMORY
;
1623 view
->protect
= vprot
;
1624 set_page_vprot( base
, size
, vprot
);
1626 wine_rb_put( &views_tree
, view
->base
, &view
->entry
);
1627 if (mmap_is_in_reserved_area( view
->base
, view
->size
))
1628 free_ranges_insert_view( view
);
1632 if (force_exec_prot
&& (unix_prot
& PROT_READ
) && !(unix_prot
& PROT_EXEC
))
1634 TRACE( "forcing exec permission on %p-%p\n", base
, (char *)base
+ size
- 1 );
1635 mprotect( base
, size
, unix_prot
| PROT_EXEC
);
1637 return STATUS_SUCCESS
;
1641 /***********************************************************************
1644 * Convert page protections to Win32 flags.
1646 static DWORD
get_win32_prot( BYTE vprot
, unsigned int map_prot
)
1648 DWORD ret
= VIRTUAL_Win32Flags
[vprot
& 0x0f];
1649 if (vprot
& VPROT_GUARD
) ret
|= PAGE_GUARD
;
1650 if (map_prot
& SEC_NOCACHE
) ret
|= PAGE_NOCACHE
;
1655 /***********************************************************************
1658 * Build page protections from Win32 flags.
1660 static NTSTATUS
get_vprot_flags( DWORD protect
, unsigned int *vprot
, BOOL image
)
1662 switch(protect
& 0xff)
1665 *vprot
= VPROT_READ
;
1667 case PAGE_READWRITE
:
1669 *vprot
= VPROT_READ
| VPROT_WRITECOPY
;
1671 *vprot
= VPROT_READ
| VPROT_WRITE
;
1673 case PAGE_WRITECOPY
:
1674 *vprot
= VPROT_READ
| VPROT_WRITECOPY
;
1677 *vprot
= VPROT_EXEC
;
1679 case PAGE_EXECUTE_READ
:
1680 *vprot
= VPROT_EXEC
| VPROT_READ
;
1682 case PAGE_EXECUTE_READWRITE
:
1684 *vprot
= VPROT_EXEC
| VPROT_READ
| VPROT_WRITECOPY
;
1686 *vprot
= VPROT_EXEC
| VPROT_READ
| VPROT_WRITE
;
1688 case PAGE_EXECUTE_WRITECOPY
:
1689 *vprot
= VPROT_EXEC
| VPROT_READ
| VPROT_WRITECOPY
;
1695 return STATUS_INVALID_PAGE_PROTECTION
;
1697 if (protect
& PAGE_GUARD
) *vprot
|= VPROT_GUARD
;
1698 return STATUS_SUCCESS
;
1702 /***********************************************************************
1705 * Wrapper for mprotect, adds PROT_EXEC if forced by force_exec_prot
1707 static inline int mprotect_exec( void *base
, size_t size
, int unix_prot
)
1709 if (force_exec_prot
&& (unix_prot
& PROT_READ
) && !(unix_prot
& PROT_EXEC
))
1711 TRACE( "forcing exec permission on %p-%p\n", base
, (char *)base
+ size
- 1 );
1712 if (!mprotect( base
, size
, unix_prot
| PROT_EXEC
)) return 0;
1713 /* exec + write may legitimately fail, in that case fall back to write only */
1714 if (!(unix_prot
& PROT_WRITE
)) return -1;
1717 return mprotect( base
, size
, unix_prot
);
1721 /***********************************************************************
1724 * Call mprotect on a page range, applying the protections from the per-page byte.
1726 static void mprotect_range( void *base
, size_t size
, BYTE set
, BYTE clear
)
1729 char *addr
= ROUND_ADDR( base
, page_mask
);
1732 size
= ROUND_SIZE( base
, size
);
1733 prot
= get_unix_prot( (get_page_vprot( addr
) & ~clear
) | set
);
1734 for (count
= i
= 1; i
< size
>> page_shift
; i
++, count
++)
1736 next
= get_unix_prot( (get_page_vprot( addr
+ (count
<< page_shift
) ) & ~clear
) | set
);
1737 if (next
== prot
) continue;
1738 mprotect_exec( addr
, count
<< page_shift
, prot
);
1739 addr
+= count
<< page_shift
;
1743 if (count
) mprotect_exec( addr
, count
<< page_shift
, prot
);
1747 /***********************************************************************
1750 * Change the protection of a range of pages.
1752 static BOOL
set_vprot( struct file_view
*view
, void *base
, size_t size
, BYTE vprot
)
1754 int unix_prot
= get_unix_prot(vprot
);
1756 if (view
->protect
& VPROT_WRITEWATCH
)
1758 /* each page may need different protections depending on write watch flag */
1759 set_page_vprot_bits( base
, size
, vprot
& ~VPROT_WRITEWATCH
, ~vprot
& ~VPROT_WRITEWATCH
);
1760 mprotect_range( base
, size
, 0, 0 );
1763 if (mprotect_exec( base
, size
, unix_prot
)) return FALSE
;
1764 set_page_vprot( base
, size
, vprot
);
1769 /***********************************************************************
1772 * Set page protections on a range of pages
1774 static NTSTATUS
set_protection( struct file_view
*view
, void *base
, SIZE_T size
, ULONG protect
)
1779 if ((status
= get_vprot_flags( protect
, &vprot
, view
->protect
& SEC_IMAGE
))) return status
;
1780 if (is_view_valloc( view
))
1782 if (vprot
& VPROT_WRITECOPY
) return STATUS_INVALID_PAGE_PROTECTION
;
1786 BYTE access
= vprot
& (VPROT_READ
| VPROT_WRITE
| VPROT_EXEC
);
1787 if ((view
->protect
& access
) != access
) return STATUS_INVALID_PAGE_PROTECTION
;
1790 if (!set_vprot( view
, base
, size
, vprot
| VPROT_COMMITTED
)) return STATUS_ACCESS_DENIED
;
1791 return STATUS_SUCCESS
;
1795 /***********************************************************************
1796 * update_write_watches
1798 static void update_write_watches( void *base
, size_t size
, size_t accessed_size
)
1800 TRACE( "updating watch %p-%p-%p\n", base
, (char *)base
+ accessed_size
, (char *)base
+ size
);
1801 /* clear write watch flag on accessed pages */
1802 set_page_vprot_bits( base
, accessed_size
, 0, VPROT_WRITEWATCH
);
1803 /* restore page protections on the entire range */
1804 mprotect_range( base
, size
, 0, 0 );
1808 /***********************************************************************
1809 * reset_write_watches
1811 * Reset write watches in a memory range.
1813 static void reset_write_watches( void *base
, SIZE_T size
)
1815 set_page_vprot_bits( base
, size
, VPROT_WRITEWATCH
, 0 );
1816 mprotect_range( base
, size
, 0, 0 );
1820 /***********************************************************************
1823 * Release the extra memory while keeping the range starting on the alignment boundary.
1825 static inline void *unmap_extra_space( void *ptr
, size_t total_size
, size_t wanted_size
, size_t align_mask
)
1827 if ((ULONG_PTR
)ptr
& align_mask
)
1829 size_t extra
= align_mask
+ 1 - ((ULONG_PTR
)ptr
& align_mask
);
1830 munmap( ptr
, extra
);
1831 ptr
= (char *)ptr
+ extra
;
1832 total_size
-= extra
;
1834 if (total_size
> wanted_size
)
1835 munmap( (char *)ptr
+ wanted_size
, total_size
- wanted_size
);
1849 /***********************************************************************
1850 * alloc_reserved_area_callback
1852 * Try to map some space inside a reserved area. Callback for mmap_enum_reserved_areas.
1854 static int alloc_reserved_area_callback( void *start
, SIZE_T size
, void *arg
)
1856 struct alloc_area
*alloc
= arg
;
1857 void *end
= (char *)start
+ size
;
1859 if (start
< address_space_start
) start
= address_space_start
;
1860 if (is_beyond_limit( start
, size
, alloc
->limit
)) end
= alloc
->limit
;
1861 if (start
>= end
) return 0;
1863 /* make sure we don't touch the preloader reserved range */
1864 if (preload_reserve_end
>= start
)
1866 if (preload_reserve_end
>= end
)
1868 if (preload_reserve_start
<= start
) return 0; /* no space in that area */
1869 if (preload_reserve_start
< end
) end
= preload_reserve_start
;
1871 else if (preload_reserve_start
<= start
) start
= preload_reserve_end
;
1874 /* range is split in two by the preloader reservation, try first part */
1875 if ((alloc
->result
= find_reserved_free_area( start
, preload_reserve_start
, alloc
->size
,
1876 alloc
->top_down
, alloc
->align_mask
)))
1878 /* then fall through to try second part */
1879 start
= preload_reserve_end
;
1882 if ((alloc
->result
= find_reserved_free_area( start
, end
, alloc
->size
, alloc
->top_down
, alloc
->align_mask
)))
1888 /***********************************************************************
1891 * mmap the fixed memory area.
1892 * virtual_mutex must be held by caller.
1894 static NTSTATUS
map_fixed_area( void *base
, size_t size
, unsigned int vprot
)
1898 switch (mmap_is_in_reserved_area( base
, size
))
1900 case -1: /* partially in a reserved area */
1903 struct area_boundary area
;
1907 mmap_enum_reserved_areas( get_area_boundary_callback
, &area
, 0 );
1908 assert( area
.boundary
);
1909 lower_size
= (char *)area
.boundary
- (char *)base
;
1910 status
= map_fixed_area( base
, lower_size
, vprot
);
1911 if (status
== STATUS_SUCCESS
)
1913 status
= map_fixed_area( area
.boundary
, size
- lower_size
, vprot
);
1914 if (status
!= STATUS_SUCCESS
) unmap_area( base
, lower_size
);
1918 case 0: /* not in a reserved area, do a normal allocation */
1919 if ((ptr
= anon_mmap_tryfixed( base
, size
, get_unix_prot(vprot
), 0 )) == MAP_FAILED
)
1921 if (errno
== ENOMEM
) return STATUS_NO_MEMORY
;
1922 if (errno
== EEXIST
) return STATUS_CONFLICTING_ADDRESSES
;
1923 return STATUS_INVALID_PARAMETER
;
1928 case 1: /* in a reserved area, make sure the address is available */
1929 if (find_view_range( base
, size
)) return STATUS_CONFLICTING_ADDRESSES
;
1930 /* replace the reserved area by our mapping */
1931 if ((ptr
= anon_mmap_fixed( base
, size
, get_unix_prot(vprot
), 0 )) != base
)
1932 return STATUS_INVALID_PARAMETER
;
1935 if (is_beyond_limit( ptr
, size
, working_set_limit
)) working_set_limit
= address_space_limit
;
1936 return STATUS_SUCCESS
;
1939 /***********************************************************************
1942 * Create a view and mmap the corresponding memory area.
1943 * virtual_mutex must be held by caller.
1945 static NTSTATUS
map_view( struct file_view
**view_ret
, void *base
, size_t size
,
1946 int top_down
, unsigned int vprot
, ULONG_PTR limit
, size_t align_mask
)
1953 if (is_beyond_limit( base
, size
, address_space_limit
))
1954 return STATUS_WORKING_SET_LIMIT_RANGE
;
1955 if (limit
&& is_beyond_limit( base
, size
, (void *)limit
))
1956 return STATUS_CONFLICTING_ADDRESSES
;
1957 status
= map_fixed_area( base
, size
, vprot
);
1958 if (status
!= STATUS_SUCCESS
) return status
;
1963 struct alloc_area alloc
;
1966 if (!align_mask
) align_mask
= granularity_mask
;
1967 view_size
= size
+ align_mask
+ 1;
1970 alloc
.top_down
= top_down
;
1971 alloc
.limit
= limit
? min( (void *)(limit
+ 1), user_space_limit
) : user_space_limit
;
1972 alloc
.align_mask
= align_mask
;
1974 if (mmap_enum_reserved_areas( alloc_reserved_area_callback
, &alloc
, top_down
))
1977 TRACE( "got mem in reserved area %p-%p\n", ptr
, (char *)ptr
+ size
);
1978 if (anon_mmap_fixed( ptr
, size
, get_unix_prot(vprot
), 0 ) != ptr
)
1979 return STATUS_INVALID_PARAMETER
;
1985 if (!(ptr
= map_free_area( address_space_start
, alloc
.limit
, size
,
1986 top_down
, get_unix_prot(vprot
), align_mask
)))
1987 return STATUS_NO_MEMORY
;
1988 TRACE( "got mem with map_free_area %p-%p\n", ptr
, (char *)ptr
+ size
);
1994 if ((ptr
= anon_mmap_alloc( view_size
, get_unix_prot(vprot
) )) == MAP_FAILED
)
1996 if (errno
== ENOMEM
) return STATUS_NO_MEMORY
;
1997 return STATUS_INVALID_PARAMETER
;
1999 TRACE( "got mem with anon mmap %p-%p\n", ptr
, (char *)ptr
+ size
);
2000 /* if we got something beyond the user limit, unmap it and retry */
2001 if (is_beyond_limit( ptr
, view_size
, user_space_limit
)) add_reserved_area( ptr
, view_size
);
2004 ptr
= unmap_extra_space( ptr
, view_size
, size
, align_mask
);
2007 status
= create_view( view_ret
, ptr
, size
, vprot
);
2008 if (status
!= STATUS_SUCCESS
) unmap_area( ptr
, size
);
2013 /***********************************************************************
2014 * map_file_into_view
2016 * Wrapper for mmap() to map a file into a view, falling back to read if mmap fails.
2017 * virtual_mutex must be held by caller.
2019 static NTSTATUS
map_file_into_view( struct file_view
*view
, int fd
, size_t start
, size_t size
,
2020 off_t offset
, unsigned int vprot
, BOOL removable
)
2023 int prot
= get_unix_prot( vprot
| VPROT_COMMITTED
/* make sure it is accessible */ );
2024 unsigned int flags
= MAP_FIXED
| ((vprot
& VPROT_WRITECOPY
) ? MAP_PRIVATE
: MAP_SHARED
);
2026 assert( start
< view
->size
);
2027 assert( start
+ size
<= view
->size
);
2029 if (force_exec_prot
&& (vprot
& VPROT_READ
))
2031 TRACE( "forcing exec permission on mapping %p-%p\n",
2032 (char *)view
->base
+ start
, (char *)view
->base
+ start
+ size
- 1 );
2036 /* only try mmap if media is not removable (or if we require write access) */
2037 if (!removable
|| (flags
& MAP_SHARED
))
2039 if (mmap( (char *)view
->base
+ start
, size
, prot
, flags
, fd
, offset
) != MAP_FAILED
)
2044 case EINVAL
: /* file offset is not page-aligned, fall back to read() */
2045 if (flags
& MAP_SHARED
) return STATUS_INVALID_PARAMETER
;
2048 case ENODEV
: /* filesystem doesn't support mmap(), fall back to read() */
2049 if (vprot
& VPROT_WRITE
)
2051 ERR( "shared writable mmap not supported, broken filesystem?\n" );
2052 return STATUS_NOT_SUPPORTED
;
2056 case EPERM
: /* noexec filesystem, fall back to read() */
2057 if (flags
& MAP_SHARED
)
2059 if (prot
& PROT_EXEC
) ERR( "failed to set PROT_EXEC on file map, noexec filesystem?\n" );
2060 return STATUS_ACCESS_DENIED
;
2062 if (prot
& PROT_EXEC
) WARN( "failed to set PROT_EXEC on file map, noexec filesystem?\n" );
2065 return STATUS_NO_MEMORY
;
2069 /* Reserve the memory with an anonymous mmap */
2070 ptr
= anon_mmap_fixed( (char *)view
->base
+ start
, size
, PROT_READ
| PROT_WRITE
, 0 );
2071 if (ptr
== MAP_FAILED
) return STATUS_NO_MEMORY
;
2072 /* Now read in the file */
2073 pread( fd
, ptr
, size
, offset
);
2074 if (prot
!= (PROT_READ
|PROT_WRITE
)) mprotect( ptr
, size
, prot
); /* Set the right protection */
2076 set_page_vprot( (char *)view
->base
+ start
, size
, vprot
);
2077 return STATUS_SUCCESS
;
2081 /***********************************************************************
2082 * get_committed_size
2084 * Get the size of the committed range with equal masked vprot bytes starting at base.
2085 * Also return the protections for the first page.
2087 static SIZE_T
get_committed_size( struct file_view
*view
, void *base
, BYTE
*vprot
, BYTE vprot_mask
)
2089 SIZE_T offset
, size
;
2091 base
= ROUND_ADDR( base
, page_mask
);
2092 offset
= (char *)base
- (char *)view
->base
;
2094 if (view
->protect
& SEC_RESERVE
)
2098 *vprot
= get_page_vprot( base
);
2100 SERVER_START_REQ( get_mapping_committed_range
)
2102 req
->base
= wine_server_client_ptr( view
->base
);
2103 req
->offset
= offset
;
2104 if (!wine_server_call( req
))
2107 if (reply
->committed
)
2109 *vprot
|= VPROT_COMMITTED
;
2110 set_page_vprot_bits( base
, size
, VPROT_COMMITTED
, 0 );
2116 if (!size
|| !(vprot_mask
& ~VPROT_COMMITTED
)) return size
;
2118 else size
= view
->size
- offset
;
2120 return get_vprot_range_size( base
, size
, vprot_mask
, vprot
);
2124 /***********************************************************************
2127 * Decommit some pages of a given view.
2128 * virtual_mutex must be held by caller.
2130 static NTSTATUS
decommit_pages( struct file_view
*view
, size_t start
, size_t size
)
2132 if (!size
) size
= view
->size
;
2133 if (anon_mmap_fixed( (char *)view
->base
+ start
, size
, PROT_NONE
, 0 ) != MAP_FAILED
)
2135 set_page_vprot_bits( (char *)view
->base
+ start
, size
, 0, VPROT_COMMITTED
);
2136 return STATUS_SUCCESS
;
2138 return STATUS_NO_MEMORY
;
2142 /***********************************************************************
2143 * allocate_dos_memory
2145 * Allocate the DOS memory range.
2147 static NTSTATUS
allocate_dos_memory( struct file_view
**view
, unsigned int vprot
)
2151 void * const low_64k
= (void *)0x10000;
2152 const size_t dosmem_size
= 0x110000;
2153 int unix_prot
= get_unix_prot( vprot
);
2155 /* check for existing view */
2157 if (find_view_range( 0, dosmem_size
)) return STATUS_CONFLICTING_ADDRESSES
;
2159 /* check without the first 64K */
2161 if (mmap_is_in_reserved_area( low_64k
, dosmem_size
- 0x10000 ) != 1)
2163 addr
= anon_mmap_tryfixed( low_64k
, dosmem_size
- 0x10000, unix_prot
, 0 );
2164 if (addr
== MAP_FAILED
) return map_view( view
, NULL
, dosmem_size
, FALSE
, vprot
, 0, 0 );
2167 /* now try to allocate the low 64K too */
2169 if (mmap_is_in_reserved_area( NULL
, 0x10000 ) != 1)
2171 addr
= anon_mmap_tryfixed( (void *)page_size
, 0x10000 - page_size
, unix_prot
, 0 );
2172 if (addr
!= MAP_FAILED
)
2174 if (!anon_mmap_fixed( NULL
, page_size
, unix_prot
, 0 ))
2177 TRACE( "successfully mapped low 64K range\n" );
2179 else TRACE( "failed to map page 0\n" );
2184 TRACE( "failed to map low 64K range\n" );
2188 /* now reserve the whole range */
2190 size
= (char *)dosmem_size
- (char *)addr
;
2191 anon_mmap_fixed( addr
, size
, unix_prot
, 0 );
2192 return create_view( view
, addr
, size
, vprot
);
2196 /***********************************************************************
2199 * Map the header of a PE file into memory.
2201 static NTSTATUS
map_pe_header( void *ptr
, size_t size
, int fd
, BOOL
*removable
)
2203 if (!size
) return STATUS_INVALID_IMAGE_FORMAT
;
2207 if (mmap( ptr
, size
, PROT_READ
|PROT_WRITE
|PROT_EXEC
, MAP_FIXED
|MAP_PRIVATE
, fd
, 0 ) != MAP_FAILED
)
2208 return STATUS_SUCCESS
;
2214 WARN( "noexec file system, falling back to read\n" );
2218 WARN( "file system doesn't support mmap, falling back to read\n" );
2221 return STATUS_NO_MEMORY
;
2225 pread( fd
, ptr
, size
, 0 );
2226 return STATUS_SUCCESS
; /* page protections will be updated later */
2231 /***********************************************************************
2232 * apply_arm64x_relocations
2234 static void apply_arm64x_relocations( char *base
, const IMAGE_BASE_RELOCATION
*reloc
, size_t size
)
2236 const IMAGE_BASE_RELOCATION
*reloc_end
= (const IMAGE_BASE_RELOCATION
*)((const char *)reloc
+ size
);
2238 while (reloc
< reloc_end
- 1 && reloc
->SizeOfBlock
)
2240 const USHORT
*rel
= (const USHORT
*)(reloc
+ 1);
2241 const USHORT
*rel_end
= (const USHORT
*)reloc
+ reloc
->SizeOfBlock
/ sizeof(USHORT
);
2242 char *page
= base
+ reloc
->VirtualAddress
;
2244 while (rel
< rel_end
&& *rel
)
2246 USHORT offset
= *rel
& 0xfff;
2247 USHORT type
= (*rel
>> 12) & 3;
2248 USHORT arg
= *rel
>> 14;
2253 case IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL
:
2254 memset( page
+ offset
, 0, 1 << arg
);
2256 case IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE
:
2257 memcpy( page
+ offset
, rel
, 1 << arg
);
2258 rel
+= (1 << arg
) / sizeof(USHORT
);
2260 case IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA
:
2261 val
= (unsigned int)*rel
++ * ((arg
& 2) ? 8 : 4);
2262 if (arg
& 1) val
= -val
;
2263 *(int *)(page
+ offset
) += val
;
2267 reloc
= (const IMAGE_BASE_RELOCATION
*)rel_end
;
2272 /***********************************************************************
2273 * update_arm64x_mapping
2275 static void update_arm64x_mapping( char *base
, IMAGE_NT_HEADERS
*nt
, IMAGE_SECTION_HEADER
*sections
)
2277 ULONG i
, size
, sec
, offset
;
2278 const IMAGE_DATA_DIRECTORY
*dir
;
2279 const IMAGE_LOAD_CONFIG_DIRECTORY
*cfg
;
2280 const IMAGE_ARM64EC_METADATA
*metadata
;
2281 const IMAGE_DYNAMIC_RELOCATION_TABLE
*table
;
2282 const char *ptr
, *end
;
2284 /* retrieve config directory */
2286 if (nt
->OptionalHeader
.Magic
!= IMAGE_NT_OPTIONAL_HDR64_MAGIC
) return;
2287 dir
= nt
->OptionalHeader
.DataDirectory
+ IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG
;
2288 if (!dir
->VirtualAddress
|| !dir
->Size
) return;
2289 cfg
= (void *)(base
+ dir
->VirtualAddress
);
2290 size
= min( dir
->Size
, cfg
->Size
);
2292 /* update code ranges */
2294 if (size
<= offsetof( IMAGE_LOAD_CONFIG_DIRECTORY
, CHPEMetadataPointer
)) return;
2295 metadata
= (void *)(base
+ (cfg
->CHPEMetadataPointer
- nt
->OptionalHeader
.ImageBase
));
2296 if (metadata
->CodeMap
&& arm64ec_map
)
2298 const IMAGE_CHPE_RANGE_ENTRY
*map
= (void *)(base
+ metadata
->CodeMap
);
2300 for (i
= 0; i
< metadata
->CodeMapCount
; i
++)
2302 if ((map
[i
].StartOffset
& 0x3) != 1 /* arm64ec */) continue;
2303 set_arm64ec_range( base
+ (map
[i
].StartOffset
& ~3), map
[i
].Length
);
2307 /* apply dynamic relocations */
2309 if (size
<= offsetof( IMAGE_LOAD_CONFIG_DIRECTORY
, DynamicValueRelocTableSection
)) return;
2310 offset
= cfg
->DynamicValueRelocTableOffset
;
2311 sec
= cfg
->DynamicValueRelocTableSection
;
2312 if (!sec
|| sec
> nt
->FileHeader
.NumberOfSections
) return;
2313 if (offset
>= sections
[sec
- 1].Misc
.VirtualSize
) return;
2314 table
= (const IMAGE_DYNAMIC_RELOCATION_TABLE
*)(base
+ sections
[sec
- 1].VirtualAddress
+ offset
);
2315 ptr
= (const char *)(table
+ 1);
2316 end
= ptr
+ table
->Size
;
2317 switch (table
->Version
)
2322 const IMAGE_DYNAMIC_RELOCATION64
*dyn
= (const IMAGE_DYNAMIC_RELOCATION64
*)ptr
;
2323 if (dyn
->Symbol
== IMAGE_DYNAMIC_RELOCATION_ARM64X
)
2325 apply_arm64x_relocations( base
, (const IMAGE_BASE_RELOCATION
*)(dyn
+ 1),
2326 dyn
->BaseRelocSize
);
2329 ptr
+= sizeof(*dyn
) + dyn
->BaseRelocSize
;
2335 const IMAGE_DYNAMIC_RELOCATION64_V2
*dyn
= (const IMAGE_DYNAMIC_RELOCATION64_V2
*)ptr
;
2336 if (dyn
->Symbol
== IMAGE_DYNAMIC_RELOCATION_ARM64X
)
2338 apply_arm64x_relocations( base
, (const IMAGE_BASE_RELOCATION
*)(dyn
+ 1),
2339 dyn
->FixupInfoSize
);
2342 ptr
+= dyn
->HeaderSize
+ dyn
->FixupInfoSize
;
2346 FIXME( "unsupported version %u\n", table
->Version
);
2351 #endif /* __aarch64__ */
2353 /***********************************************************************
2354 * map_image_into_view
2356 * Map an executable (PE format) image into an existing view.
2357 * virtual_mutex must be held by caller.
2359 static NTSTATUS
map_image_into_view( struct file_view
*view
, const WCHAR
*filename
, int fd
, void *orig_base
,
2360 SIZE_T header_size
, ULONG image_flags
, int shared_fd
, BOOL removable
)
2362 IMAGE_DOS_HEADER
*dos
;
2363 IMAGE_NT_HEADERS
*nt
;
2364 IMAGE_SECTION_HEADER sections
[96];
2365 IMAGE_SECTION_HEADER
*sec
;
2366 IMAGE_DATA_DIRECTORY
*imports
;
2367 NTSTATUS status
= STATUS_CONFLICTING_ADDRESSES
;
2371 char *header_end
, *header_start
;
2372 char *ptr
= view
->base
;
2373 SIZE_T total_size
= view
->size
;
2375 TRACE_(module
)( "mapping PE file %s at %p-%p\n", debugstr_w(filename
), ptr
, ptr
+ total_size
);
2377 /* map the header */
2380 header_size
= min( header_size
, st
.st_size
);
2381 if ((status
= map_pe_header( view
->base
, header_size
, fd
, &removable
))) return status
;
2383 status
= STATUS_INVALID_IMAGE_FORMAT
; /* generic error */
2384 dos
= (IMAGE_DOS_HEADER
*)ptr
;
2385 nt
= (IMAGE_NT_HEADERS
*)(ptr
+ dos
->e_lfanew
);
2386 header_end
= ptr
+ ROUND_SIZE( 0, header_size
);
2387 memset( ptr
+ header_size
, 0, header_end
- (ptr
+ header_size
) );
2388 if ((char *)(nt
+ 1) > header_end
) return status
;
2389 header_start
= (char*)&nt
->OptionalHeader
+nt
->FileHeader
.SizeOfOptionalHeader
;
2390 if (nt
->FileHeader
.NumberOfSections
> ARRAY_SIZE( sections
)) return status
;
2391 if (header_start
+ sizeof(*sections
) * nt
->FileHeader
.NumberOfSections
> header_end
) return status
;
2392 /* Some applications (e.g. the Steam version of Borderlands) map over the top of the section headers,
2393 * copying the headers into local memory is necessary to properly load such applications. */
2394 memcpy(sections
, header_start
, sizeof(*sections
) * nt
->FileHeader
.NumberOfSections
);
2397 imports
= nt
->OptionalHeader
.DataDirectory
+ IMAGE_DIRECTORY_ENTRY_IMPORT
;
2398 if (!imports
->Size
|| !imports
->VirtualAddress
) imports
= NULL
;
2400 /* check for non page-aligned binary */
2402 if (image_flags
& IMAGE_FLAGS_ImageMappedFlat
)
2404 /* unaligned sections, this happens for native subsystem binaries */
2405 /* in that case Windows simply maps in the whole file */
2407 total_size
= min( total_size
, ROUND_SIZE( 0, st
.st_size
));
2408 if (map_file_into_view( view
, fd
, 0, total_size
, 0, VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITECOPY
,
2409 removable
) != STATUS_SUCCESS
) return status
;
2411 /* check that all sections are loaded at the right offset */
2412 if (nt
->OptionalHeader
.FileAlignment
!= nt
->OptionalHeader
.SectionAlignment
) return status
;
2413 for (i
= 0; i
< nt
->FileHeader
.NumberOfSections
; i
++)
2415 if (sec
[i
].VirtualAddress
!= sec
[i
].PointerToRawData
)
2416 return status
; /* Windows refuses to load in that case too */
2419 /* set the image protections */
2420 set_vprot( view
, ptr
, total_size
, VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITECOPY
| VPROT_EXEC
);
2422 /* no relocations are performed on non page-aligned binaries */
2423 return STATUS_SUCCESS
;
2427 /* map all the sections */
2429 for (i
= pos
= 0; i
< nt
->FileHeader
.NumberOfSections
; i
++, sec
++)
2431 static const SIZE_T sector_align
= 0x1ff;
2432 SIZE_T map_size
, file_start
, file_size
, end
;
2434 if (!sec
->Misc
.VirtualSize
)
2435 map_size
= ROUND_SIZE( 0, sec
->SizeOfRawData
);
2437 map_size
= ROUND_SIZE( 0, sec
->Misc
.VirtualSize
);
2439 /* file positions are rounded to sector boundaries regardless of OptionalHeader.FileAlignment */
2440 file_start
= sec
->PointerToRawData
& ~sector_align
;
2441 file_size
= (sec
->SizeOfRawData
+ (sec
->PointerToRawData
& sector_align
) + sector_align
) & ~sector_align
;
2442 if (file_size
> map_size
) file_size
= map_size
;
2444 /* a few sanity checks */
2445 end
= sec
->VirtualAddress
+ ROUND_SIZE( sec
->VirtualAddress
, map_size
);
2446 if (sec
->VirtualAddress
> total_size
|| end
> total_size
|| end
< sec
->VirtualAddress
)
2448 WARN_(module
)( "%s section %.8s too large (%x+%lx/%lx)\n",
2449 debugstr_w(filename
), sec
->Name
, (int)sec
->VirtualAddress
, map_size
, total_size
);
2453 if ((sec
->Characteristics
& IMAGE_SCN_MEM_SHARED
) &&
2454 (sec
->Characteristics
& IMAGE_SCN_MEM_WRITE
))
2456 TRACE_(module
)( "%s mapping shared section %.8s at %p off %x (%x) size %lx (%lx) flags %x\n",
2457 debugstr_w(filename
), sec
->Name
, ptr
+ sec
->VirtualAddress
,
2458 (int)sec
->PointerToRawData
, (int)pos
, file_size
, map_size
,
2459 (int)sec
->Characteristics
);
2460 if (map_file_into_view( view
, shared_fd
, sec
->VirtualAddress
, map_size
, pos
,
2461 VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITE
, FALSE
) != STATUS_SUCCESS
)
2463 ERR_(module
)( "Could not map %s shared section %.8s\n", debugstr_w(filename
), sec
->Name
);
2467 /* check if the import directory falls inside this section */
2468 if (imports
&& imports
->VirtualAddress
>= sec
->VirtualAddress
&&
2469 imports
->VirtualAddress
< sec
->VirtualAddress
+ map_size
)
2471 UINT_PTR base
= imports
->VirtualAddress
& ~page_mask
;
2472 UINT_PTR end
= base
+ ROUND_SIZE( imports
->VirtualAddress
, imports
->Size
);
2473 if (end
> sec
->VirtualAddress
+ map_size
) end
= sec
->VirtualAddress
+ map_size
;
2475 map_file_into_view( view
, shared_fd
, base
, end
- base
,
2476 pos
+ (base
- sec
->VirtualAddress
),
2477 VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITECOPY
, FALSE
);
2483 TRACE_(module
)( "mapping %s section %.8s at %p off %x size %x virt %x flags %x\n",
2484 debugstr_w(filename
), sec
->Name
, ptr
+ sec
->VirtualAddress
,
2485 (int)sec
->PointerToRawData
, (int)sec
->SizeOfRawData
,
2486 (int)sec
->Misc
.VirtualSize
, (int)sec
->Characteristics
);
2488 if (!sec
->PointerToRawData
|| !file_size
) continue;
2490 /* Note: if the section is not aligned properly map_file_into_view will magically
2491 * fall back to read(), so we don't need to check anything here.
2493 end
= file_start
+ file_size
;
2494 if (sec
->PointerToRawData
>= st
.st_size
||
2495 end
> ((st
.st_size
+ sector_align
) & ~sector_align
) ||
2497 map_file_into_view( view
, fd
, sec
->VirtualAddress
, file_size
, file_start
,
2498 VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITECOPY
,
2499 removable
) != STATUS_SUCCESS
)
2501 ERR_(module
)( "Could not map %s section %.8s, file probably truncated\n",
2502 debugstr_w(filename
), sec
->Name
);
2506 if (file_size
& page_mask
)
2508 end
= ROUND_SIZE( 0, file_size
);
2509 if (end
> map_size
) end
= map_size
;
2510 TRACE_(module
)("clearing %p - %p\n",
2511 ptr
+ sec
->VirtualAddress
+ file_size
,
2512 ptr
+ sec
->VirtualAddress
+ end
);
2513 memset( ptr
+ sec
->VirtualAddress
+ file_size
, 0, end
- file_size
);
2518 if (main_image_info
.Machine
== IMAGE_FILE_MACHINE_AMD64
&&
2519 nt
->FileHeader
.Machine
== IMAGE_FILE_MACHINE_ARM64
)
2520 update_arm64x_mapping( ptr
, nt
, sections
);
2523 /* set the image protections */
2525 set_vprot( view
, ptr
, ROUND_SIZE( 0, header_size
), VPROT_COMMITTED
| VPROT_READ
);
2528 for (i
= 0; i
< nt
->FileHeader
.NumberOfSections
; i
++, sec
++)
2531 BYTE vprot
= VPROT_COMMITTED
;
2533 if (sec
->Misc
.VirtualSize
)
2534 size
= ROUND_SIZE( sec
->VirtualAddress
, sec
->Misc
.VirtualSize
);
2536 size
= ROUND_SIZE( sec
->VirtualAddress
, sec
->SizeOfRawData
);
2538 if (sec
->Characteristics
& IMAGE_SCN_MEM_READ
) vprot
|= VPROT_READ
;
2539 if (sec
->Characteristics
& IMAGE_SCN_MEM_WRITE
) vprot
|= VPROT_WRITECOPY
;
2540 if (sec
->Characteristics
& IMAGE_SCN_MEM_EXECUTE
) vprot
|= VPROT_EXEC
;
2542 if (!set_vprot( view
, ptr
+ sec
->VirtualAddress
, size
, vprot
) && (vprot
& VPROT_EXEC
))
2543 ERR( "failed to set %08x protection on %s section %.8s, noexec filesystem?\n",
2544 (int)sec
->Characteristics
, debugstr_w(filename
), sec
->Name
);
2547 #ifdef VALGRIND_LOAD_PDB_DEBUGINFO
2548 VALGRIND_LOAD_PDB_DEBUGINFO(fd
, ptr
, total_size
, ptr
- (char *)orig_base
);
2550 return STATUS_SUCCESS
;
2554 /***********************************************************************
2557 static unsigned int get_mapping_info( HANDLE handle
, ACCESS_MASK access
, unsigned int *sec_flags
,
2558 mem_size_t
*full_size
, HANDLE
*shared_file
, pe_image_info_t
**info
)
2560 pe_image_info_t
*image_info
;
2561 SIZE_T total
, size
= 1024;
2562 unsigned int status
;
2566 if (!(image_info
= malloc( size
))) return STATUS_NO_MEMORY
;
2568 SERVER_START_REQ( get_mapping_info
)
2570 req
->handle
= wine_server_obj_handle( handle
);
2571 req
->access
= access
;
2572 wine_server_set_reply( req
, image_info
, size
);
2573 status
= wine_server_call( req
);
2574 *sec_flags
= reply
->flags
;
2575 *full_size
= reply
->size
;
2576 total
= reply
->total
;
2577 *shared_file
= wine_server_ptr_handle( reply
->shared_file
);
2580 if (!status
&& total
<= size
- sizeof(WCHAR
)) break;
2582 if (status
) return status
;
2583 if (*shared_file
) NtClose( *shared_file
);
2584 size
= total
+ sizeof(WCHAR
);
2589 WCHAR
*filename
= (WCHAR
*)(image_info
+ 1);
2591 assert( total
>= sizeof(*image_info
) );
2592 total
-= sizeof(*image_info
);
2593 filename
[total
/ sizeof(WCHAR
)] = 0;
2596 else free( image_info
);
2598 return STATUS_SUCCESS
;
2602 /***********************************************************************
2605 * Map a PE image section into memory.
2607 static NTSTATUS
virtual_map_image( HANDLE mapping
, ACCESS_MASK access
, void **addr_ptr
, SIZE_T
*size_ptr
,
2608 ULONG_PTR zero_bits
, HANDLE shared_file
, ULONG alloc_type
,
2609 pe_image_info_t
*image_info
, WCHAR
*filename
, BOOL is_builtin
)
2611 unsigned int vprot
= SEC_IMAGE
| SEC_FILE
| VPROT_COMMITTED
| VPROT_READ
| VPROT_EXEC
| VPROT_WRITECOPY
;
2612 int unix_fd
= -1, needs_close
;
2613 int shared_fd
= -1, shared_needs_close
= 0;
2614 SIZE_T size
= image_info
->map_size
;
2615 struct file_view
*view
;
2616 unsigned int status
;
2620 if ((status
= server_get_unix_fd( mapping
, 0, &unix_fd
, &needs_close
, NULL
, NULL
)))
2623 if (shared_file
&& ((status
= server_get_unix_fd( shared_file
, FILE_READ_DATA
|FILE_WRITE_DATA
,
2624 &shared_fd
, &shared_needs_close
, NULL
, NULL
))))
2626 if (needs_close
) close( unix_fd
);
2630 status
= STATUS_INVALID_PARAMETER
;
2631 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
2633 base
= wine_server_get_ptr( image_info
->base
);
2634 if ((ULONG_PTR
)base
!= image_info
->base
) base
= NULL
;
2636 if ((char *)base
>= (char *)address_space_start
) /* make sure the DOS area remains free */
2637 status
= map_view( &view
, base
, size
, alloc_type
& MEM_TOP_DOWN
, vprot
, get_zero_bits_mask( zero_bits
), 0 );
2639 if (status
) status
= map_view( &view
, NULL
, size
, alloc_type
& MEM_TOP_DOWN
, vprot
, get_zero_bits_mask( zero_bits
), 0 );
2640 if (status
) goto done
;
2642 status
= map_image_into_view( view
, filename
, unix_fd
, base
, image_info
->header_size
,
2643 image_info
->image_flags
, shared_fd
, needs_close
);
2644 if (status
== STATUS_SUCCESS
)
2646 SERVER_START_REQ( map_view
)
2648 req
->mapping
= wine_server_obj_handle( mapping
);
2649 req
->access
= access
;
2650 req
->base
= wine_server_client_ptr( view
->base
);
2652 status
= wine_server_call( req
);
2656 if (NT_SUCCESS(status
))
2658 if (is_builtin
) add_builtin_module( view
->base
, NULL
);
2659 *addr_ptr
= view
->base
;
2661 VIRTUAL_DEBUG_DUMP_VIEW( view
);
2663 else delete_view( view
);
2666 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
2667 if (needs_close
) close( unix_fd
);
2668 if (shared_needs_close
) close( shared_fd
);
2673 /***********************************************************************
2674 * virtual_map_section
2676 * Map a file section into memory.
2678 static unsigned int virtual_map_section( HANDLE handle
, PVOID
*addr_ptr
, ULONG_PTR zero_bits
,
2679 SIZE_T commit_size
, const LARGE_INTEGER
*offset_ptr
, SIZE_T
*size_ptr
,
2680 ULONG alloc_type
, ULONG protect
)
2683 mem_size_t full_size
;
2686 pe_image_info_t
*image_info
= NULL
;
2689 int unix_handle
= -1, needs_close
;
2690 unsigned int vprot
, sec_flags
;
2691 struct file_view
*view
;
2693 LARGE_INTEGER offset
;
2700 case PAGE_WRITECOPY
:
2701 access
= SECTION_MAP_READ
;
2703 case PAGE_READWRITE
:
2704 access
= SECTION_MAP_WRITE
;
2707 case PAGE_EXECUTE_READ
:
2708 case PAGE_EXECUTE_WRITECOPY
:
2709 access
= SECTION_MAP_READ
| SECTION_MAP_EXECUTE
;
2711 case PAGE_EXECUTE_READWRITE
:
2712 access
= SECTION_MAP_WRITE
| SECTION_MAP_EXECUTE
;
2715 return STATUS_INVALID_PAGE_PROTECTION
;
2718 res
= get_mapping_info( handle
, access
, &sec_flags
, &full_size
, &shared_file
, &image_info
);
2719 if (res
) return res
;
2723 filename
= (WCHAR
*)(image_info
+ 1);
2724 /* check if we can replace that mapping with the builtin */
2725 res
= load_builtin( image_info
, filename
, addr_ptr
, size_ptr
, zero_bits
);
2726 if (res
== STATUS_IMAGE_ALREADY_LOADED
)
2727 res
= virtual_map_image( handle
, access
, addr_ptr
, size_ptr
, zero_bits
, shared_file
,
2728 alloc_type
, image_info
, filename
, FALSE
);
2729 if (shared_file
) NtClose( shared_file
);
2735 offset
.QuadPart
= offset_ptr
? offset_ptr
->QuadPart
: 0;
2736 if (offset
.QuadPart
>= full_size
) return STATUS_INVALID_PARAMETER
;
2740 if (size
> full_size
- offset
.QuadPart
) return STATUS_INVALID_VIEW_SIZE
;
2744 size
= full_size
- offset
.QuadPart
;
2745 if (size
!= full_size
- offset
.QuadPart
) /* truncated */
2747 WARN( "Files larger than 4Gb (%s) not supported on this platform\n",
2748 wine_dbgstr_longlong(full_size
) );
2749 return STATUS_INVALID_PARAMETER
;
2752 if (!(size
= ROUND_SIZE( 0, size
))) return STATUS_INVALID_PARAMETER
; /* wrap-around */
2754 get_vprot_flags( protect
, &vprot
, FALSE
);
2756 if (!(sec_flags
& SEC_RESERVE
)) vprot
|= VPROT_COMMITTED
;
2758 if ((res
= server_get_unix_fd( handle
, 0, &unix_handle
, &needs_close
, NULL
, NULL
))) return res
;
2760 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
2762 res
= map_view( &view
, base
, size
, alloc_type
& MEM_TOP_DOWN
, vprot
, get_zero_bits_mask( zero_bits
), 0 );
2765 TRACE( "handle=%p size=%lx offset=%s\n", handle
, size
, wine_dbgstr_longlong(offset
.QuadPart
) );
2766 res
= map_file_into_view( view
, unix_handle
, 0, size
, offset
.QuadPart
, vprot
, needs_close
);
2767 if (res
== STATUS_SUCCESS
)
2769 SERVER_START_REQ( map_view
)
2771 req
->mapping
= wine_server_obj_handle( handle
);
2772 req
->access
= access
;
2773 req
->base
= wine_server_client_ptr( view
->base
);
2775 req
->start
= offset
.QuadPart
;
2776 res
= wine_server_call( req
);
2780 else ERR( "mapping %p %lx %s failed\n", view
->base
, size
, wine_dbgstr_longlong(offset
.QuadPart
) );
2782 if (NT_SUCCESS(res
))
2784 *addr_ptr
= view
->base
;
2786 VIRTUAL_DEBUG_DUMP_VIEW( view
);
2788 else delete_view( view
);
2791 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
2792 if (needs_close
) close( unix_handle
);
2797 struct alloc_virtual_heap
2803 /* callback for mmap_enum_reserved_areas to allocate space for the virtual heap */
2804 static int alloc_virtual_heap( void *base
, SIZE_T size
, void *arg
)
2806 struct alloc_virtual_heap
*alloc
= arg
;
2807 void *end
= (char *)base
+ size
;
2809 if (is_beyond_limit( base
, size
, address_space_limit
)) address_space_limit
= (char *)base
+ size
;
2810 if (is_win64
&& base
< (void *)0x80000000) return 0;
2811 if (preload_reserve_end
>= end
)
2813 if (preload_reserve_start
<= base
) return 0; /* no space in that area */
2814 if (preload_reserve_start
< end
) end
= preload_reserve_start
;
2816 else if (preload_reserve_end
> base
)
2818 if (preload_reserve_start
<= base
) base
= preload_reserve_end
;
2819 else if ((char *)end
- (char *)preload_reserve_end
>= alloc
->size
) base
= preload_reserve_end
;
2820 else end
= preload_reserve_start
;
2822 if ((char *)end
- (char *)base
< alloc
->size
) return 0;
2823 alloc
->base
= anon_mmap_fixed( (char *)end
- alloc
->size
, alloc
->size
, PROT_READ
|PROT_WRITE
, 0 );
2824 return (alloc
->base
!= MAP_FAILED
);
2827 /***********************************************************************
2830 void virtual_init(void)
2832 const struct preload_info
**preload_info
= dlsym( RTLD_DEFAULT
, "wine_main_preload_info" );
2833 const char *preload
= getenv( "WINEPRELOADRESERVE" );
2834 struct alloc_virtual_heap alloc_views
;
2837 pthread_mutexattr_t attr
;
2839 pthread_mutexattr_init( &attr
);
2840 pthread_mutexattr_settype( &attr
, PTHREAD_MUTEX_RECURSIVE
);
2841 pthread_mutex_init( &virtual_mutex
, &attr
);
2842 pthread_mutexattr_destroy( &attr
);
2844 if (preload_info
&& *preload_info
)
2845 for (i
= 0; (*preload_info
)[i
].size
; i
++)
2846 mmap_add_reserved_area( (*preload_info
)[i
].addr
, (*preload_info
)[i
].size
);
2848 mmap_init( preload_info
? *preload_info
: NULL
);
2850 if ((preload
= getenv("WINEPRELOADRESERVE")))
2852 unsigned long start
, end
;
2853 if (sscanf( preload
, "%lx-%lx", &start
, &end
) == 2)
2855 preload_reserve_start
= (void *)start
;
2856 preload_reserve_end
= (void *)end
;
2857 /* some apps start inside the DOS area */
2858 if (preload_reserve_start
)
2859 address_space_start
= min( address_space_start
, preload_reserve_start
);
2863 /* try to find space in a reserved area for the views and pages protection table */
2865 pages_vprot_size
= ((size_t)address_space_limit
>> page_shift
>> pages_vprot_shift
) + 1;
2866 alloc_views
.size
= 2 * view_block_size
+ pages_vprot_size
* sizeof(*pages_vprot
);
2868 alloc_views
.size
= 2 * view_block_size
+ (1U << (32 - page_shift
));
2870 if (mmap_enum_reserved_areas( alloc_virtual_heap
, &alloc_views
, 1 ))
2871 mmap_remove_reserved_area( alloc_views
.base
, alloc_views
.size
);
2873 alloc_views
.base
= anon_mmap_alloc( alloc_views
.size
, PROT_READ
| PROT_WRITE
);
2875 assert( alloc_views
.base
!= MAP_FAILED
);
2876 view_block_start
= alloc_views
.base
;
2877 view_block_end
= view_block_start
+ view_block_size
/ sizeof(*view_block_start
);
2878 free_ranges
= (void *)((char *)alloc_views
.base
+ view_block_size
);
2879 pages_vprot
= (void *)((char *)alloc_views
.base
+ 2 * view_block_size
);
2880 wine_rb_init( &views_tree
, compare_view
);
2882 free_ranges
[0].base
= (void *)0;
2883 free_ranges
[0].end
= (void *)~0;
2884 free_ranges_end
= free_ranges
+ 1;
2886 /* make the DOS area accessible (except the low 64K) to hide bugs in broken apps like Excel 2003 */
2887 size
= (char *)address_space_start
- (char *)0x10000;
2888 if (size
&& mmap_is_in_reserved_area( (void*)0x10000, size
) == 1)
2889 anon_mmap_fixed( (void *)0x10000, size
, PROT_READ
| PROT_WRITE
, 0 );
2893 /***********************************************************************
2894 * get_system_affinity_mask
2896 ULONG_PTR
get_system_affinity_mask(void)
2898 ULONG num_cpus
= peb
->NumberOfProcessors
;
2899 if (num_cpus
>= sizeof(ULONG_PTR
) * 8) return ~(ULONG_PTR
)0;
2900 return ((ULONG_PTR
)1 << num_cpus
) - 1;
2903 /***********************************************************************
2904 * virtual_get_system_info
2906 void virtual_get_system_info( SYSTEM_BASIC_INFORMATION
*info
, BOOL wow64
)
2908 #if defined(HAVE_SYSINFO) \
2909 && defined(HAVE_STRUCT_SYSINFO_TOTALRAM) && defined(HAVE_STRUCT_SYSINFO_MEM_UNIT)
2910 struct sysinfo sinfo
;
2912 if (!sysinfo(&sinfo
))
2914 ULONG64 total
= (ULONG64
)sinfo
.totalram
* sinfo
.mem_unit
;
2915 info
->MmHighestPhysicalPage
= max(1, total
/ page_size
);
2917 #elif defined(_SC_PHYS_PAGES)
2918 LONG64 phys_pages
= sysconf( _SC_PHYS_PAGES
);
2920 info
->MmHighestPhysicalPage
= max(1, phys_pages
);
2922 info
->MmHighestPhysicalPage
= 0x7fffffff / page_size
;
2926 info
->KeMaximumIncrement
= 0; /* FIXME */
2927 info
->PageSize
= page_size
;
2928 info
->MmLowestPhysicalPage
= 1;
2929 info
->MmNumberOfPhysicalPages
= info
->MmHighestPhysicalPage
- info
->MmLowestPhysicalPage
;
2930 info
->AllocationGranularity
= granularity_mask
+ 1;
2931 info
->LowestUserAddress
= (void *)0x10000;
2932 info
->ActiveProcessorsAffinityMask
= get_system_affinity_mask();
2933 info
->NumberOfProcessors
= peb
->NumberOfProcessors
;
2934 if (wow64
) info
->HighestUserAddress
= (char *)get_wow_user_space_limit() - 1;
2935 else info
->HighestUserAddress
= (char *)user_space_limit
- 1;
2939 /***********************************************************************
2940 * virtual_map_builtin_module
2942 NTSTATUS
virtual_map_builtin_module( HANDLE mapping
, void **module
, SIZE_T
*size
, SECTION_IMAGE_INFORMATION
*info
,
2943 ULONG_PTR zero_bits
, WORD machine
, BOOL prefer_native
)
2945 mem_size_t full_size
;
2946 unsigned int sec_flags
;
2948 pe_image_info_t
*image_info
= NULL
;
2949 ACCESS_MASK access
= SECTION_MAP_READ
| SECTION_MAP_EXECUTE
;
2953 if ((status
= get_mapping_info( mapping
, access
, &sec_flags
, &full_size
, &shared_file
, &image_info
)))
2956 if (!image_info
) return STATUS_INVALID_PARAMETER
;
2960 filename
= (WCHAR
*)(image_info
+ 1);
2962 if (!(image_info
->image_flags
& IMAGE_FLAGS_WineBuiltin
)) /* ignore non-builtins */
2964 WARN( "%s found in WINEDLLPATH but not a builtin, ignoring\n", debugstr_w(filename
) );
2965 status
= STATUS_DLL_NOT_FOUND
;
2967 else if (machine
&& image_info
->machine
!= machine
)
2969 TRACE( "%s is for arch %04x, continuing search\n", debugstr_w(filename
), image_info
->machine
);
2970 status
= STATUS_IMAGE_MACHINE_TYPE_MISMATCH
;
2972 else if (prefer_native
&& (image_info
->dll_charact
& IMAGE_DLLCHARACTERISTICS_PREFER_NATIVE
))
2974 TRACE( "%s has prefer-native flag, ignoring builtin\n", debugstr_w(filename
) );
2975 status
= STATUS_IMAGE_ALREADY_LOADED
;
2979 status
= virtual_map_image( mapping
, SECTION_MAP_READ
| SECTION_MAP_EXECUTE
,
2980 module
, size
, zero_bits
, shared_file
, 0, image_info
, filename
, TRUE
);
2981 virtual_fill_image_information( image_info
, info
);
2984 if (shared_file
) NtClose( shared_file
);
2990 /***********************************************************************
2991 * virtual_create_builtin_view
2993 NTSTATUS
virtual_create_builtin_view( void *module
, const UNICODE_STRING
*nt_name
,
2994 pe_image_info_t
*info
, void *so_handle
)
2998 IMAGE_DOS_HEADER
*dos
= module
;
2999 IMAGE_NT_HEADERS
*nt
= (IMAGE_NT_HEADERS
*)((char *)dos
+ dos
->e_lfanew
);
3000 SIZE_T size
= info
->map_size
;
3001 IMAGE_SECTION_HEADER
*sec
;
3002 struct file_view
*view
;
3003 void *base
= wine_server_get_ptr( info
->base
);
3006 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3007 status
= create_view( &view
, base
, size
, SEC_IMAGE
| SEC_FILE
| VPROT_SYSTEM
|
3008 VPROT_COMMITTED
| VPROT_READ
| VPROT_WRITECOPY
| VPROT_EXEC
);
3011 TRACE( "created %p-%p for %s\n", base
, (char *)base
+ size
, debugstr_us(nt_name
) );
3013 /* The PE header is always read-only, no write, no execute. */
3014 set_page_vprot( base
, page_size
, VPROT_COMMITTED
| VPROT_READ
);
3016 sec
= (IMAGE_SECTION_HEADER
*)((char *)&nt
->OptionalHeader
+ nt
->FileHeader
.SizeOfOptionalHeader
);
3017 for (i
= 0; i
< nt
->FileHeader
.NumberOfSections
; i
++)
3019 BYTE flags
= VPROT_COMMITTED
;
3021 if (sec
[i
].Characteristics
& IMAGE_SCN_MEM_EXECUTE
) flags
|= VPROT_EXEC
;
3022 if (sec
[i
].Characteristics
& IMAGE_SCN_MEM_READ
) flags
|= VPROT_READ
;
3023 if (sec
[i
].Characteristics
& IMAGE_SCN_MEM_WRITE
) flags
|= VPROT_WRITE
;
3024 set_page_vprot( (char *)base
+ sec
[i
].VirtualAddress
, sec
[i
].Misc
.VirtualSize
, flags
);
3027 SERVER_START_REQ( map_view
)
3029 req
->base
= wine_server_client_ptr( view
->base
);
3031 wine_server_add_data( req
, info
, sizeof(*info
) );
3032 wine_server_add_data( req
, nt_name
->Buffer
, nt_name
->Length
);
3033 status
= wine_server_call( req
);
3039 add_builtin_module( view
->base
, so_handle
);
3040 VIRTUAL_DEBUG_DUMP_VIEW( view
);
3041 if (is_beyond_limit( base
, size
, working_set_limit
)) working_set_limit
= address_space_limit
;
3043 else delete_view( view
);
3045 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3051 /* set some initial values in a new TEB */
3052 static TEB
*init_teb( void *ptr
, BOOL is_wow
)
3054 struct ntdll_thread_data
*thread_data
;
3057 TEB32
*teb32
= (TEB32
*)((char *)ptr
+ teb_offset
);
3061 teb32
->Peb
= PtrToUlong( (char *)peb
+ page_size
);
3062 teb32
->Tib
.Self
= PtrToUlong( teb32
);
3063 teb32
->Tib
.ExceptionList
= ~0u;
3064 teb32
->ActivationContextStackPointer
= PtrToUlong( &teb32
->ActivationContextStack
);
3065 teb32
->ActivationContextStack
.FrameListCache
.Flink
=
3066 teb32
->ActivationContextStack
.FrameListCache
.Blink
=
3067 PtrToUlong( &teb32
->ActivationContextStack
.FrameListCache
);
3068 teb32
->StaticUnicodeString
.Buffer
= PtrToUlong( teb32
->StaticUnicodeBuffer
);
3069 teb32
->StaticUnicodeString
.MaximumLength
= sizeof( teb32
->StaticUnicodeBuffer
);
3070 teb32
->GdiBatchCount
= PtrToUlong( teb64
);
3071 teb32
->WowTebOffset
= -teb_offset
;
3072 if (is_wow
) teb64
->WowTebOffset
= teb_offset
;
3075 teb64
->Peb
= PtrToUlong( (char *)peb
- page_size
);
3076 teb64
->Tib
.Self
= PtrToUlong( teb64
);
3077 teb64
->Tib
.ExceptionList
= PtrToUlong( teb32
);
3078 teb64
->ActivationContextStackPointer
= PtrToUlong( &teb64
->ActivationContextStack
);
3079 teb64
->ActivationContextStack
.FrameListCache
.Flink
=
3080 teb64
->ActivationContextStack
.FrameListCache
.Blink
=
3081 PtrToUlong( &teb64
->ActivationContextStack
.FrameListCache
);
3082 teb64
->StaticUnicodeString
.Buffer
= PtrToUlong( teb64
->StaticUnicodeBuffer
);
3083 teb64
->StaticUnicodeString
.MaximumLength
= sizeof( teb64
->StaticUnicodeBuffer
);
3084 teb64
->WowTebOffset
= teb_offset
;
3087 teb32
->GdiBatchCount
= PtrToUlong( teb64
);
3088 teb32
->WowTebOffset
= -teb_offset
;
3092 teb
->Tib
.Self
= &teb
->Tib
;
3093 teb
->Tib
.ExceptionList
= (void *)~0ul;
3094 teb
->Tib
.StackBase
= (void *)~0ul;
3095 teb
->ActivationContextStackPointer
= &teb
->ActivationContextStack
;
3096 InitializeListHead( &teb
->ActivationContextStack
.FrameListCache
);
3097 teb
->StaticUnicodeString
.Buffer
= teb
->StaticUnicodeBuffer
;
3098 teb
->StaticUnicodeString
.MaximumLength
= sizeof(teb
->StaticUnicodeBuffer
);
3099 thread_data
= (struct ntdll_thread_data
*)&teb
->GdiTebBatch
;
3100 thread_data
->request_fd
= -1;
3101 thread_data
->reply_fd
= -1;
3102 thread_data
->wait_fd
[0] = -1;
3103 thread_data
->wait_fd
[1] = -1;
3104 list_add_head( &teb_list
, &thread_data
->entry
);
3109 /***********************************************************************
3110 * virtual_alloc_first_teb
3112 TEB
*virtual_alloc_first_teb(void)
3116 unsigned int status
;
3117 SIZE_T data_size
= page_size
;
3118 SIZE_T block_size
= signal_stack_mask
+ 1;
3119 SIZE_T total
= 32 * block_size
;
3121 /* reserve space for shared user data */
3122 status
= NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&user_shared_data
, 0, &data_size
,
3123 MEM_RESERVE
| MEM_COMMIT
, PAGE_READONLY
);
3126 ERR( "wine: failed to map the shared user data: %08x\n", status
);
3130 NtAllocateVirtualMemory( NtCurrentProcess(), &teb_block
, is_win64
? 0x7fffffff : 0, &total
,
3131 MEM_RESERVE
| MEM_TOP_DOWN
, PAGE_READWRITE
);
3133 ptr
= (char *)teb_block
+ 30 * block_size
;
3134 data_size
= 2 * block_size
;
3135 NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&ptr
, 0, &data_size
, MEM_COMMIT
, PAGE_READWRITE
);
3136 peb
= (PEB
*)((char *)teb_block
+ 31 * block_size
+ (is_win64
? 0 : page_size
));
3137 teb
= init_teb( ptr
, FALSE
);
3138 pthread_key_create( &teb_key
, NULL
);
3139 pthread_setspecific( teb_key
, teb
);
3144 /***********************************************************************
3147 NTSTATUS
virtual_alloc_teb( TEB
**ret_teb
)
3152 NTSTATUS status
= STATUS_SUCCESS
;
3153 SIZE_T block_size
= signal_stack_mask
+ 1;
3155 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3158 ptr
= next_free_teb
;
3159 next_free_teb
= *(void **)ptr
;
3160 memset( ptr
, 0, teb_size
);
3166 SIZE_T total
= 32 * block_size
;
3168 if ((status
= NtAllocateVirtualMemory( NtCurrentProcess(), &ptr
, is_win64
&& is_wow64() ? 0x7fffffff : 0,
3169 &total
, MEM_RESERVE
, PAGE_READWRITE
)))
3171 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3177 ptr
= ((char *)teb_block
+ --teb_block_pos
* block_size
);
3178 NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&ptr
, 0, &block_size
,
3179 MEM_COMMIT
, PAGE_READWRITE
);
3181 *ret_teb
= teb
= init_teb( ptr
, is_wow64() );
3182 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3184 if ((status
= signal_alloc_thread( teb
)))
3186 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3187 *(void **)ptr
= next_free_teb
;
3188 next_free_teb
= ptr
;
3189 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3195 /***********************************************************************
3198 void virtual_free_teb( TEB
*teb
)
3200 struct ntdll_thread_data
*thread_data
= (struct ntdll_thread_data
*)&teb
->GdiTebBatch
;
3204 WOW_TEB
*wow_teb
= get_wow_teb( teb
);
3206 signal_free_thread( teb
);
3207 if (teb
->DeallocationStack
)
3210 NtFreeVirtualMemory( GetCurrentProcess(), &teb
->DeallocationStack
, &size
, MEM_RELEASE
);
3212 if (thread_data
->kernel_stack
)
3215 NtFreeVirtualMemory( GetCurrentProcess(), &thread_data
->kernel_stack
, &size
, MEM_RELEASE
);
3217 if (wow_teb
&& (ptr
= ULongToPtr( wow_teb
->DeallocationStack
)))
3220 NtFreeVirtualMemory( GetCurrentProcess(), &ptr
, &size
, MEM_RELEASE
);
3223 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3224 list_remove( &thread_data
->entry
);
3226 if (!is_win64
) ptr
= (char *)ptr
- teb_offset
;
3227 *(void **)ptr
= next_free_teb
;
3228 next_free_teb
= ptr
;
3229 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3233 /***********************************************************************
3234 * virtual_clear_tls_index
3236 NTSTATUS
virtual_clear_tls_index( ULONG index
)
3238 struct ntdll_thread_data
*thread_data
;
3241 if (index
< TLS_MINIMUM_AVAILABLE
)
3243 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3244 LIST_FOR_EACH_ENTRY( thread_data
, &teb_list
, struct ntdll_thread_data
, entry
)
3246 TEB
*teb
= CONTAINING_RECORD( thread_data
, TEB
, GdiTebBatch
);
3248 WOW_TEB
*wow_teb
= get_wow_teb( teb
);
3249 if (wow_teb
) wow_teb
->TlsSlots
[index
] = 0;
3252 teb
->TlsSlots
[index
] = 0;
3254 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3258 index
-= TLS_MINIMUM_AVAILABLE
;
3259 if (index
>= 8 * sizeof(peb
->TlsExpansionBitmapBits
)) return STATUS_INVALID_PARAMETER
;
3261 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3262 LIST_FOR_EACH_ENTRY( thread_data
, &teb_list
, struct ntdll_thread_data
, entry
)
3264 TEB
*teb
= CONTAINING_RECORD( thread_data
, TEB
, GdiTebBatch
);
3266 WOW_TEB
*wow_teb
= get_wow_teb( teb
);
3269 if (wow_teb
->TlsExpansionSlots
)
3270 ((ULONG
*)ULongToPtr( wow_teb
->TlsExpansionSlots
))[index
] = 0;
3274 if (teb
->TlsExpansionSlots
) teb
->TlsExpansionSlots
[index
] = 0;
3276 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3278 return STATUS_SUCCESS
;
3282 /***********************************************************************
3283 * virtual_alloc_thread_stack
3285 NTSTATUS
virtual_alloc_thread_stack( INITIAL_TEB
*stack
, ULONG_PTR zero_bits
, SIZE_T reserve_size
,
3286 SIZE_T commit_size
, BOOL guard_page
)
3288 struct file_view
*view
;
3293 if (!reserve_size
) reserve_size
= main_image_info
.MaximumStackSize
;
3294 if (!commit_size
) commit_size
= main_image_info
.CommittedStackSize
;
3296 size
= max( reserve_size
, commit_size
);
3297 if (size
< 1024 * 1024) size
= 1024 * 1024; /* Xlib needs a large stack */
3298 size
= (size
+ 0xffff) & ~0xffff; /* round to 64K boundary */
3300 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3302 if ((status
= map_view( &view
, NULL
, size
, FALSE
, VPROT_READ
| VPROT_WRITE
| VPROT_COMMITTED
,
3303 get_zero_bits_mask( zero_bits
), 0 )) != STATUS_SUCCESS
)
3306 #ifdef VALGRIND_STACK_REGISTER
3307 VALGRIND_STACK_REGISTER( view
->base
, (char *)view
->base
+ view
->size
);
3310 /* setup no access guard page */
3313 set_page_vprot( view
->base
, page_size
, VPROT_COMMITTED
);
3314 set_page_vprot( (char *)view
->base
+ page_size
, page_size
,
3315 VPROT_READ
| VPROT_WRITE
| VPROT_COMMITTED
| VPROT_GUARD
);
3316 mprotect_range( view
->base
, 2 * page_size
, 0, 0 );
3318 VIRTUAL_DEBUG_DUMP_VIEW( view
);
3320 /* note: limit is lower than base since the stack grows down */
3321 stack
->OldStackBase
= 0;
3322 stack
->OldStackLimit
= 0;
3323 stack
->DeallocationStack
= view
->base
;
3324 stack
->StackBase
= (char *)view
->base
+ view
->size
;
3325 stack
->StackLimit
= (char *)view
->base
+ (guard_page
? 2 * page_size
: 0);
3327 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3332 /***********************************************************************
3333 * virtual_alloc_arm64ec_map
3335 void *virtual_alloc_arm64ec_map(void)
3338 SIZE_T size
= ((ULONG_PTR
)user_space_limit
+ page_size
) >> (page_shift
+ 3); /* one bit per page */
3339 unsigned int status
= NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&arm64ec_map
, 0, &size
,
3340 MEM_COMMIT
, PAGE_READWRITE
);
3343 ERR( "failed to allocate ARM64EC map: %08x\n", status
);
3351 /***********************************************************************
3352 * virtual_map_user_shared_data
3354 void virtual_map_user_shared_data(void)
3356 static const WCHAR nameW
[] = {'\\','K','e','r','n','e','l','O','b','j','e','c','t','s',
3357 '\\','_','_','w','i','n','e','_','u','s','e','r','_','s','h','a','r','e','d','_','d','a','t','a',0};
3358 UNICODE_STRING name_str
= RTL_CONSTANT_STRING( nameW
);
3359 OBJECT_ATTRIBUTES attr
= { sizeof(attr
), 0, &name_str
};
3360 unsigned int status
;
3362 int res
, fd
, needs_close
;
3364 if ((status
= NtOpenSection( §ion
, SECTION_ALL_ACCESS
, &attr
)))
3366 ERR( "failed to open the USD section: %08x\n", status
);
3369 if ((res
= server_get_unix_fd( section
, 0, &fd
, &needs_close
, NULL
, NULL
)) ||
3370 (user_shared_data
!= mmap( user_shared_data
, page_size
, PROT_READ
, MAP_SHARED
|MAP_FIXED
, fd
, 0 )))
3372 ERR( "failed to remap the process USD: %d\n", res
);
3375 if (needs_close
) close( fd
);
3380 struct thread_stack_info
3389 /***********************************************************************
3390 * is_inside_thread_stack
3392 static BOOL
is_inside_thread_stack( void *ptr
, struct thread_stack_info
*stack
)
3394 TEB
*teb
= NtCurrentTeb();
3395 WOW_TEB
*wow_teb
= get_wow_teb( teb
);
3397 stack
->start
= teb
->DeallocationStack
;
3398 stack
->limit
= teb
->Tib
.StackLimit
;
3399 stack
->end
= teb
->Tib
.StackBase
;
3400 stack
->guaranteed
= max( teb
->GuaranteedStackBytes
, page_size
* (is_win64
? 2 : 1) );
3401 stack
->is_wow
= FALSE
;
3402 if ((char *)ptr
> stack
->start
&& (char *)ptr
<= stack
->end
) return TRUE
;
3404 if (!wow_teb
) return FALSE
;
3405 stack
->start
= ULongToPtr( wow_teb
->DeallocationStack
);
3406 stack
->limit
= ULongToPtr( wow_teb
->Tib
.StackLimit
);
3407 stack
->end
= ULongToPtr( wow_teb
->Tib
.StackBase
);
3408 stack
->guaranteed
= max( wow_teb
->GuaranteedStackBytes
, page_size
* (is_win64
? 1 : 2) );
3409 stack
->is_wow
= TRUE
;
3410 return ((char *)ptr
> stack
->start
&& (char *)ptr
<= stack
->end
);
3414 /***********************************************************************
3417 static NTSTATUS
grow_thread_stack( char *page
, struct thread_stack_info
*stack_info
)
3421 set_page_vprot_bits( page
, page_size
, 0, VPROT_GUARD
);
3422 mprotect_range( page
, page_size
, 0, 0 );
3423 if (page
>= stack_info
->start
+ page_size
+ stack_info
->guaranteed
)
3425 set_page_vprot_bits( page
- page_size
, page_size
, VPROT_COMMITTED
| VPROT_GUARD
, 0 );
3426 mprotect_range( page
- page_size
, page_size
, 0, 0 );
3428 else /* inside guaranteed space -> overflow exception */
3430 page
= stack_info
->start
+ page_size
;
3431 set_page_vprot_bits( page
, stack_info
->guaranteed
, VPROT_COMMITTED
, VPROT_GUARD
);
3432 mprotect_range( page
, stack_info
->guaranteed
, 0, 0 );
3433 ret
= STATUS_STACK_OVERFLOW
;
3435 if (stack_info
->is_wow
)
3437 WOW_TEB
*wow_teb
= get_wow_teb( NtCurrentTeb() );
3438 wow_teb
->Tib
.StackLimit
= PtrToUlong( page
);
3440 else NtCurrentTeb()->Tib
.StackLimit
= page
;
3445 /***********************************************************************
3446 * virtual_handle_fault
3448 NTSTATUS
virtual_handle_fault( void *addr
, DWORD err
, void *stack
)
3450 NTSTATUS ret
= STATUS_ACCESS_VIOLATION
;
3451 char *page
= ROUND_ADDR( addr
, page_mask
);
3454 mutex_lock( &virtual_mutex
); /* no need for signal masking inside signal handler */
3455 vprot
= get_page_vprot( page
);
3456 if (!is_inside_signal_stack( stack
) && (vprot
& VPROT_GUARD
))
3458 struct thread_stack_info stack_info
;
3459 if (!is_inside_thread_stack( page
, &stack_info
))
3461 set_page_vprot_bits( page
, page_size
, 0, VPROT_GUARD
);
3462 mprotect_range( page
, page_size
, 0, 0 );
3463 ret
= STATUS_GUARD_PAGE_VIOLATION
;
3465 else ret
= grow_thread_stack( page
, &stack_info
);
3467 else if (err
& EXCEPTION_WRITE_FAULT
)
3469 if (vprot
& VPROT_WRITEWATCH
)
3471 set_page_vprot_bits( page
, page_size
, 0, VPROT_WRITEWATCH
);
3472 mprotect_range( page
, page_size
, 0, 0 );
3474 /* ignore fault if page is writable now */
3475 if (get_unix_prot( get_page_vprot( page
)) & PROT_WRITE
)
3477 if ((vprot
& VPROT_WRITEWATCH
) || is_write_watch_range( page
, page_size
))
3478 ret
= STATUS_SUCCESS
;
3481 mutex_unlock( &virtual_mutex
);
3486 /***********************************************************************
3487 * virtual_setup_exception
3489 void *virtual_setup_exception( void *stack_ptr
, size_t size
, EXCEPTION_RECORD
*rec
)
3491 char *stack
= stack_ptr
;
3492 struct thread_stack_info stack_info
;
3494 if (!is_inside_thread_stack( stack
, &stack_info
))
3496 if (is_inside_signal_stack( stack
))
3498 ERR( "nested exception on signal stack addr %p stack %p\n", rec
->ExceptionAddress
, stack
);
3501 WARN( "exception outside of stack limits addr %p stack %p (%p-%p-%p)\n",
3502 rec
->ExceptionAddress
, stack
, NtCurrentTeb()->DeallocationStack
,
3503 NtCurrentTeb()->Tib
.StackLimit
, NtCurrentTeb()->Tib
.StackBase
);
3504 return stack
- size
;
3509 if (stack
< stack_info
.start
+ 4096)
3511 /* stack overflow on last page, unrecoverable */
3512 UINT diff
= stack_info
.start
+ 4096 - stack
;
3513 ERR( "stack overflow %u bytes addr %p stack %p (%p-%p-%p)\n",
3514 diff
, rec
->ExceptionAddress
, stack
, stack_info
.start
, stack_info
.limit
, stack_info
.end
);
3517 else if (stack
< stack_info
.limit
)
3519 mutex_lock( &virtual_mutex
); /* no need for signal masking inside signal handler */
3520 if ((get_page_vprot( stack
) & VPROT_GUARD
) &&
3521 grow_thread_stack( ROUND_ADDR( stack
, page_mask
), &stack_info
))
3523 rec
->ExceptionCode
= STATUS_STACK_OVERFLOW
;
3524 rec
->NumberParameters
= 0;
3526 mutex_unlock( &virtual_mutex
);
3528 #if defined(VALGRIND_MAKE_MEM_UNDEFINED)
3529 VALGRIND_MAKE_MEM_UNDEFINED( stack
, size
);
3530 #elif defined(VALGRIND_MAKE_WRITABLE)
3531 VALGRIND_MAKE_WRITABLE( stack
, size
);
3537 /***********************************************************************
3538 * check_write_access
3540 * Check if the memory range is writable, temporarily disabling write watches if necessary.
3542 static NTSTATUS
check_write_access( void *base
, size_t size
, BOOL
*has_write_watch
)
3545 char *addr
= ROUND_ADDR( base
, page_mask
);
3547 size
= ROUND_SIZE( base
, size
);
3548 for (i
= 0; i
< size
; i
+= page_size
)
3550 BYTE vprot
= get_page_vprot( addr
+ i
);
3551 if (vprot
& VPROT_WRITEWATCH
) *has_write_watch
= TRUE
;
3552 if (!(get_unix_prot( vprot
& ~VPROT_WRITEWATCH
) & PROT_WRITE
))
3553 return STATUS_INVALID_USER_BUFFER
;
3555 if (*has_write_watch
)
3556 mprotect_range( addr
, size
, 0, VPROT_WRITEWATCH
); /* temporarily enable write access */
3557 return STATUS_SUCCESS
;
3561 /***********************************************************************
3562 * virtual_locked_server_call
3564 unsigned int virtual_locked_server_call( void *req_ptr
)
3566 struct __server_request_info
* const req
= req_ptr
;
3568 void *addr
= req
->reply_data
;
3569 data_size_t size
= req
->u
.req
.request_header
.reply_size
;
3570 BOOL has_write_watch
= FALSE
;
3571 unsigned int ret
= STATUS_ACCESS_VIOLATION
;
3573 if (!size
) return wine_server_call( req_ptr
);
3575 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3576 if (!(ret
= check_write_access( addr
, size
, &has_write_watch
)))
3578 ret
= server_call_unlocked( req
);
3579 if (has_write_watch
) update_write_watches( addr
, size
, wine_server_reply_size( req
));
3581 else memset( &req
->u
.reply
, 0, sizeof(req
->u
.reply
) );
3582 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3587 /***********************************************************************
3588 * virtual_locked_read
3590 ssize_t
virtual_locked_read( int fd
, void *addr
, size_t size
)
3593 BOOL has_write_watch
= FALSE
;
3596 ssize_t ret
= read( fd
, addr
, size
);
3597 if (ret
!= -1 || errno
!= EFAULT
) return ret
;
3599 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3600 if (!check_write_access( addr
, size
, &has_write_watch
))
3602 ret
= read( fd
, addr
, size
);
3604 if (has_write_watch
) update_write_watches( addr
, size
, max( 0, ret
));
3606 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3612 /***********************************************************************
3613 * virtual_locked_pread
3615 ssize_t
virtual_locked_pread( int fd
, void *addr
, size_t size
, off_t offset
)
3618 BOOL has_write_watch
= FALSE
;
3621 ssize_t ret
= pread( fd
, addr
, size
, offset
);
3622 if (ret
!= -1 || errno
!= EFAULT
) return ret
;
3624 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3625 if (!check_write_access( addr
, size
, &has_write_watch
))
3627 ret
= pread( fd
, addr
, size
, offset
);
3629 if (has_write_watch
) update_write_watches( addr
, size
, max( 0, ret
));
3631 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3637 /***********************************************************************
3638 * virtual_locked_recvmsg
3640 ssize_t
virtual_locked_recvmsg( int fd
, struct msghdr
*hdr
, int flags
)
3644 BOOL has_write_watch
= FALSE
;
3647 ssize_t ret
= recvmsg( fd
, hdr
, flags
);
3648 if (ret
!= -1 || errno
!= EFAULT
) return ret
;
3650 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3651 for (i
= 0; i
< hdr
->msg_iovlen
; i
++)
3652 if (check_write_access( hdr
->msg_iov
[i
].iov_base
, hdr
->msg_iov
[i
].iov_len
, &has_write_watch
))
3654 if (i
== hdr
->msg_iovlen
)
3656 ret
= recvmsg( fd
, hdr
, flags
);
3659 if (has_write_watch
)
3660 while (i
--) update_write_watches( hdr
->msg_iov
[i
].iov_base
, hdr
->msg_iov
[i
].iov_len
, 0 );
3662 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3668 /***********************************************************************
3669 * virtual_is_valid_code_address
3671 BOOL
virtual_is_valid_code_address( const void *addr
, SIZE_T size
)
3673 struct file_view
*view
;
3677 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3678 if ((view
= find_view( addr
, size
)))
3679 ret
= !(view
->protect
& VPROT_SYSTEM
); /* system views are not visible to the app */
3680 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3685 /***********************************************************************
3686 * virtual_check_buffer_for_read
3688 * Check if a memory buffer can be read, triggering page faults if needed for DIB section access.
3690 BOOL
virtual_check_buffer_for_read( const void *ptr
, SIZE_T size
)
3692 if (!size
) return TRUE
;
3693 if (!ptr
) return FALSE
;
3697 volatile const char *p
= ptr
;
3698 char dummy
__attribute__((unused
));
3699 SIZE_T count
= size
;
3701 while (count
> page_size
)
3708 dummy
= p
[count
- 1];
3719 /***********************************************************************
3720 * virtual_check_buffer_for_write
3722 * Check if a memory buffer can be written to, triggering page faults if needed for write watches.
3724 BOOL
virtual_check_buffer_for_write( void *ptr
, SIZE_T size
)
3726 if (!size
) return TRUE
;
3727 if (!ptr
) return FALSE
;
3731 volatile char *p
= ptr
;
3732 SIZE_T count
= size
;
3734 while (count
> page_size
)
3752 /***********************************************************************
3753 * virtual_uninterrupted_read_memory
3755 * Similar to NtReadVirtualMemory, but without wineserver calls. Moreover
3756 * permissions are checked before accessing each page, to ensure that no
3757 * exceptions can happen.
3759 SIZE_T
virtual_uninterrupted_read_memory( const void *addr
, void *buffer
, SIZE_T size
)
3761 struct file_view
*view
;
3763 SIZE_T bytes_read
= 0;
3765 if (!size
) return 0;
3767 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3768 if ((view
= find_view( addr
, size
)))
3770 if (!(view
->protect
& VPROT_SYSTEM
))
3772 while (bytes_read
< size
&& (get_unix_prot( get_page_vprot( addr
)) & PROT_READ
))
3774 SIZE_T block_size
= min( size
- bytes_read
, page_size
- ((UINT_PTR
)addr
& page_mask
) );
3775 memcpy( buffer
, addr
, block_size
);
3777 addr
= (const void *)((const char *)addr
+ block_size
);
3778 buffer
= (void *)((char *)buffer
+ block_size
);
3779 bytes_read
+= block_size
;
3783 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3788 /***********************************************************************
3789 * virtual_uninterrupted_write_memory
3791 * Similar to NtWriteVirtualMemory, but without wineserver calls. Moreover
3792 * permissions are checked before accessing each page, to ensure that no
3793 * exceptions can happen.
3795 NTSTATUS
virtual_uninterrupted_write_memory( void *addr
, const void *buffer
, SIZE_T size
)
3797 BOOL has_write_watch
= FALSE
;
3801 if (!size
) return STATUS_SUCCESS
;
3803 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3804 if (!(ret
= check_write_access( addr
, size
, &has_write_watch
)))
3806 memcpy( addr
, buffer
, size
);
3807 if (has_write_watch
) update_write_watches( addr
, size
, size
);
3809 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3814 /***********************************************************************
3815 * virtual_set_force_exec
3817 * Whether to force exec prot on all views.
3819 void virtual_set_force_exec( BOOL enable
)
3821 struct file_view
*view
;
3824 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3825 if (!force_exec_prot
!= !enable
) /* change all existing views */
3827 force_exec_prot
= enable
;
3829 WINE_RB_FOR_EACH_ENTRY( view
, &views_tree
, struct file_view
, entry
)
3831 /* file mappings are always accessible */
3832 BYTE commit
= is_view_valloc( view
) ? 0 : VPROT_COMMITTED
;
3834 mprotect_range( view
->base
, view
->size
, commit
, 0 );
3837 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
3846 /* free reserved areas above the limit; callback for mmap_enum_reserved_areas */
3847 static int free_reserved_memory( void *base
, SIZE_T size
, void *arg
)
3849 struct free_range
*range
= arg
;
3851 if ((char *)base
>= range
->limit
) return 0;
3852 if ((char *)base
+ size
<= range
->base
) return 0;
3853 if ((char *)base
< range
->base
)
3855 size
-= range
->base
- (char *)base
;
3858 if ((char *)base
+ size
> range
->limit
) size
= range
->limit
- (char *)base
;
3859 remove_reserved_area( base
, size
);
3860 return 1; /* stop enumeration since the list has changed */
3863 /***********************************************************************
3864 * virtual_release_address_space
3866 * Release some address space once we have loaded and initialized the app.
3868 static void virtual_release_address_space(void)
3870 struct free_range range
;
3872 range
.base
= (char *)0x82000000;
3873 range
.limit
= get_wow_user_space_limit();
3875 if (range
.limit
> (char *)0xfffff000) return; /* 64-bit limit, nothing to do */
3877 if (range
.limit
> range
.base
)
3879 while (mmap_enum_reserved_areas( free_reserved_memory
, &range
, 1 )) /* nothing */;
3881 /* On macOS, we still want to free some of low memory, for OpenGL resources */
3882 range
.base
= (char *)0x40000000;
3887 else range
.base
= (char *)0x20000000;
3889 range
.limit
= (char *)0x7f000000;
3890 while (mmap_enum_reserved_areas( free_reserved_memory
, &range
, 0 )) /* nothing */;
3894 /***********************************************************************
3895 * virtual_set_large_address_space
3897 * Enable use of a large address space when allowed by the application.
3899 void virtual_set_large_address_space(void)
3901 /* no large address space on win9x */
3902 if (peb
->OSPlatformId
!= VER_PLATFORM_WIN32_NT
) return;
3904 user_space_limit
= working_set_limit
= address_space_limit
;
3908 /***********************************************************************
3909 * allocate_virtual_memory
3911 * NtAllocateVirtualMemory[Ex] implementation.
3913 static NTSTATUS
allocate_virtual_memory( void **ret
, SIZE_T
*size_ptr
, ULONG type
, ULONG protect
,
3914 ULONG_PTR limit
, ULONG_PTR align
)
3918 BOOL is_dos_memory
= FALSE
;
3919 struct file_view
*view
;
3921 SIZE_T size
= *size_ptr
;
3922 NTSTATUS status
= STATUS_SUCCESS
;
3924 /* Round parameters to a page boundary */
3926 if (is_beyond_limit( 0, size
, working_set_limit
)) return STATUS_WORKING_SET_LIMIT_RANGE
;
3930 if (type
& MEM_RESERVE
) /* Round down to 64k boundary */
3931 base
= ROUND_ADDR( *ret
, granularity_mask
);
3933 base
= ROUND_ADDR( *ret
, page_mask
);
3934 size
= (((UINT_PTR
)*ret
+ size
+ page_mask
) & ~page_mask
) - (UINT_PTR
)base
;
3936 /* disallow low 64k, wrap-around and kernel space */
3937 if (((char *)base
< (char *)0x10000) ||
3938 ((char *)base
+ size
< (char *)base
) ||
3939 is_beyond_limit( base
, size
, address_space_limit
))
3941 /* address 1 is magic to mean DOS area */
3942 if (!base
&& *ret
== (void *)1 && size
== 0x110000) is_dos_memory
= TRUE
;
3943 else return STATUS_INVALID_PARAMETER
;
3949 size
= (size
+ page_mask
) & ~page_mask
;
3952 /* Compute the alloc type flags */
3954 if (!(type
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)) ||
3955 (type
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_TOP_DOWN
| MEM_WRITE_WATCH
| MEM_RESET
)))
3957 WARN("called with wrong alloc type flags (%08x) !\n", (int)type
);
3958 return STATUS_INVALID_PARAMETER
;
3961 /* Reserve the memory */
3963 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
3965 if ((type
& MEM_RESERVE
) || !base
)
3967 if (!(status
= get_vprot_flags( protect
, &vprot
, FALSE
)))
3969 if (type
& MEM_COMMIT
) vprot
|= VPROT_COMMITTED
;
3970 if (type
& MEM_WRITE_WATCH
) vprot
|= VPROT_WRITEWATCH
;
3971 if (protect
& PAGE_NOCACHE
) vprot
|= SEC_NOCACHE
;
3973 if (vprot
& VPROT_WRITECOPY
) status
= STATUS_INVALID_PAGE_PROTECTION
;
3974 else if (is_dos_memory
) status
= allocate_dos_memory( &view
, vprot
);
3975 else status
= map_view( &view
, base
, size
, type
& MEM_TOP_DOWN
, vprot
, limit
,
3976 align
? align
- 1 : granularity_mask
);
3978 if (status
== STATUS_SUCCESS
) base
= view
->base
;
3981 else if (type
& MEM_RESET
)
3983 if (!(view
= find_view( base
, size
))) status
= STATUS_NOT_MAPPED_VIEW
;
3984 else madvise( base
, size
, MADV_DONTNEED
);
3986 else /* commit the pages */
3988 if (!(view
= find_view( base
, size
))) status
= STATUS_NOT_MAPPED_VIEW
;
3989 else if (view
->protect
& SEC_FILE
) status
= STATUS_ALREADY_COMMITTED
;
3990 else if (!(status
= set_protection( view
, base
, size
, protect
)) && (view
->protect
& SEC_RESERVE
))
3992 SERVER_START_REQ( add_mapping_committed_range
)
3994 req
->base
= wine_server_client_ptr( view
->base
);
3995 req
->offset
= (char *)base
- (char *)view
->base
;
3997 wine_server_call( req
);
4003 if (!status
) VIRTUAL_DEBUG_DUMP_VIEW( view
);
4005 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4007 if (status
== STATUS_SUCCESS
)
4016 /***********************************************************************
4017 * NtAllocateVirtualMemory (NTDLL.@)
4018 * ZwAllocateVirtualMemory (NTDLL.@)
4020 NTSTATUS WINAPI
NtAllocateVirtualMemory( HANDLE process
, PVOID
*ret
, ULONG_PTR zero_bits
,
4021 SIZE_T
*size_ptr
, ULONG type
, ULONG protect
)
4025 TRACE("%p %p %08lx %x %08x\n", process
, *ret
, *size_ptr
, (int)type
, (int)protect
);
4027 if (!*size_ptr
) return STATUS_INVALID_PARAMETER
;
4028 if (zero_bits
> 21 && zero_bits
< 32) return STATUS_INVALID_PARAMETER_3
;
4029 if (zero_bits
> 32 && zero_bits
< granularity_mask
) return STATUS_INVALID_PARAMETER_3
;
4031 if (!is_old_wow64() && zero_bits
>= 32) return STATUS_INVALID_PARAMETER_3
;
4034 if (process
!= NtCurrentProcess())
4037 apc_result_t result
;
4038 unsigned int status
;
4040 memset( &call
, 0, sizeof(call
) );
4042 call
.virtual_alloc
.type
= APC_VIRTUAL_ALLOC
;
4043 call
.virtual_alloc
.addr
= wine_server_client_ptr( *ret
);
4044 call
.virtual_alloc
.size
= *size_ptr
;
4045 call
.virtual_alloc
.zero_bits
= zero_bits
;
4046 call
.virtual_alloc
.op_type
= type
;
4047 call
.virtual_alloc
.prot
= protect
;
4048 status
= server_queue_process_apc( process
, &call
, &result
);
4049 if (status
!= STATUS_SUCCESS
) return status
;
4051 if (result
.virtual_alloc
.status
== STATUS_SUCCESS
)
4053 *ret
= wine_server_get_ptr( result
.virtual_alloc
.addr
);
4054 *size_ptr
= result
.virtual_alloc
.size
;
4056 return result
.virtual_alloc
.status
;
4060 limit
= get_zero_bits_mask( zero_bits
);
4064 return allocate_virtual_memory( ret
, size_ptr
, type
, protect
, limit
, 0 );
4068 /***********************************************************************
4069 * NtAllocateVirtualMemoryEx (NTDLL.@)
4070 * ZwAllocateVirtualMemoryEx (NTDLL.@)
4072 NTSTATUS WINAPI
NtAllocateVirtualMemoryEx( HANDLE process
, PVOID
*ret
, SIZE_T
*size_ptr
, ULONG type
,
4073 ULONG protect
, MEM_EXTENDED_PARAMETER
*parameters
,
4076 ULONG_PTR limit
= 0;
4077 ULONG_PTR align
= 0;
4079 TRACE("%p %p %08lx %x %08x %p %u\n",
4080 process
, *ret
, *size_ptr
, (int)type
, (int)protect
, parameters
, (int)count
);
4082 if (count
&& !parameters
) return STATUS_INVALID_PARAMETER
;
4086 MEM_ADDRESS_REQUIREMENTS
*r
= NULL
;
4089 for (i
= 0; i
< count
; ++i
)
4091 if (parameters
[i
].Type
== MemExtendedParameterInvalidType
|| parameters
[i
].Type
>= MemExtendedParameterMax
)
4093 WARN( "Invalid parameter type %d.\n", parameters
[i
].Type
);
4094 return STATUS_INVALID_PARAMETER
;
4096 if (parameters
[i
].Type
!= MemExtendedParameterAddressRequirements
)
4098 FIXME( "Parameter type %d is not supported.\n", parameters
[i
].Type
);
4103 WARN( "Duplicate parameter.\n" );
4104 return STATUS_INVALID_PARAMETER
;
4106 r
= (MEM_ADDRESS_REQUIREMENTS
*)parameters
[i
].Pointer
;
4108 if (r
->LowestStartingAddress
)
4109 FIXME( "Not supported requirements LowestStartingAddress %p, Alignment %p.\n",
4110 r
->LowestStartingAddress
, (void *)r
->Alignment
);
4114 if (*ret
|| (r
->Alignment
& (r
->Alignment
- 1)) || r
->Alignment
- 1 < granularity_mask
)
4116 WARN( "Invalid alignment %lu.\n", r
->Alignment
);
4117 return STATUS_INVALID_PARAMETER
;
4119 align
= r
->Alignment
;
4122 limit
= (ULONG_PTR
)r
->HighestEndingAddress
;
4123 if (limit
&& (*ret
|| limit
> (ULONG_PTR
)user_space_limit
|| ((limit
+ 1) & (page_mask
- 1))))
4125 WARN( "Invalid limit %p.\n", r
->HighestEndingAddress
);
4126 return STATUS_INVALID_PARAMETER
;
4128 TRACE( "limit %p, align %p.\n", (void *)limit
, (void *)align
);
4132 if (!*size_ptr
) return STATUS_INVALID_PARAMETER
;
4134 if (process
!= NtCurrentProcess())
4137 apc_result_t result
;
4138 unsigned int status
;
4140 memset( &call
, 0, sizeof(call
) );
4142 call
.virtual_alloc_ex
.type
= APC_VIRTUAL_ALLOC_EX
;
4143 call
.virtual_alloc_ex
.addr
= wine_server_client_ptr( *ret
);
4144 call
.virtual_alloc_ex
.size
= *size_ptr
;
4145 call
.virtual_alloc_ex
.limit
= limit
;
4146 call
.virtual_alloc_ex
.align
= align
;
4147 call
.virtual_alloc_ex
.op_type
= type
;
4148 call
.virtual_alloc_ex
.prot
= protect
;
4149 status
= server_queue_process_apc( process
, &call
, &result
);
4150 if (status
!= STATUS_SUCCESS
) return status
;
4152 if (result
.virtual_alloc_ex
.status
== STATUS_SUCCESS
)
4154 *ret
= wine_server_get_ptr( result
.virtual_alloc_ex
.addr
);
4155 *size_ptr
= result
.virtual_alloc_ex
.size
;
4157 return result
.virtual_alloc_ex
.status
;
4160 return allocate_virtual_memory( ret
, size_ptr
, type
, protect
, limit
, align
);
4164 /***********************************************************************
4165 * NtFreeVirtualMemory (NTDLL.@)
4166 * ZwFreeVirtualMemory (NTDLL.@)
4168 NTSTATUS WINAPI
NtFreeVirtualMemory( HANDLE process
, PVOID
*addr_ptr
, SIZE_T
*size_ptr
, ULONG type
)
4170 struct file_view
*view
;
4173 unsigned int status
= STATUS_SUCCESS
;
4174 LPVOID addr
= *addr_ptr
;
4175 SIZE_T size
= *size_ptr
;
4177 TRACE("%p %p %08lx %x\n", process
, addr
, size
, (int)type
);
4179 if (process
!= NtCurrentProcess())
4182 apc_result_t result
;
4184 memset( &call
, 0, sizeof(call
) );
4186 call
.virtual_free
.type
= APC_VIRTUAL_FREE
;
4187 call
.virtual_free
.addr
= wine_server_client_ptr( addr
);
4188 call
.virtual_free
.size
= size
;
4189 call
.virtual_free
.op_type
= type
;
4190 status
= server_queue_process_apc( process
, &call
, &result
);
4191 if (status
!= STATUS_SUCCESS
) return status
;
4193 if (result
.virtual_free
.status
== STATUS_SUCCESS
)
4195 *addr_ptr
= wine_server_get_ptr( result
.virtual_free
.addr
);
4196 *size_ptr
= result
.virtual_free
.size
;
4198 return result
.virtual_free
.status
;
4201 /* Fix the parameters */
4203 if (size
) size
= ROUND_SIZE( addr
, size
);
4204 base
= ROUND_ADDR( addr
, page_mask
);
4206 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4208 /* avoid freeing the DOS area when a broken app passes a NULL pointer */
4211 /* address 1 is magic to mean release reserved space */
4212 if (addr
== (void *)1 && !size
&& type
== MEM_RELEASE
) virtual_release_address_space();
4213 else status
= STATUS_INVALID_PARAMETER
;
4215 else if (!(view
= find_view( base
, size
)) || !is_view_valloc( view
))
4217 status
= STATUS_INVALID_PARAMETER
;
4219 else if (type
== MEM_RELEASE
)
4221 /* Free the pages */
4223 if (size
) status
= STATUS_INVALID_PARAMETER
;
4224 else if (base
!= view
->base
) status
= STATUS_FREE_VM_NOT_AT_BASE
;
4228 *size_ptr
= view
->size
;
4229 delete_view( view
);
4232 else if (type
== MEM_DECOMMIT
)
4234 if (!size
&& base
!= view
->base
) status
= STATUS_FREE_VM_NOT_AT_BASE
;
4235 else status
= decommit_pages( view
, base
- (char *)view
->base
, size
);
4236 if (status
== STATUS_SUCCESS
)
4244 WARN("called with wrong free type flags (%08x) !\n", (int)type
);
4245 status
= STATUS_INVALID_PARAMETER
;
4248 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4253 /***********************************************************************
4254 * NtProtectVirtualMemory (NTDLL.@)
4255 * ZwProtectVirtualMemory (NTDLL.@)
4257 NTSTATUS WINAPI
NtProtectVirtualMemory( HANDLE process
, PVOID
*addr_ptr
, SIZE_T
*size_ptr
,
4258 ULONG new_prot
, ULONG
*old_prot
)
4260 struct file_view
*view
;
4262 unsigned int status
= STATUS_SUCCESS
;
4265 SIZE_T size
= *size_ptr
;
4266 LPVOID addr
= *addr_ptr
;
4269 TRACE("%p %p %08lx %08x\n", process
, addr
, size
, (int)new_prot
);
4272 return STATUS_ACCESS_VIOLATION
;
4274 if (process
!= NtCurrentProcess())
4277 apc_result_t result
;
4279 memset( &call
, 0, sizeof(call
) );
4281 call
.virtual_protect
.type
= APC_VIRTUAL_PROTECT
;
4282 call
.virtual_protect
.addr
= wine_server_client_ptr( addr
);
4283 call
.virtual_protect
.size
= size
;
4284 call
.virtual_protect
.prot
= new_prot
;
4285 status
= server_queue_process_apc( process
, &call
, &result
);
4286 if (status
!= STATUS_SUCCESS
) return status
;
4288 if (result
.virtual_protect
.status
== STATUS_SUCCESS
)
4290 *addr_ptr
= wine_server_get_ptr( result
.virtual_protect
.addr
);
4291 *size_ptr
= result
.virtual_protect
.size
;
4292 *old_prot
= result
.virtual_protect
.prot
;
4294 return result
.virtual_protect
.status
;
4297 /* Fix the parameters */
4299 size
= ROUND_SIZE( addr
, size
);
4300 base
= ROUND_ADDR( addr
, page_mask
);
4302 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4304 if ((view
= find_view( base
, size
)))
4306 /* Make sure all the pages are committed */
4307 if (get_committed_size( view
, base
, &vprot
, VPROT_COMMITTED
) >= size
&& (vprot
& VPROT_COMMITTED
))
4309 old
= get_win32_prot( vprot
, view
->protect
);
4310 status
= set_protection( view
, base
, size
, new_prot
);
4312 else status
= STATUS_NOT_COMMITTED
;
4314 else status
= STATUS_INVALID_PARAMETER
;
4316 if (!status
) VIRTUAL_DEBUG_DUMP_VIEW( view
);
4318 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4320 if (status
== STATUS_SUCCESS
)
4330 /* retrieve state for a free memory area; callback for mmap_enum_reserved_areas */
4331 static int get_free_mem_state_callback( void *start
, SIZE_T size
, void *arg
)
4333 MEMORY_BASIC_INFORMATION
*info
= arg
;
4334 void *end
= (char *)start
+ size
;
4336 if ((char *)info
->BaseAddress
+ info
->RegionSize
<= (char *)start
) return 0;
4338 if (info
->BaseAddress
>= end
)
4340 if (info
->AllocationBase
< end
) info
->AllocationBase
= end
;
4344 if (info
->BaseAddress
>= start
|| start
<= address_space_start
)
4346 /* it's a real free area */
4347 info
->State
= MEM_FREE
;
4348 info
->Protect
= PAGE_NOACCESS
;
4349 info
->AllocationBase
= 0;
4350 info
->AllocationProtect
= 0;
4352 if ((char *)info
->BaseAddress
+ info
->RegionSize
> (char *)end
)
4353 info
->RegionSize
= (char *)end
- (char *)info
->BaseAddress
;
4355 else /* outside of the reserved area, pretend it's allocated */
4357 info
->RegionSize
= (char *)start
- (char *)info
->BaseAddress
;
4359 info
->State
= MEM_RESERVE
;
4360 info
->Protect
= PAGE_NOACCESS
;
4361 info
->AllocationProtect
= PAGE_NOACCESS
;
4362 info
->Type
= MEM_PRIVATE
;
4364 info
->State
= MEM_FREE
;
4365 info
->Protect
= PAGE_NOACCESS
;
4366 info
->AllocationBase
= 0;
4367 info
->AllocationProtect
= 0;
4374 static unsigned int fill_basic_memory_info( const void *addr
, MEMORY_BASIC_INFORMATION
*info
)
4376 char *base
, *alloc_base
= 0, *alloc_end
= working_set_limit
;
4377 struct wine_rb_entry
*ptr
;
4378 struct file_view
*view
;
4381 base
= ROUND_ADDR( addr
, page_mask
);
4383 if (is_beyond_limit( base
, 1, working_set_limit
)) return STATUS_INVALID_PARAMETER
;
4385 /* Find the view containing the address */
4387 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4388 ptr
= views_tree
.root
;
4391 view
= WINE_RB_ENTRY_VALUE( ptr
, struct file_view
, entry
);
4392 if ((char *)view
->base
> base
)
4394 alloc_end
= view
->base
;
4397 else if ((char *)view
->base
+ view
->size
<= base
)
4399 alloc_base
= (char *)view
->base
+ view
->size
;
4404 alloc_base
= view
->base
;
4405 alloc_end
= (char *)view
->base
+ view
->size
;
4410 /* Fill the info structure */
4412 info
->AllocationBase
= alloc_base
;
4413 info
->BaseAddress
= base
;
4414 info
->RegionSize
= alloc_end
- base
;
4418 if (!mmap_enum_reserved_areas( get_free_mem_state_callback
, info
, 0 ))
4420 /* not in a reserved area at all, pretend it's allocated */
4422 if (base
>= (char *)address_space_start
)
4424 info
->State
= MEM_RESERVE
;
4425 info
->Protect
= PAGE_NOACCESS
;
4426 info
->AllocationProtect
= PAGE_NOACCESS
;
4427 info
->Type
= MEM_PRIVATE
;
4432 info
->State
= MEM_FREE
;
4433 info
->Protect
= PAGE_NOACCESS
;
4434 info
->AllocationBase
= 0;
4435 info
->AllocationProtect
= 0;
4444 info
->RegionSize
= get_committed_size( view
, base
, &vprot
, ~VPROT_WRITEWATCH
);
4445 info
->State
= (vprot
& VPROT_COMMITTED
) ? MEM_COMMIT
: MEM_RESERVE
;
4446 info
->Protect
= (vprot
& VPROT_COMMITTED
) ? get_win32_prot( vprot
, view
->protect
) : 0;
4447 info
->AllocationProtect
= get_win32_prot( view
->protect
, view
->protect
);
4448 if (view
->protect
& SEC_IMAGE
) info
->Type
= MEM_IMAGE
;
4449 else if (view
->protect
& (SEC_FILE
| SEC_RESERVE
| SEC_COMMIT
)) info
->Type
= MEM_MAPPED
;
4450 else info
->Type
= MEM_PRIVATE
;
4452 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4454 return STATUS_SUCCESS
;
4457 /* get basic information about a memory block */
4458 static unsigned int get_basic_memory_info( HANDLE process
, LPCVOID addr
,
4459 MEMORY_BASIC_INFORMATION
*info
,
4460 SIZE_T len
, SIZE_T
*res_len
)
4462 unsigned int status
;
4464 if (len
< sizeof(*info
))
4465 return STATUS_INFO_LENGTH_MISMATCH
;
4467 if (process
!= NtCurrentProcess())
4470 apc_result_t result
;
4472 memset( &call
, 0, sizeof(call
) );
4474 call
.virtual_query
.type
= APC_VIRTUAL_QUERY
;
4475 call
.virtual_query
.addr
= wine_server_client_ptr( addr
);
4476 status
= server_queue_process_apc( process
, &call
, &result
);
4477 if (status
!= STATUS_SUCCESS
) return status
;
4479 if (result
.virtual_query
.status
== STATUS_SUCCESS
)
4481 info
->BaseAddress
= wine_server_get_ptr( result
.virtual_query
.base
);
4482 info
->AllocationBase
= wine_server_get_ptr( result
.virtual_query
.alloc_base
);
4483 info
->RegionSize
= result
.virtual_query
.size
;
4484 info
->Protect
= result
.virtual_query
.prot
;
4485 info
->AllocationProtect
= result
.virtual_query
.alloc_prot
;
4486 info
->State
= (DWORD
)result
.virtual_query
.state
<< 12;
4487 info
->Type
= (DWORD
)result
.virtual_query
.alloc_type
<< 16;
4488 if (info
->RegionSize
!= result
.virtual_query
.size
) /* truncated */
4489 return STATUS_INVALID_PARAMETER
; /* FIXME */
4490 if (res_len
) *res_len
= sizeof(*info
);
4492 return result
.virtual_query
.status
;
4495 if ((status
= fill_basic_memory_info( addr
, info
))) return status
;
4497 if (res_len
) *res_len
= sizeof(*info
);
4498 return STATUS_SUCCESS
;
4501 static unsigned int get_memory_region_info( HANDLE process
, LPCVOID addr
, MEMORY_REGION_INFORMATION
*info
,
4502 SIZE_T len
, SIZE_T
*res_len
)
4504 MEMORY_BASIC_INFORMATION basic_info
;
4505 unsigned int status
;
4507 if (len
< FIELD_OFFSET(MEMORY_REGION_INFORMATION
, CommitSize
))
4508 return STATUS_INFO_LENGTH_MISMATCH
;
4510 if (process
!= NtCurrentProcess())
4512 FIXME("Unimplemented for other processes.\n");
4513 return STATUS_NOT_IMPLEMENTED
;
4516 if ((status
= fill_basic_memory_info( addr
, &basic_info
))) return status
;
4518 info
->AllocationBase
= basic_info
.AllocationBase
;
4519 info
->AllocationProtect
= basic_info
.AllocationProtect
;
4520 info
->RegionType
= 0; /* FIXME */
4521 if (len
>= FIELD_OFFSET(MEMORY_REGION_INFORMATION
, CommitSize
))
4522 info
->RegionSize
= basic_info
.RegionSize
;
4523 if (len
>= FIELD_OFFSET(MEMORY_REGION_INFORMATION
, PartitionId
))
4524 info
->CommitSize
= basic_info
.State
== MEM_COMMIT
? basic_info
.RegionSize
: 0;
4526 if (res_len
) *res_len
= sizeof(*info
);
4527 return STATUS_SUCCESS
;
4530 static NTSTATUS
get_working_set_ex( HANDLE process
, LPCVOID addr
,
4531 MEMORY_WORKING_SET_EX_INFORMATION
*info
,
4532 SIZE_T len
, SIZE_T
*res_len
)
4534 #if !defined(HAVE_LIBPROCSTAT)
4535 static int pagemap_fd
= -2;
4537 MEMORY_WORKING_SET_EX_INFORMATION
*p
;
4540 if (process
!= NtCurrentProcess())
4542 FIXME( "(process=%p,addr=%p) Unimplemented information class: MemoryWorkingSetExInformation\n", process
, addr
);
4543 return STATUS_INVALID_INFO_CLASS
;
4546 #if defined(HAVE_LIBPROCSTAT)
4548 struct procstat
*pstat
;
4549 unsigned int proc_count
;
4550 struct kinfo_proc
*kip
= NULL
;
4551 unsigned int vmentry_count
= 0;
4552 struct kinfo_vmentry
*vmentries
= NULL
;
4554 pstat
= procstat_open_sysctl();
4556 kip
= procstat_getprocs( pstat
, KERN_PROC_PID
, getpid(), &proc_count
);
4558 vmentries
= procstat_getvmmap( pstat
, kip
, &vmentry_count
);
4559 if (vmentries
== NULL
)
4560 WARN( "couldn't get process vmmap, errno %d\n", errno
);
4562 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4563 for (p
= info
; (UINT_PTR
)(p
+ 1) <= (UINT_PTR
)info
+ len
; p
++)
4566 struct kinfo_vmentry
*entry
= NULL
;
4568 struct file_view
*view
;
4570 memset( &p
->VirtualAttributes
, 0, sizeof(p
->VirtualAttributes
) );
4571 if ((view
= find_view( p
->VirtualAddress
, 0 )) &&
4572 get_committed_size( view
, p
->VirtualAddress
, &vprot
, VPROT_COMMITTED
) &&
4573 (vprot
& VPROT_COMMITTED
))
4575 for (i
= 0; i
< vmentry_count
&& entry
== NULL
; i
++)
4577 if (vmentries
[i
].kve_start
<= (ULONG_PTR
)p
->VirtualAddress
&& (ULONG_PTR
)p
->VirtualAddress
<= vmentries
[i
].kve_end
)
4578 entry
= &vmentries
[i
];
4581 p
->VirtualAttributes
.Valid
= !(vprot
& VPROT_GUARD
) && (vprot
& 0x0f) && entry
&& entry
->kve_type
!= KVME_TYPE_SWAP
;
4582 p
->VirtualAttributes
.Shared
= !is_view_valloc( view
);
4583 if (p
->VirtualAttributes
.Shared
&& p
->VirtualAttributes
.Valid
)
4584 p
->VirtualAttributes
.ShareCount
= 1; /* FIXME */
4585 if (p
->VirtualAttributes
.Valid
)
4586 p
->VirtualAttributes
.Win32Protection
= get_win32_prot( vprot
, view
->protect
);
4589 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4592 procstat_freevmmap( pstat
, vmentries
);
4594 procstat_freeprocs( pstat
, kip
);
4596 procstat_close( pstat
);
4599 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4600 if (pagemap_fd
== -2)
4603 if ((pagemap_fd
= open( "/proc/self/pagemap", O_RDONLY
| O_CLOEXEC
, 0 )) == -1 && errno
== EINVAL
)
4605 pagemap_fd
= open( "/proc/self/pagemap", O_RDONLY
, 0 );
4607 if (pagemap_fd
== -1) WARN( "unable to open /proc/self/pagemap\n" );
4608 else fcntl(pagemap_fd
, F_SETFD
, FD_CLOEXEC
); /* in case O_CLOEXEC isn't supported */
4611 for (p
= info
; (UINT_PTR
)(p
+ 1) <= (UINT_PTR
)info
+ len
; p
++)
4615 struct file_view
*view
;
4617 memset( &p
->VirtualAttributes
, 0, sizeof(p
->VirtualAttributes
) );
4619 if ((view
= find_view( p
->VirtualAddress
, 0 )) &&
4620 get_committed_size( view
, p
->VirtualAddress
, &vprot
, VPROT_COMMITTED
) &&
4621 (vprot
& VPROT_COMMITTED
))
4623 if (pagemap_fd
== -1 ||
4624 pread( pagemap_fd
, &pagemap
, sizeof(pagemap
), ((UINT_PTR
)p
->VirtualAddress
>> page_shift
) * sizeof(pagemap
) ) != sizeof(pagemap
))
4626 /* If we don't have pagemap information, default to invalid. */
4630 p
->VirtualAttributes
.Valid
= !(vprot
& VPROT_GUARD
) && (vprot
& 0x0f) && (pagemap
>> 63);
4631 p
->VirtualAttributes
.Shared
= !is_view_valloc( view
) && ((pagemap
>> 61) & 1);
4632 if (p
->VirtualAttributes
.Shared
&& p
->VirtualAttributes
.Valid
)
4633 p
->VirtualAttributes
.ShareCount
= 1; /* FIXME */
4634 if (p
->VirtualAttributes
.Valid
)
4635 p
->VirtualAttributes
.Win32Protection
= get_win32_prot( vprot
, view
->protect
);
4638 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4642 *res_len
= (UINT_PTR
)p
- (UINT_PTR
)info
;
4643 return STATUS_SUCCESS
;
4646 static unsigned int get_memory_section_name( HANDLE process
, LPCVOID addr
,
4647 MEMORY_SECTION_NAME
*info
, SIZE_T len
, SIZE_T
*ret_len
)
4649 unsigned int status
;
4651 if (!info
) return STATUS_ACCESS_VIOLATION
;
4653 SERVER_START_REQ( get_mapping_filename
)
4655 req
->process
= wine_server_obj_handle( process
);
4656 req
->addr
= wine_server_client_ptr( addr
);
4657 if (len
> sizeof(*info
) + sizeof(WCHAR
))
4658 wine_server_set_reply( req
, info
+ 1, len
- sizeof(*info
) - sizeof(WCHAR
) );
4659 status
= wine_server_call( req
);
4660 if (!status
|| status
== STATUS_BUFFER_OVERFLOW
)
4662 if (ret_len
) *ret_len
= sizeof(*info
) + reply
->len
+ sizeof(WCHAR
);
4663 if (len
< sizeof(*info
)) status
= STATUS_INFO_LENGTH_MISMATCH
;
4666 info
->SectionFileName
.Buffer
= (WCHAR
*)(info
+ 1);
4667 info
->SectionFileName
.Length
= reply
->len
;
4668 info
->SectionFileName
.MaximumLength
= reply
->len
+ sizeof(WCHAR
);
4669 info
->SectionFileName
.Buffer
[reply
->len
/ sizeof(WCHAR
)] = 0;
4678 /***********************************************************************
4679 * NtQueryVirtualMemory (NTDLL.@)
4680 * ZwQueryVirtualMemory (NTDLL.@)
4682 NTSTATUS WINAPI
NtQueryVirtualMemory( HANDLE process
, LPCVOID addr
,
4683 MEMORY_INFORMATION_CLASS info_class
,
4684 PVOID buffer
, SIZE_T len
, SIZE_T
*res_len
)
4688 TRACE("(%p, %p, info_class=%d, %p, %ld, %p)\n",
4689 process
, addr
, info_class
, buffer
, len
, res_len
);
4693 case MemoryBasicInformation
:
4694 return get_basic_memory_info( process
, addr
, buffer
, len
, res_len
);
4696 case MemoryWorkingSetExInformation
:
4697 return get_working_set_ex( process
, addr
, buffer
, len
, res_len
);
4699 case MemoryMappedFilenameInformation
:
4700 return get_memory_section_name( process
, addr
, buffer
, len
, res_len
);
4702 case MemoryRegionInformation
:
4703 return get_memory_region_info( process
, addr
, buffer
, len
, res_len
);
4705 case MemoryWineUnixFuncs
:
4706 case MemoryWineUnixWow64Funcs
:
4707 if (len
!= sizeof(unixlib_handle_t
)) return STATUS_INFO_LENGTH_MISMATCH
;
4708 if (process
== GetCurrentProcess())
4710 void *module
= (void *)addr
;
4711 const void *funcs
= NULL
;
4713 status
= get_builtin_unix_funcs( module
, info_class
== MemoryWineUnixWow64Funcs
, &funcs
);
4714 if (!status
) *(unixlib_handle_t
*)buffer
= (UINT_PTR
)funcs
;
4717 return STATUS_INVALID_HANDLE
;
4720 FIXME("(%p,%p,info_class=%d,%p,%ld,%p) Unknown information class\n",
4721 process
, addr
, info_class
, buffer
, len
, res_len
);
4722 return STATUS_INVALID_INFO_CLASS
;
4727 /***********************************************************************
4728 * NtLockVirtualMemory (NTDLL.@)
4729 * ZwLockVirtualMemory (NTDLL.@)
4731 NTSTATUS WINAPI
NtLockVirtualMemory( HANDLE process
, PVOID
*addr
, SIZE_T
*size
, ULONG unknown
)
4733 unsigned int status
= STATUS_SUCCESS
;
4735 if (process
!= NtCurrentProcess())
4738 apc_result_t result
;
4740 memset( &call
, 0, sizeof(call
) );
4742 call
.virtual_lock
.type
= APC_VIRTUAL_LOCK
;
4743 call
.virtual_lock
.addr
= wine_server_client_ptr( *addr
);
4744 call
.virtual_lock
.size
= *size
;
4745 status
= server_queue_process_apc( process
, &call
, &result
);
4746 if (status
!= STATUS_SUCCESS
) return status
;
4748 if (result
.virtual_lock
.status
== STATUS_SUCCESS
)
4750 *addr
= wine_server_get_ptr( result
.virtual_lock
.addr
);
4751 *size
= result
.virtual_lock
.size
;
4753 return result
.virtual_lock
.status
;
4756 *size
= ROUND_SIZE( *addr
, *size
);
4757 *addr
= ROUND_ADDR( *addr
, page_mask
);
4759 if (mlock( *addr
, *size
)) status
= STATUS_ACCESS_DENIED
;
4764 /***********************************************************************
4765 * NtUnlockVirtualMemory (NTDLL.@)
4766 * ZwUnlockVirtualMemory (NTDLL.@)
4768 NTSTATUS WINAPI
NtUnlockVirtualMemory( HANDLE process
, PVOID
*addr
, SIZE_T
*size
, ULONG unknown
)
4770 unsigned int status
= STATUS_SUCCESS
;
4772 if (process
!= NtCurrentProcess())
4775 apc_result_t result
;
4777 memset( &call
, 0, sizeof(call
) );
4779 call
.virtual_unlock
.type
= APC_VIRTUAL_UNLOCK
;
4780 call
.virtual_unlock
.addr
= wine_server_client_ptr( *addr
);
4781 call
.virtual_unlock
.size
= *size
;
4782 status
= server_queue_process_apc( process
, &call
, &result
);
4783 if (status
!= STATUS_SUCCESS
) return status
;
4785 if (result
.virtual_unlock
.status
== STATUS_SUCCESS
)
4787 *addr
= wine_server_get_ptr( result
.virtual_unlock
.addr
);
4788 *size
= result
.virtual_unlock
.size
;
4790 return result
.virtual_unlock
.status
;
4793 *size
= ROUND_SIZE( *addr
, *size
);
4794 *addr
= ROUND_ADDR( *addr
, page_mask
);
4796 if (munlock( *addr
, *size
)) status
= STATUS_ACCESS_DENIED
;
4801 /***********************************************************************
4802 * NtMapViewOfSection (NTDLL.@)
4803 * ZwMapViewOfSection (NTDLL.@)
4805 NTSTATUS WINAPI
NtMapViewOfSection( HANDLE handle
, HANDLE process
, PVOID
*addr_ptr
, ULONG_PTR zero_bits
,
4806 SIZE_T commit_size
, const LARGE_INTEGER
*offset_ptr
, SIZE_T
*size_ptr
,
4807 SECTION_INHERIT inherit
, ULONG alloc_type
, ULONG protect
)
4810 SIZE_T mask
= granularity_mask
;
4811 LARGE_INTEGER offset
;
4813 offset
.QuadPart
= offset_ptr
? offset_ptr
->QuadPart
: 0;
4815 TRACE("handle=%p process=%p addr=%p off=%s size=%lx access=%x\n",
4816 handle
, process
, *addr_ptr
, wine_dbgstr_longlong(offset
.QuadPart
), *size_ptr
, (int)protect
);
4818 /* Check parameters */
4819 if (zero_bits
> 21 && zero_bits
< 32)
4820 return STATUS_INVALID_PARAMETER_4
;
4822 /* If both addr_ptr and zero_bits are passed, they have match */
4823 if (*addr_ptr
&& zero_bits
&& zero_bits
< 32 &&
4824 (((UINT_PTR
)*addr_ptr
) >> (32 - zero_bits
)))
4825 return STATUS_INVALID_PARAMETER_4
;
4826 if (*addr_ptr
&& zero_bits
>= 32 &&
4827 (((UINT_PTR
)*addr_ptr
) & ~zero_bits
))
4828 return STATUS_INVALID_PARAMETER_4
;
4831 if (!is_old_wow64())
4833 if (zero_bits
>= 32) return STATUS_INVALID_PARAMETER_4
;
4834 if (alloc_type
& AT_ROUND_TO_PAGE
)
4836 *addr_ptr
= ROUND_ADDR( *addr_ptr
, page_mask
);
4842 if ((offset
.u
.LowPart
& mask
) || (*addr_ptr
&& ((UINT_PTR
)*addr_ptr
& mask
)))
4843 return STATUS_MAPPED_ALIGNMENT
;
4845 if (process
!= NtCurrentProcess())
4848 apc_result_t result
;
4850 memset( &call
, 0, sizeof(call
) );
4852 call
.map_view
.type
= APC_MAP_VIEW
;
4853 call
.map_view
.handle
= wine_server_obj_handle( handle
);
4854 call
.map_view
.addr
= wine_server_client_ptr( *addr_ptr
);
4855 call
.map_view
.size
= *size_ptr
;
4856 call
.map_view
.offset
= offset
.QuadPart
;
4857 call
.map_view
.zero_bits
= zero_bits
;
4858 call
.map_view
.alloc_type
= alloc_type
;
4859 call
.map_view
.prot
= protect
;
4860 res
= server_queue_process_apc( process
, &call
, &result
);
4861 if (res
!= STATUS_SUCCESS
) return res
;
4863 if (NT_SUCCESS(result
.map_view
.status
))
4865 *addr_ptr
= wine_server_get_ptr( result
.map_view
.addr
);
4866 *size_ptr
= result
.map_view
.size
;
4868 return result
.map_view
.status
;
4871 return virtual_map_section( handle
, addr_ptr
, zero_bits
, commit_size
,
4872 offset_ptr
, size_ptr
, alloc_type
, protect
);
4875 /***********************************************************************
4876 * NtMapViewOfSectionEx (NTDLL.@)
4877 * ZwMapViewOfSectionEx (NTDLL.@)
4879 NTSTATUS WINAPI
NtMapViewOfSectionEx( HANDLE handle
, HANDLE process
, PVOID
*addr_ptr
, const LARGE_INTEGER
*offset_ptr
,
4880 SIZE_T
*size_ptr
, ULONG alloc_type
, ULONG protect
, MEM_EXTENDED_PARAMETER
*params
, ULONG params_count
)
4883 FIXME("Ignoring extended parameters.\n");
4885 return NtMapViewOfSection( handle
, process
, addr_ptr
, 0, 0, offset_ptr
, size_ptr
, ViewShare
, alloc_type
, protect
);
4888 /***********************************************************************
4889 * NtUnmapViewOfSection (NTDLL.@)
4890 * ZwUnmapViewOfSection (NTDLL.@)
4892 NTSTATUS WINAPI
NtUnmapViewOfSection( HANDLE process
, PVOID addr
)
4894 struct file_view
*view
;
4895 unsigned int status
= STATUS_NOT_MAPPED_VIEW
;
4898 if (process
!= NtCurrentProcess())
4901 apc_result_t result
;
4903 memset( &call
, 0, sizeof(call
) );
4905 call
.unmap_view
.type
= APC_UNMAP_VIEW
;
4906 call
.unmap_view
.addr
= wine_server_client_ptr( addr
);
4907 status
= server_queue_process_apc( process
, &call
, &result
);
4908 if (status
== STATUS_SUCCESS
) status
= result
.unmap_view
.status
;
4912 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
4913 if ((view
= find_view( addr
, 0 )) && !is_view_valloc( view
))
4915 if (view
->protect
& VPROT_SYSTEM
)
4917 struct builtin_module
*builtin
;
4919 LIST_FOR_EACH_ENTRY( builtin
, &builtin_modules
, struct builtin_module
, entry
)
4921 if (builtin
->module
!= view
->base
) continue;
4922 if (builtin
->refcount
> 1)
4924 TRACE( "not freeing in-use builtin %p\n", view
->base
);
4925 builtin
->refcount
--;
4926 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4927 return STATUS_SUCCESS
;
4932 SERVER_START_REQ( unmap_view
)
4934 req
->base
= wine_server_client_ptr( view
->base
);
4935 status
= wine_server_call( req
);
4940 if (view
->protect
& SEC_IMAGE
) release_builtin_module( view
->base
);
4941 delete_view( view
);
4943 else FIXME( "failed to unmap %p %x\n", view
->base
, status
);
4945 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
4949 /***********************************************************************
4950 * NtUnmapViewOfSectionEx (NTDLL.@)
4951 * ZwUnmapViewOfSectionEx (NTDLL.@)
4953 NTSTATUS WINAPI
NtUnmapViewOfSectionEx( HANDLE process
, PVOID addr
, ULONG flags
)
4955 if (flags
) FIXME("Ignoring flags %#x.\n", (int)flags
);
4956 return NtUnmapViewOfSection( process
, addr
);
4959 /******************************************************************************
4960 * virtual_fill_image_information
4962 * Helper for NtQuerySection.
4964 void virtual_fill_image_information( const pe_image_info_t
*pe_info
, SECTION_IMAGE_INFORMATION
*info
)
4966 info
->TransferAddress
= wine_server_get_ptr( pe_info
->base
+ pe_info
->entry_point
);
4967 info
->ZeroBits
= pe_info
->zerobits
;
4968 info
->MaximumStackSize
= pe_info
->stack_size
;
4969 info
->CommittedStackSize
= pe_info
->stack_commit
;
4970 info
->SubSystemType
= pe_info
->subsystem
;
4971 info
->MinorSubsystemVersion
= pe_info
->subsystem_minor
;
4972 info
->MajorSubsystemVersion
= pe_info
->subsystem_major
;
4973 info
->MajorOperatingSystemVersion
= pe_info
->osversion_major
;
4974 info
->MinorOperatingSystemVersion
= pe_info
->osversion_minor
;
4975 info
->ImageCharacteristics
= pe_info
->image_charact
;
4976 info
->DllCharacteristics
= pe_info
->dll_charact
;
4977 info
->Machine
= pe_info
->machine
;
4978 info
->ImageContainsCode
= pe_info
->contains_code
;
4979 info
->ImageFlags
= pe_info
->image_flags
;
4980 info
->LoaderFlags
= pe_info
->loader_flags
;
4981 info
->ImageFileSize
= pe_info
->file_size
;
4982 info
->CheckSum
= pe_info
->checksum
;
4983 #ifndef _WIN64 /* don't return 64-bit values to 32-bit processes */
4984 if (is_machine_64bit( pe_info
->machine
))
4986 info
->TransferAddress
= (void *)0x81231234; /* sic */
4987 info
->MaximumStackSize
= 0x100000;
4988 info
->CommittedStackSize
= 0x10000;
4993 /******************************************************************************
4994 * NtQuerySection (NTDLL.@)
4995 * ZwQuerySection (NTDLL.@)
4997 NTSTATUS WINAPI
NtQuerySection( HANDLE handle
, SECTION_INFORMATION_CLASS
class, void *ptr
,
4998 SIZE_T size
, SIZE_T
*ret_size
)
5000 unsigned int status
;
5001 pe_image_info_t image_info
;
5005 case SectionBasicInformation
:
5006 if (size
< sizeof(SECTION_BASIC_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
5008 case SectionImageInformation
:
5009 if (size
< sizeof(SECTION_IMAGE_INFORMATION
)) return STATUS_INFO_LENGTH_MISMATCH
;
5012 FIXME( "class %u not implemented\n", class );
5013 return STATUS_NOT_IMPLEMENTED
;
5015 if (!ptr
) return STATUS_ACCESS_VIOLATION
;
5017 SERVER_START_REQ( get_mapping_info
)
5019 req
->handle
= wine_server_obj_handle( handle
);
5020 req
->access
= SECTION_QUERY
;
5021 wine_server_set_reply( req
, &image_info
, sizeof(image_info
) );
5022 if (!(status
= wine_server_call( req
)))
5024 if (class == SectionBasicInformation
)
5026 SECTION_BASIC_INFORMATION
*info
= ptr
;
5027 info
->Attributes
= reply
->flags
;
5028 info
->BaseAddress
= NULL
;
5029 info
->Size
.QuadPart
= reply
->size
;
5030 if (ret_size
) *ret_size
= sizeof(*info
);
5032 else if (reply
->flags
& SEC_IMAGE
)
5034 SECTION_IMAGE_INFORMATION
*info
= ptr
;
5035 virtual_fill_image_information( &image_info
, info
);
5036 if (ret_size
) *ret_size
= sizeof(*info
);
5038 else status
= STATUS_SECTION_NOT_IMAGE
;
5047 /***********************************************************************
5048 * NtFlushVirtualMemory (NTDLL.@)
5049 * ZwFlushVirtualMemory (NTDLL.@)
5051 NTSTATUS WINAPI
NtFlushVirtualMemory( HANDLE process
, LPCVOID
*addr_ptr
,
5052 SIZE_T
*size_ptr
, ULONG unknown
)
5054 struct file_view
*view
;
5055 unsigned int status
= STATUS_SUCCESS
;
5057 void *addr
= ROUND_ADDR( *addr_ptr
, page_mask
);
5059 if (process
!= NtCurrentProcess())
5062 apc_result_t result
;
5064 memset( &call
, 0, sizeof(call
) );
5066 call
.virtual_flush
.type
= APC_VIRTUAL_FLUSH
;
5067 call
.virtual_flush
.addr
= wine_server_client_ptr( addr
);
5068 call
.virtual_flush
.size
= *size_ptr
;
5069 status
= server_queue_process_apc( process
, &call
, &result
);
5070 if (status
!= STATUS_SUCCESS
) return status
;
5072 if (result
.virtual_flush
.status
== STATUS_SUCCESS
)
5074 *addr_ptr
= wine_server_get_ptr( result
.virtual_flush
.addr
);
5075 *size_ptr
= result
.virtual_flush
.size
;
5077 return result
.virtual_flush
.status
;
5080 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
5081 if (!(view
= find_view( addr
, *size_ptr
))) status
= STATUS_INVALID_PARAMETER
;
5084 if (!*size_ptr
) *size_ptr
= view
->size
;
5087 if (msync( addr
, *size_ptr
, MS_ASYNC
)) status
= STATUS_NOT_MAPPED_DATA
;
5090 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
5095 /***********************************************************************
5096 * NtGetWriteWatch (NTDLL.@)
5097 * ZwGetWriteWatch (NTDLL.@)
5099 NTSTATUS WINAPI
NtGetWriteWatch( HANDLE process
, ULONG flags
, PVOID base
, SIZE_T size
, PVOID
*addresses
,
5100 ULONG_PTR
*count
, ULONG
*granularity
)
5102 NTSTATUS status
= STATUS_SUCCESS
;
5105 size
= ROUND_SIZE( base
, size
);
5106 base
= ROUND_ADDR( base
, page_mask
);
5108 if (!count
|| !granularity
) return STATUS_ACCESS_VIOLATION
;
5109 if (!*count
|| !size
) return STATUS_INVALID_PARAMETER
;
5110 if (flags
& ~WRITE_WATCH_FLAG_RESET
) return STATUS_INVALID_PARAMETER
;
5112 if (!addresses
) return STATUS_ACCESS_VIOLATION
;
5114 TRACE( "%p %x %p-%p %p %lu\n", process
, (int)flags
, base
, (char *)base
+ size
,
5115 addresses
, *count
);
5117 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
5119 if (is_write_watch_range( base
, size
))
5123 char *end
= addr
+ size
;
5125 while (pos
< *count
&& addr
< end
)
5127 if (!(get_page_vprot( addr
) & VPROT_WRITEWATCH
)) addresses
[pos
++] = addr
;
5130 if (flags
& WRITE_WATCH_FLAG_RESET
) reset_write_watches( base
, addr
- (char *)base
);
5132 *granularity
= page_size
;
5134 else status
= STATUS_INVALID_PARAMETER
;
5136 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
5141 /***********************************************************************
5142 * NtResetWriteWatch (NTDLL.@)
5143 * ZwResetWriteWatch (NTDLL.@)
5145 NTSTATUS WINAPI
NtResetWriteWatch( HANDLE process
, PVOID base
, SIZE_T size
)
5147 NTSTATUS status
= STATUS_SUCCESS
;
5150 size
= ROUND_SIZE( base
, size
);
5151 base
= ROUND_ADDR( base
, page_mask
);
5153 TRACE( "%p %p-%p\n", process
, base
, (char *)base
+ size
);
5155 if (!size
) return STATUS_INVALID_PARAMETER
;
5157 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
5159 if (is_write_watch_range( base
, size
))
5160 reset_write_watches( base
, size
);
5162 status
= STATUS_INVALID_PARAMETER
;
5164 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
5169 /***********************************************************************
5170 * NtReadVirtualMemory (NTDLL.@)
5171 * ZwReadVirtualMemory (NTDLL.@)
5173 NTSTATUS WINAPI
NtReadVirtualMemory( HANDLE process
, const void *addr
, void *buffer
,
5174 SIZE_T size
, SIZE_T
*bytes_read
)
5176 unsigned int status
;
5178 if (virtual_check_buffer_for_write( buffer
, size
))
5180 SERVER_START_REQ( read_process_memory
)
5182 req
->handle
= wine_server_obj_handle( process
);
5183 req
->addr
= wine_server_client_ptr( addr
);
5184 wine_server_set_reply( req
, buffer
, size
);
5185 if ((status
= wine_server_call( req
))) size
= 0;
5191 status
= STATUS_ACCESS_VIOLATION
;
5194 if (bytes_read
) *bytes_read
= size
;
5199 /***********************************************************************
5200 * NtWriteVirtualMemory (NTDLL.@)
5201 * ZwWriteVirtualMemory (NTDLL.@)
5203 NTSTATUS WINAPI
NtWriteVirtualMemory( HANDLE process
, void *addr
, const void *buffer
,
5204 SIZE_T size
, SIZE_T
*bytes_written
)
5206 unsigned int status
;
5208 if (virtual_check_buffer_for_read( buffer
, size
))
5210 SERVER_START_REQ( write_process_memory
)
5212 req
->handle
= wine_server_obj_handle( process
);
5213 req
->addr
= wine_server_client_ptr( addr
);
5214 wine_server_add_data( req
, buffer
, size
);
5215 if ((status
= wine_server_call( req
))) size
= 0;
5221 status
= STATUS_PARTIAL_COPY
;
5224 if (bytes_written
) *bytes_written
= size
;
5229 /***********************************************************************
5230 * NtAreMappedFilesTheSame (NTDLL.@)
5231 * ZwAreMappedFilesTheSame (NTDLL.@)
5233 NTSTATUS WINAPI
NtAreMappedFilesTheSame(PVOID addr1
, PVOID addr2
)
5235 struct file_view
*view1
, *view2
;
5236 unsigned int status
;
5239 TRACE("%p %p\n", addr1
, addr2
);
5241 server_enter_uninterrupted_section( &virtual_mutex
, &sigset
);
5243 view1
= find_view( addr1
, 0 );
5244 view2
= find_view( addr2
, 0 );
5246 if (!view1
|| !view2
)
5247 status
= STATUS_INVALID_ADDRESS
;
5248 else if (is_view_valloc( view1
) || is_view_valloc( view2
))
5249 status
= STATUS_CONFLICTING_ADDRESSES
;
5250 else if (view1
== view2
)
5251 status
= STATUS_SUCCESS
;
5252 else if ((view1
->protect
& VPROT_SYSTEM
) || (view2
->protect
& VPROT_SYSTEM
))
5253 status
= STATUS_NOT_SAME_DEVICE
;
5256 SERVER_START_REQ( is_same_mapping
)
5258 req
->base1
= wine_server_client_ptr( view1
->base
);
5259 req
->base2
= wine_server_client_ptr( view2
->base
);
5260 status
= wine_server_call( req
);
5265 server_leave_uninterrupted_section( &virtual_mutex
, &sigset
);
5270 static NTSTATUS
prefetch_memory( HANDLE process
, ULONG_PTR count
,
5271 PMEMORY_RANGE_ENTRY addresses
, ULONG flags
)
5276 static unsigned int once
;
5280 FIXME( "(process=%p,flags=%u) NtSetInformationVirtualMemory(VmPrefetchInformation) partial stub\n",
5281 process
, (int)flags
);
5284 for (i
= 0; i
< count
; i
++)
5286 if (!addresses
[i
].NumberOfBytes
) return STATUS_INVALID_PARAMETER_4
;
5289 if (process
!= NtCurrentProcess()) return STATUS_SUCCESS
;
5291 for (i
= 0; i
< count
; i
++)
5293 base
= ROUND_ADDR( addresses
[i
].VirtualAddress
, page_mask
);
5294 size
= ROUND_SIZE( addresses
[i
].VirtualAddress
, addresses
[i
].NumberOfBytes
);
5295 madvise( base
, size
, MADV_WILLNEED
);
5298 return STATUS_SUCCESS
;
5301 /***********************************************************************
5302 * NtSetInformationVirtualMemory (NTDLL.@)
5303 * ZwSetInformationVirtualMemory (NTDLL.@)
5305 NTSTATUS WINAPI
NtSetInformationVirtualMemory( HANDLE process
,
5306 VIRTUAL_MEMORY_INFORMATION_CLASS info_class
,
5307 ULONG_PTR count
, PMEMORY_RANGE_ENTRY addresses
,
5308 PVOID ptr
, ULONG size
)
5310 TRACE("(%p, info_class=%d, %lu, %p, %p, %u)\n",
5311 process
, info_class
, count
, addresses
, ptr
, (int)size
);
5315 case VmPrefetchInformation
:
5316 if (!ptr
) return STATUS_INVALID_PARAMETER_5
;
5317 if (size
!= sizeof(ULONG
)) return STATUS_INVALID_PARAMETER_6
;
5318 if (!count
) return STATUS_INVALID_PARAMETER_3
;
5319 return prefetch_memory( process
, count
, addresses
, *(ULONG
*)ptr
);
5322 FIXME("(%p,info_class=%d,%lu,%p,%p,%u) Unknown information class\n",
5323 process
, info_class
, count
, addresses
, ptr
, (int)size
);
5324 return STATUS_INVALID_PARAMETER_2
;
5329 /**********************************************************************
5330 * NtFlushInstructionCache (NTDLL.@)
5332 NTSTATUS WINAPI
NtFlushInstructionCache( HANDLE handle
, const void *addr
, SIZE_T size
)
5334 #if defined(__x86_64__) || defined(__i386__)
5336 #elif defined(HAVE___CLEAR_CACHE)
5337 if (handle
== GetCurrentProcess())
5339 __clear_cache( (char *)addr
, (char *)addr
+ size
);
5344 if (!once
++) FIXME( "%p %p %ld other process not supported\n", handle
, addr
, size
);
5348 if (!once
++) FIXME( "%p %p %ld\n", handle
, addr
, size
);
5350 return STATUS_SUCCESS
;
5354 /**********************************************************************
5355 * NtFlushProcessWriteBuffers (NTDLL.@)
5357 void WINAPI
NtFlushProcessWriteBuffers(void)
5359 static int once
= 0;
5360 if (!once
++) FIXME( "stub\n" );
5364 /**********************************************************************
5365 * NtCreatePagingFile (NTDLL.@)
5367 NTSTATUS WINAPI
NtCreatePagingFile( UNICODE_STRING
*name
, LARGE_INTEGER
*min_size
,
5368 LARGE_INTEGER
*max_size
, LARGE_INTEGER
*actual_size
)
5370 FIXME( "(%s %p %p %p) stub\n", debugstr_us(name
), min_size
, max_size
, actual_size
);
5371 return STATUS_SUCCESS
;
5376 /***********************************************************************
5377 * NtWow64AllocateVirtualMemory64 (NTDLL.@)
5378 * ZwWow64AllocateVirtualMemory64 (NTDLL.@)
5380 NTSTATUS WINAPI
NtWow64AllocateVirtualMemory64( HANDLE process
, ULONG64
*ret
, ULONG64 zero_bits
,
5381 ULONG64
*size_ptr
, ULONG type
, ULONG protect
)
5385 unsigned int status
;
5387 TRACE("%p %s %s %x %08x\n", process
,
5388 wine_dbgstr_longlong(*ret
), wine_dbgstr_longlong(*size_ptr
), (int)type
, (int)protect
);
5390 if (!*size_ptr
) return STATUS_INVALID_PARAMETER_4
;
5391 if (zero_bits
> 21 && zero_bits
< 32) return STATUS_INVALID_PARAMETER_3
;
5393 if (process
!= NtCurrentProcess())
5396 apc_result_t result
;
5398 memset( &call
, 0, sizeof(call
) );
5400 call
.virtual_alloc
.type
= APC_VIRTUAL_ALLOC
;
5401 call
.virtual_alloc
.addr
= *ret
;
5402 call
.virtual_alloc
.size
= *size_ptr
;
5403 call
.virtual_alloc
.zero_bits
= zero_bits
;
5404 call
.virtual_alloc
.op_type
= type
;
5405 call
.virtual_alloc
.prot
= protect
;
5406 status
= server_queue_process_apc( process
, &call
, &result
);
5407 if (status
!= STATUS_SUCCESS
) return status
;
5409 if (result
.virtual_alloc
.status
== STATUS_SUCCESS
)
5411 *ret
= result
.virtual_alloc
.addr
;
5412 *size_ptr
= result
.virtual_alloc
.size
;
5414 return result
.virtual_alloc
.status
;
5417 base
= (void *)(ULONG_PTR
)*ret
;
5419 if ((ULONG_PTR
)base
!= *ret
) return STATUS_CONFLICTING_ADDRESSES
;
5420 if (size
!= *size_ptr
) return STATUS_WORKING_SET_LIMIT_RANGE
;
5422 status
= NtAllocateVirtualMemory( process
, &base
, zero_bits
, &size
, type
, protect
);
5425 *ret
= (ULONG_PTR
)base
;
5432 /***********************************************************************
5433 * NtWow64ReadVirtualMemory64 (NTDLL.@)
5434 * ZwWow64ReadVirtualMemory64 (NTDLL.@)
5436 NTSTATUS WINAPI
NtWow64ReadVirtualMemory64( HANDLE process
, ULONG64 addr
, void *buffer
,
5437 ULONG64 size
, ULONG64
*bytes_read
)
5439 unsigned int status
;
5441 if (size
> MAXLONG
) size
= MAXLONG
;
5443 if (virtual_check_buffer_for_write( buffer
, size
))
5445 SERVER_START_REQ( read_process_memory
)
5447 req
->handle
= wine_server_obj_handle( process
);
5449 wine_server_set_reply( req
, buffer
, size
);
5450 if ((status
= wine_server_call( req
))) size
= 0;
5456 status
= STATUS_ACCESS_VIOLATION
;
5459 if (bytes_read
) *bytes_read
= size
;
5464 /***********************************************************************
5465 * NtWow64WriteVirtualMemory64 (NTDLL.@)
5466 * ZwWow64WriteVirtualMemory64 (NTDLL.@)
5468 NTSTATUS WINAPI
NtWow64WriteVirtualMemory64( HANDLE process
, ULONG64 addr
, const void *buffer
,
5469 ULONG64 size
, ULONG64
*bytes_written
)
5471 unsigned int status
;
5473 if (size
> MAXLONG
) size
= MAXLONG
;
5475 if (virtual_check_buffer_for_read( buffer
, size
))
5477 SERVER_START_REQ( write_process_memory
)
5479 req
->handle
= wine_server_obj_handle( process
);
5481 wine_server_add_data( req
, buffer
, size
);
5482 if ((status
= wine_server_call( req
))) size
= 0;
5488 status
= STATUS_PARTIAL_COPY
;
5491 if (bytes_written
) *bytes_written
= size
;
5496 /***********************************************************************
5497 * NtWow64GetNativeSystemInformation (NTDLL.@)
5498 * ZwWow64GetNativeSystemInformation (NTDLL.@)
5500 NTSTATUS WINAPI
NtWow64GetNativeSystemInformation( SYSTEM_INFORMATION_CLASS
class, void *info
,
5501 ULONG len
, ULONG
*retlen
)
5507 case SystemCpuInformation
:
5508 status
= NtQuerySystemInformation( class, info
, len
, retlen
);
5509 if (!status
&& is_old_wow64())
5511 SYSTEM_CPU_INFORMATION
*cpu
= info
;
5513 if (cpu
->ProcessorArchitecture
== PROCESSOR_ARCHITECTURE_INTEL
)
5514 cpu
->ProcessorArchitecture
= PROCESSOR_ARCHITECTURE_AMD64
;
5517 case SystemBasicInformation
:
5518 case SystemEmulationBasicInformation
:
5519 case SystemEmulationProcessorInformation
:
5520 return NtQuerySystemInformation( class, info
, len
, retlen
);
5521 case SystemNativeBasicInformation
:
5522 return NtQuerySystemInformation( SystemBasicInformation
, info
, len
, retlen
);
5524 if (is_old_wow64()) return STATUS_INVALID_INFO_CLASS
;
5525 return NtQuerySystemInformation( class, info
, len
, retlen
);
5529 /***********************************************************************
5530 * NtWow64IsProcessorFeaturePresent (NTDLL.@)
5531 * ZwWow64IsProcessorFeaturePresent (NTDLL.@)
5533 NTSTATUS WINAPI
NtWow64IsProcessorFeaturePresent( UINT feature
)
5535 return feature
< PROCESSOR_FEATURE_MAX
&& user_shared_data
->ProcessorFeatures
[feature
];