Implement the correct zero_bits behavior for this single case: * Limit the search in reserved areas to the lower 2G range, * Pass the MAP_32BIT flag to mmap as a fallback.
LuaJIT <= v2.0.5 for example, when running in 64bit, allocates its memory in the lower 2GB memory region by using the zero_bits parameter.
This will fix this particular scenario, while trying to minimize the changes on all the other cases.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/kernel32/tests/virtual.c | 1 - dlls/ntdll/virtual.c | 35 +++++++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c index 79eed345910..72ab3efac86 100644 --- a/dlls/kernel32/tests/virtual.c +++ b/dlls/kernel32/tests/virtual.c @@ -474,7 +474,6 @@ static void test_VirtualAlloc(void) zero_bits = 1; status = pNtAllocateVirtualMemory(GetCurrentProcess(), &addr2, 1, &size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); - todo_wine ok((status == STATUS_SUCCESS || broken(status == STATUS_INVALID_PARAMETER_3) /* winxp */) && ((UINT_PTR)addr2 >> (32 - zero_bits)) == 0, "NtAllocateVirtualMemory returned %08x, addr2: %p\n", status, addr2); diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c index 175d73cbe7c..b07afa8f924 100644 --- a/dlls/ntdll/virtual.c +++ b/dlls/ntdll/virtual.c @@ -1083,7 +1083,7 @@ static NTSTATUS map_fixed_area( void *base, size_t size, unsigned int vprot ) * The csVirtual section must be held by caller. */ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, size_t mask, - int top_down, unsigned int vprot ) + int top_down, unsigned int vprot, size_t zero_bits ) { void *ptr; NTSTATUS status; @@ -1100,11 +1100,26 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, { size_t view_size = size + mask + 1; struct alloc_area alloc; + int flags = 0;
alloc.size = size; alloc.mask = mask; alloc.top_down = top_down; alloc.limit = user_space_limit; + +#if defined(__x86_64__) && defined(MAP_32BIT) + /* HACK: only works for zero_bits == 1, this is a simple workaround + * for some 64bit code that tries to allocate memory in the lower + * 2GB segment using zero_bits parameter. + */ + assert(zero_bits <= 1); + if (zero_bits == 1) + { + alloc.limit = (void*)(((~(UINT_PTR)0) >> (32 + zero_bits)) & ~0xffff); + flags = MAP_32BIT; + } +#endif + if (wine_mmap_enum_reserved_areas( alloc_reserved_area_callback, &alloc, top_down )) { ptr = alloc.result; @@ -1116,7 +1131,7 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size,
for (;;) { - if ((ptr = wine_anon_mmap( NULL, view_size, VIRTUAL_GetUnixProt(vprot), 0 )) == (void *)-1) + if ((ptr = wine_anon_mmap( NULL, view_size, VIRTUAL_GetUnixProt(vprot), flags )) == (void *)-1) { if (errno == ENOMEM) return STATUS_NO_MEMORY; return STATUS_INVALID_PARAMETER; @@ -1284,7 +1299,7 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot if (addr != low_64k) { if (addr != (void *)-1) munmap( addr, dosmem_size - 0x10000 ); - return map_view( view, NULL, dosmem_size, 0xffff, 0, vprot ); + return map_view( view, NULL, dosmem_size, 0xffff, 0, vprot, 0 ); } }
@@ -1388,11 +1403,11 @@ static NTSTATUS map_image( HANDLE hmapping, ACCESS_MASK access, int fd, SIZE_T m
if (base >= (char *)address_space_start) /* make sure the DOS area remains free */ status = map_view( &view, base, total_size, mask, FALSE, SEC_IMAGE | SEC_FILE | - VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY ); + VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY, 0 );
if (status != STATUS_SUCCESS) status = map_view( &view, NULL, total_size, mask, FALSE, SEC_IMAGE | SEC_FILE | - VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY ); + VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY, 0 );
if (status != STATUS_SUCCESS) goto error;
@@ -1713,7 +1728,7 @@ NTSTATUS virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG zero_bits, S get_vprot_flags( protect, &vprot, sec_flags & SEC_IMAGE ); vprot |= sec_flags; if (!(sec_flags & SEC_RESERVE)) vprot |= VPROT_COMMITTED; - res = map_view( &view, *addr_ptr, size, mask, FALSE, vprot ); + res = map_view( &view, *addr_ptr, size, mask, FALSE, vprot, 0 ); if (res) { server_leave_uninterrupted_section( &csVirtual, &sigset ); @@ -1946,7 +1961,7 @@ NTSTATUS virtual_alloc_thread_stack( TEB *teb, SIZE_T reserve_size, SIZE_T commi server_enter_uninterrupted_section( &csVirtual, &sigset );
if ((status = map_view( &view, NULL, size + extra_size, 0xffff, 0, - VPROT_READ | VPROT_WRITE | VPROT_COMMITTED )) != STATUS_SUCCESS) + VPROT_READ | VPROT_WRITE | VPROT_COMMITTED, 0 )) != STATUS_SUCCESS) goto done;
#ifdef VALGRIND_STACK_REGISTER @@ -2468,7 +2483,11 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG zero_ TRACE("%p %p %08lx %x %08x\n", process, *ret, *size_ptr, type, protect );
if (!*size_ptr) return STATUS_INVALID_PARAMETER; +#if defined(__x86_64__) && !defined(MAP_32BIT) if (zero_bits) +#else + if (zero_bits > 1) +#endif { FIXME("Unimplemented zero_bits handling\n"); return STATUS_INVALID_PARAMETER_3; @@ -2570,7 +2589,7 @@ NTSTATUS virtual_alloc( PVOID *ret, ULONG zero_bits, SIZE_T *size_ptr,
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, mask, type & MEM_TOP_DOWN, vprot ); + else status = map_view( &view, base, size, mask, type & MEM_TOP_DOWN, vprot, zero_bits );
if (status == STATUS_SUCCESS) base = view->base; }