From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/virtual.c | 12 +++--- dlls/ntdll/unix/server.c | 7 +++- dlls/ntdll/unix/virtual.c | 82 ++++++++++++++++++++++++-------------- server/protocol.def | 1 + server/trace.c | 1 + 5 files changed, 64 insertions(+), 39 deletions(-)
diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c index d6e975772f5..daa1257fb96 100644 --- a/dlls/ntdll/tests/virtual.c +++ b/dlls/ntdll/tests/virtual.c @@ -586,7 +586,7 @@ static void test_NtAllocateVirtualMemoryEx_address_requirements(void) addr = NULL; status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, PAGE_EXECUTE_READWRITE, ext, 1); - todo_wine ok(status == STATUS_NO_MEMORY, "Unexpected status %08lx.\n", status); + ok(status == STATUS_NO_MEMORY, "Unexpected status %08lx.\n", status);
a.HighestEndingAddress = NULL; a.Alignment = 0x8000; @@ -594,14 +594,14 @@ static void test_NtAllocateVirtualMemoryEx_address_requirements(void) addr = NULL; status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, PAGE_EXECUTE_READWRITE, ext, 1); - todo_wine ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status);
a.Alignment = 0x30000; size = 0x1000; addr = NULL; status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, PAGE_EXECUTE_READWRITE, ext, 1); - todo_wine ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status);
a.Alignment = 0x40000; size = 0x1000; @@ -609,10 +609,10 @@ static void test_NtAllocateVirtualMemoryEx_address_requirements(void) status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, PAGE_EXECUTE_READWRITE, ext, 1); ok(!status, "Unexpected status %08lx.\n", status); - todo_wine ok(!((ULONG_PTR)addr & 0x3ffff), "Unexpected addr %p.\n", addr); + ok(!((ULONG_PTR)addr & 0x3ffff), "Unexpected addr %p.\n", addr); status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE, ext, 1); - todo_wine ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status);
size = 0; status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); @@ -620,7 +620,7 @@ static void test_NtAllocateVirtualMemoryEx_address_requirements(void)
status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, PAGE_EXECUTE_READWRITE, ext, 1); - todo_wine ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); }
struct test_stack_size_thread_args diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index b7d8733f2bc..07d9c8d3acd 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -358,7 +358,7 @@ static NTSTATUS invoke_user_apc( CONTEXT *context, const user_apc_t *apc, NTSTAT */ static void invoke_system_apc( const apc_call_t *call, apc_result_t *result, BOOL self ) { - SIZE_T size, bits, limit; + SIZE_T size, bits, limit, align; void *addr;
memset( result, 0, sizeof(*result) ); @@ -416,9 +416,12 @@ static void invoke_system_apc( const apc_call_t *call, apc_result_t *result, BOO addr = wine_server_get_ptr( call->virtual_alloc_ex.addr ); size = call->virtual_alloc_ex.size; limit = min( (ULONG_PTR)sbi.HighestUserAddress, call->virtual_alloc_ex.limit ); - if ((ULONG_PTR)addr == call->virtual_alloc_ex.addr && size == call->virtual_alloc_ex.size) + align = call->virtual_alloc_ex.align; + if ((ULONG_PTR)addr == call->virtual_alloc_ex.addr && size == call->virtual_alloc_ex.size + && align == call->virtual_alloc_ex.align) { r.HighestEndingAddress = (void *)limit; + r.Alignment = align; result->virtual_alloc_ex.status = NtAllocateVirtualMemoryEx( NtCurrentProcess(), &addr, &size, call->virtual_alloc_ex.op_type, call->virtual_alloc_ex.prot, &ext, 1 ); diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 7b2b019c345..8dcdaa10494 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -1274,15 +1274,15 @@ static void* try_map_free_area( void *base, void *end, ptrdiff_t step, * Find a free area between views inside the specified range and map it. * virtual_mutex must be held by caller. */ -static void *map_free_area( void *base, void *end, size_t size, int top_down, int unix_prot ) +static void *map_free_area( void *base, void *end, size_t size, int top_down, int unix_prot, size_t align_mask ) { struct wine_rb_entry *first = find_view_inside_range( &base, &end, top_down ); - ptrdiff_t step = top_down ? -(granularity_mask + 1) : (granularity_mask + 1); + ptrdiff_t step = top_down ? -(align_mask + 1) : (align_mask + 1); void *start;
if (top_down) { - start = ROUND_ADDR( (char *)end - size, granularity_mask ); + start = ROUND_ADDR( (char *)end - size, align_mask ); if (start >= end || start < base) return NULL;
while (first) @@ -1290,7 +1290,7 @@ static void *map_free_area( void *base, void *end, size_t size, int top_down, in struct file_view *view = WINE_RB_ENTRY_VALUE( first, struct file_view, entry ); if ((start = try_map_free_area( (char *)view->base + view->size, (char *)start + size, step, start, size, unix_prot ))) break; - start = ROUND_ADDR( (char *)view->base - size, granularity_mask ); + start = ROUND_ADDR( (char *)view->base - size, align_mask ); /* stop if remaining space is not large enough */ if (!start || start >= end || start < base) return NULL; first = rb_prev( first ); @@ -1298,7 +1298,7 @@ static void *map_free_area( void *base, void *end, size_t size, int top_down, in } else { - start = ROUND_ADDR( (char *)base + granularity_mask, granularity_mask ); + start = ROUND_ADDR( (char *)base + align_mask, align_mask ); if (!start || start >= end || (char *)end - (char *)start < size) return NULL;
while (first) @@ -1306,7 +1306,7 @@ static void *map_free_area( void *base, void *end, size_t size, int top_down, in struct file_view *view = WINE_RB_ENTRY_VALUE( first, struct file_view, entry ); if ((start = try_map_free_area( start, view->base, step, start, size, unix_prot ))) break; - start = ROUND_ADDR( (char *)view->base + view->size + granularity_mask, granularity_mask ); + start = ROUND_ADDR( (char *)view->base + view->size + align_mask, align_mask ); /* stop if remaining space is not large enough */ if (!start || start >= end || (char *)end - (char *)start < size) return NULL; first = rb_next( first ); @@ -1327,13 +1327,13 @@ static void *map_free_area( void *base, void *end, size_t size, int top_down, in * virtual_mutex must be held by caller. * The range must be inside the preloader reserved range. */ -static void *find_reserved_free_area( void *base, void *end, size_t size, int top_down ) +static void *find_reserved_free_area( void *base, void *end, size_t size, int top_down, size_t align_mask ) { struct range_entry *range; void *start;
- base = ROUND_ADDR( (char *)base + granularity_mask, granularity_mask ); - end = (char *)ROUND_ADDR( (char *)end - size, granularity_mask ) + size; + base = ROUND_ADDR( (char *)base + align_mask, align_mask ); + end = (char *)ROUND_ADDR( (char *)end - size, align_mask ) + size;
if (top_down) { @@ -1341,13 +1341,13 @@ static void *find_reserved_free_area( void *base, void *end, size_t size, int to range = free_ranges_lower_bound( start ); assert(range != free_ranges_end && range->end >= start);
- if ((char *)range->end - (char *)start < size) start = ROUND_ADDR( (char *)range->end - size, granularity_mask ); + if ((char *)range->end - (char *)start < size) start = ROUND_ADDR( (char *)range->end - size, align_mask ); do { if (start >= end || start < base || (char *)end - (char *)start < size) return NULL; if (start < range->end && start >= range->base && (char *)range->end - (char *)start >= size) break; if (--range < free_ranges) return NULL; - start = ROUND_ADDR( (char *)range->end - size, granularity_mask ); + start = ROUND_ADDR( (char *)range->end - size, align_mask ); } while (1); } @@ -1357,13 +1357,13 @@ static void *find_reserved_free_area( void *base, void *end, size_t size, int to range = free_ranges_lower_bound( start ); assert(range != free_ranges_end && range->end >= start);
- if (start < range->base) start = ROUND_ADDR( (char *)range->base + granularity_mask, granularity_mask ); + if (start < range->base) start = ROUND_ADDR( (char *)range->base + align_mask, align_mask ); do { if (start >= end || start < base || (char *)end - (char *)start < size) return NULL; if (start < range->end && start >= range->base && (char *)range->end - (char *)start >= size) break; if (++range == free_ranges_end) return NULL; - start = ROUND_ADDR( (char *)range->base + granularity_mask, granularity_mask ); + start = ROUND_ADDR( (char *)range->base + align_mask, align_mask ); } while (1); } @@ -1773,13 +1773,13 @@ static void reset_write_watches( void *base, SIZE_T size ) /*********************************************************************** * unmap_extra_space * - * Release the extra memory while keeping the range starting on the granularity boundary. + * Release the extra memory while keeping the range starting on the alignment boundary. */ -static inline void *unmap_extra_space( void *ptr, size_t total_size, size_t wanted_size ) +static inline void *unmap_extra_space( void *ptr, size_t total_size, size_t wanted_size, size_t align_mask ) { - if ((ULONG_PTR)ptr & granularity_mask) + if ((ULONG_PTR)ptr & align_mask) { - size_t extra = granularity_mask + 1 - ((ULONG_PTR)ptr & granularity_mask); + size_t extra = align_mask + 1 - ((ULONG_PTR)ptr & align_mask); munmap( ptr, extra ); ptr = (char *)ptr + extra; total_size -= extra; @@ -1796,6 +1796,7 @@ struct alloc_area int top_down; void *limit; void *result; + size_t align_mask; };
/*********************************************************************** @@ -1825,13 +1826,13 @@ static int alloc_reserved_area_callback( void *start, SIZE_T size, void *arg ) { /* range is split in two by the preloader reservation, try first part */ if ((alloc->result = find_reserved_free_area( start, preload_reserve_start, alloc->size, - alloc->top_down ))) + alloc->top_down, alloc->align_mask ))) return 1; /* then fall through to try second part */ start = preload_reserve_end; } } - if ((alloc->result = find_reserved_free_area( start, end, alloc->size, alloc->top_down ))) + if ((alloc->result = find_reserved_free_area( start, end, alloc->size, alloc->top_down, alloc->align_mask ))) return 1;
return 0; @@ -1895,11 +1896,13 @@ static NTSTATUS map_fixed_area( void *base, size_t size, unsigned int vprot ) * virtual_mutex must be held by caller. */ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, - int top_down, unsigned int vprot, ULONG_PTR limit ) + int top_down, unsigned int vprot, ULONG_PTR limit, size_t align_mask ) { void *ptr; NTSTATUS status;
+ TRACE("align_mask %#zx.\n", align_mask); + if (base) { if (is_beyond_limit( base, size, address_space_limit )) @@ -1910,12 +1913,16 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, } else { - size_t view_size = size + granularity_mask + 1; struct alloc_area alloc; + size_t view_size; + + if (!align_mask) align_mask = granularity_mask; + view_size = size + align_mask + 1;
alloc.size = size; alloc.top_down = top_down; alloc.limit = limit ? min( (void *)(limit + 1), user_space_limit ) : user_space_limit; + alloc.align_mask = align_mask;
if (mmap_enum_reserved_areas( alloc_reserved_area_callback, &alloc, top_down )) { @@ -1929,7 +1936,7 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, if (limit) { if (!(ptr = map_free_area( address_space_start, alloc.limit, size, - top_down, get_unix_prot(vprot) ))) + top_down, get_unix_prot(vprot), align_mask ))) return STATUS_NO_MEMORY; TRACE( "got mem with map_free_area %p-%p\n", ptr, (char *)ptr + size ); goto done; @@ -1947,7 +1954,7 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, if (is_beyond_limit( ptr, view_size, user_space_limit )) add_reserved_area( ptr, view_size ); else break; } - ptr = unmap_extra_space( ptr, view_size, size ); + ptr = unmap_extra_space( ptr, view_size, size, align_mask ); } done: status = create_view( view_ret, ptr, size, vprot ); @@ -2107,7 +2114,7 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot if (mmap_is_in_reserved_area( low_64k, dosmem_size - 0x10000 ) != 1) { addr = anon_mmap_tryfixed( low_64k, dosmem_size - 0x10000, unix_prot, 0 ); - if (addr == MAP_FAILED) return map_view( view, NULL, dosmem_size, FALSE, vprot, 0 ); + if (addr == MAP_FAILED) return map_view( view, NULL, dosmem_size, FALSE, vprot, 0, 0 ); }
/* now try to allocate the low 64K too */ @@ -2451,9 +2458,9 @@ static NTSTATUS virtual_map_image( HANDLE mapping, ACCESS_MASK access, void **ad if ((ULONG_PTR)base != image_info->base) base = NULL;
if ((char *)base >= (char *)address_space_start) /* make sure the DOS area remains free */ - status = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits ) ); + status = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits ), 0 );
- if (status) status = map_view( &view, NULL, size, alloc_type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits )); + if (status) status = map_view( &view, NULL, size, alloc_type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits ), 0 ); if (status) goto done;
status = map_image_into_view( view, filename, unix_fd, base, image_info->header_size, @@ -2576,7 +2583,7 @@ static NTSTATUS virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_PTR z
server_enter_uninterrupted_section( &virtual_mutex, &sigset );
- res = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits ) ); + res = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits ), 0 ); if (res) goto done;
TRACE( "handle=%p size=%lx offset=%x%08x\n", handle, size, offset.u.HighPart, offset.u.LowPart ); @@ -3114,7 +3121,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI server_enter_uninterrupted_section( &virtual_mutex, &sigset );
if ((status = map_view( &view, NULL, size + extra_size, FALSE, - VPROT_READ | VPROT_WRITE | VPROT_COMMITTED, get_zero_bits_mask( zero_bits ) )) + VPROT_READ | VPROT_WRITE | VPROT_COMMITTED, get_zero_bits_mask( zero_bits ), 0 )) != STATUS_SUCCESS) goto done;
@@ -3732,6 +3739,7 @@ NTSTATUS WINAPI NtAllocateVirtualMemoryEx( HANDLE process, PVOID *ret, SIZE_T *s SIZE_T size = *size_ptr; NTSTATUS status = STATUS_SUCCESS; ULONG_PTR limit = 0; + ULONG_PTR align = 0;
TRACE("%p %p %08lx %x %08x %p %u\n", process, *ret, size, type, protect, parameters, count );
@@ -3763,17 +3771,27 @@ NTSTATUS WINAPI NtAllocateVirtualMemoryEx( HANDLE process, PVOID *ret, SIZE_T *s mem_requirements_found = 1; r = (MEM_ADDRESS_REQUIREMENTS *)parameters[i].Pointer;
- if (r->LowestStartingAddress || r->Alignment) + if (r->LowestStartingAddress) FIXME( "Not supported requirements LowestStartingAddress %p, Alignment %p.\n", r->LowestStartingAddress, (void *)r->Alignment );
+ if (r->Alignment) + { + if (*ret || (r->Alignment & (r->Alignment - 1)) || r->Alignment - 1 < granularity_mask) + { + WARN( "Invalid alignment %lu.\n", r->Alignment ); + return STATUS_INVALID_PARAMETER; + } + align = r->Alignment; + } + limit = (ULONG_PTR)r->HighestEndingAddress; if (limit && (*ret || limit > (ULONG_PTR)user_space_limit || ((limit + 1) & (page_mask - 1)))) { WARN( "Invalid limit %p.\n", r->HighestEndingAddress); return STATUS_INVALID_PARAMETER; } - TRACE( "limit %p.\n", (void *)limit ); + TRACE( "limit %p, align %p.\n", (void *)limit, (void *)align ); } }
@@ -3790,6 +3808,7 @@ NTSTATUS WINAPI NtAllocateVirtualMemoryEx( HANDLE process, PVOID *ret, SIZE_T *s call.virtual_alloc_ex.addr = wine_server_client_ptr( *ret ); call.virtual_alloc_ex.size = *size_ptr; call.virtual_alloc_ex.limit = limit; + call.virtual_alloc_ex.align = align; call.virtual_alloc_ex.op_type = type; call.virtual_alloc_ex.prot = protect; status = server_queue_process_apc( process, &call, &result ); @@ -3854,7 +3873,8 @@ NTSTATUS WINAPI NtAllocateVirtualMemoryEx( HANDLE process, PVOID *ret, SIZE_T *s
if (vprot & VPROT_WRITECOPY) status = STATUS_INVALID_PAGE_PROTECTION; else if (is_dos_memory) status = allocate_dos_memory( &view, vprot ); - else status = map_view( &view, base, size, type & MEM_TOP_DOWN, vprot, limit ); + else status = map_view( &view, base, size, type & MEM_TOP_DOWN, vprot, limit, + align ? align - 1 : granularity_mask );
if (status == STATUS_SUCCESS) base = view->base; } diff --git a/server/protocol.def b/server/protocol.def index bc8e328e3b1..19e25f26d31 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -542,6 +542,7 @@ typedef union client_ptr_t addr; /* requested address */ mem_size_t size; /* allocation size */ mem_size_t limit; /* allocation address limit */ + mem_size_t align; /* allocation alignment */ unsigned int prot; /* memory protection flags */ } virtual_alloc_ex; struct diff --git a/server/trace.c b/server/trace.c index b79a0434eb1..c4b04ed79e8 100644 --- a/server/trace.c +++ b/server/trace.c @@ -193,6 +193,7 @@ static void dump_apc_call( const char *prefix, const apc_call_t *call ) dump_uint64( "APC_VIRTUAL_ALLOC,addr==", &call->virtual_alloc_ex.addr ); dump_uint64( ",size=", &call->virtual_alloc_ex.size ); dump_uint64( ",limit=", &call->virtual_alloc_ex.limit ); + dump_uint64( ",align=", &call->virtual_alloc_ex.align ); fprintf( stderr, ",op_type=%x,prot=%x", call->virtual_alloc_ex.op_type, call->virtual_alloc_ex.prot ); break; case APC_VIRTUAL_FREE: