Commit 4e80f2ea authored by Alexandre Julliard's avatar Alexandre Julliard

ntdll: Add a helper function to try an mmap at a fixed address.

parent 72b97eeb
...@@ -59,7 +59,6 @@ ...@@ -59,7 +59,6 @@
#include "windef.h" #include "windef.h"
#include "winnt.h" #include "winnt.h"
#include "winternl.h" #include "winternl.h"
#include "wine/library.h"
#include "wine/exception.h" #include "wine/exception.h"
#include "wine/list.h" #include "wine/list.h"
#include "wine/rbtree.h" #include "wine/rbtree.h"
...@@ -173,12 +172,6 @@ static struct list teb_list = LIST_INIT( teb_list ); ...@@ -173,12 +172,6 @@ static struct list teb_list = LIST_INIT( teb_list );
#ifndef MAP_NORESERVE #ifndef MAP_NORESERVE
#define MAP_NORESERVE 0 #define MAP_NORESERVE 0
#endif #endif
#ifndef MAP_TRYFIXED
#define MAP_TRYFIXED 0
#endif
#ifndef MAP_FIXED_NOREPLACE
#define MAP_FIXED_NOREPLACE 0
#endif
#ifdef _WIN64 /* on 64-bit the page protection bytes use a 2-level table */ #ifdef _WIN64 /* on 64-bit the page protection bytes use a 2-level table */
static const size_t pages_vprot_shift = 20; static const size_t pages_vprot_shift = 20;
...@@ -211,6 +204,11 @@ static inline BOOL is_inside_signal_stack( void *ptr ) ...@@ -211,6 +204,11 @@ static inline BOOL is_inside_signal_stack( void *ptr )
(char *)ptr < (char *)get_signal_stack() + signal_stack_size); (char *)ptr < (char *)get_signal_stack() + signal_stack_size);
} }
static inline BOOL is_beyond_limit( const void *addr, size_t size, const void *limit )
{
return (addr >= limit || (const char *)addr + size > (const char *)limit);
}
/* mmap() anonymous memory at a fixed address */ /* mmap() anonymous memory at a fixed address */
void *anon_mmap_fixed( void *start, size_t size, int prot, int flags ) void *anon_mmap_fixed( void *start, size_t size, int prot, int flags )
{ {
...@@ -375,6 +373,48 @@ static int mmap_enum_reserved_areas( int (CDECL *enum_func)(void *base, SIZE_T s ...@@ -375,6 +373,48 @@ static int mmap_enum_reserved_areas( int (CDECL *enum_func)(void *base, SIZE_T s
return ret; return ret;
} }
static void *anon_mmap_tryfixed( void *start, size_t size, int prot, int flags )
{
void *ptr;
#ifdef MAP_FIXED_NOREPLACE
ptr = mmap( start, size, prot, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANON | flags, -1, 0 );
#elif defined(MAP_TRYFIXED)
ptr = mmap( start, size, prot, MAP_TRYFIXED | MAP_PRIVATE | MAP_ANON | flags, -1, 0 );
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
ptr = mmap( start, size, prot, MAP_FIXED | MAP_EXCL | MAP_PRIVATE | MAP_ANON | flags, -1, 0 );
if (ptr == MAP_FAILED && errno == EINVAL) errno = EEXIST;
#elif defined(__APPLE__)
mach_vm_address_t result = (mach_vm_address_t)start;
kern_return_t ret = mach_vm_map( mach_task_self(), &result, size, 0, VM_FLAGS_FIXED,
MEMORY_OBJECT_NULL, 0, 0, prot, VM_PROT_ALL, VM_INHERIT_COPY );
if (!ret)
{
if ((ptr = anon_mmap_fixed( start, size, prot, flags )) == MAP_FAILED)
mach_vm_deallocate( mach_task_self(), result, size );
}
else
{
errno = (ret == KERN_NO_SPACE ? EEXIST : ENOMEM);
ptr = MAP_FAILED;
}
#else
ptr = mmap( start, size, prot, MAP_PRIVATE | MAP_ANON | flags, -1, 0 );
#endif
if (ptr != MAP_FAILED && ptr != start)
{
if (is_beyond_limit( ptr, size, user_space_limit ))
{
anon_mmap_fixed( ptr, size, PROT_NONE, MAP_NORESERVE );
mmap_add_reserved_area( ptr, size );
}
else munmap( ptr, size );
ptr = MAP_FAILED;
errno = EEXIST;
}
return ptr;
}
static void reserve_area( void *addr, void *end ) static void reserve_area( void *addr, void *end )
{ {
...@@ -432,23 +472,15 @@ static void reserve_area( void *addr, void *end ) ...@@ -432,23 +472,15 @@ static void reserve_area( void *addr, void *end )
} }
#else #else
void *ptr; void *ptr;
int flags = MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_TRYFIXED;
size_t size = (char *)end - (char *)addr; size_t size = (char *)end - (char *)addr;
if (!size) return; if (!size) return;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) if ((ptr = anon_mmap_tryfixed( addr, size, PROT_NONE, MAP_NORESERVE )) != MAP_FAILED)
ptr = mmap( addr, size, PROT_NONE, flags | MAP_FIXED | MAP_EXCL, -1, 0 );
#else
ptr = mmap( addr, size, PROT_NONE, flags, -1, 0 );
#endif
if (ptr == addr)
{ {
mmap_add_reserved_area( addr, size ); mmap_add_reserved_area( addr, size );
return; return;
} }
if (ptr != (void *)-1) munmap( ptr, size );
size = (size / 2) & ~granularity_mask; size = (size / 2) & ~granularity_mask;
if (size) if (size)
{ {
...@@ -1028,20 +1060,14 @@ static void* try_map_free_area( void *base, void *end, ptrdiff_t step, ...@@ -1028,20 +1060,14 @@ static void* try_map_free_area( void *base, void *end, ptrdiff_t step,
while (start && base <= start && (char*)start + size <= (char*)end) while (start && base <= start && (char*)start + size <= (char*)end)
{ {
if ((ptr = wine_anon_mmap( start, size, unix_prot, MAP_FIXED_NOREPLACE )) == start) if ((ptr = anon_mmap_tryfixed( start, size, unix_prot, 0 )) != MAP_FAILED) return start;
return start;
TRACE( "Found free area is already mapped, start %p.\n", start ); TRACE( "Found free area is already mapped, start %p.\n", start );
if (errno != EEXIST)
if (ptr == (void *)-1 && errno != EEXIST)
{ {
ERR( "wine_anon_mmap() error %s, range %p-%p, unix_prot %#x.\n", ERR( "mmap() error %s, range %p-%p, unix_prot %#x.\n",
strerror(errno), start, (char *)start + size, unix_prot ); strerror(errno), start, (char *)start + size, unix_prot );
return NULL; return NULL;
} }
if (ptr != (void *)-1)
munmap( ptr, size );
if ((step > 0 && (char *)end - (char *)start < step) || if ((step > 0 && (char *)end - (char *)start < step) ||
(step < 0 && (char *)start - (char *)base < -step) || (step < 0 && (char *)start - (char *)base < -step) ||
step == 0) step == 0)
...@@ -1244,17 +1270,6 @@ static int CDECL get_area_boundary_callback( void *start, SIZE_T size, void *arg ...@@ -1244,17 +1270,6 @@ static int CDECL get_area_boundary_callback( void *start, SIZE_T size, void *arg
/*********************************************************************** /***********************************************************************
* is_beyond_limit
*
* Check if an address range goes beyond a given limit.
*/
static inline BOOL is_beyond_limit( const void *addr, size_t size, const void *limit )
{
return (addr >= limit || (const char *)addr + size > (const char *)limit);
}
/***********************************************************************
* unmap_area * unmap_area
* *
* Unmap an area, or simply replace it by an empty mapping if it is * Unmap an area, or simply replace it by an empty mapping if it is
...@@ -1678,18 +1693,12 @@ static NTSTATUS map_fixed_area( void *base, size_t size, unsigned int vprot ) ...@@ -1678,18 +1693,12 @@ static NTSTATUS map_fixed_area( void *base, size_t size, unsigned int vprot )
return status; return status;
} }
case 0: /* not in a reserved area, do a normal allocation */ case 0: /* not in a reserved area, do a normal allocation */
if ((ptr = wine_anon_mmap( base, size, get_unix_prot(vprot), 0 )) == (void *)-1) if ((ptr = anon_mmap_tryfixed( base, size, get_unix_prot(vprot), 0 )) == MAP_FAILED)
{ {
if (errno == ENOMEM) return STATUS_NO_MEMORY; if (errno == ENOMEM) return STATUS_NO_MEMORY;
if (errno == EEXIST) return STATUS_CONFLICTING_ADDRESSES;
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
} }
if (ptr != base)
{
/* We couldn't get the address we wanted */
if (is_beyond_limit( ptr, size, user_space_limit )) add_reserved_area( ptr, size );
else munmap( ptr, size );
return STATUS_CONFLICTING_ADDRESSES;
}
break; break;
default: default:
...@@ -1917,20 +1926,16 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot ...@@ -1917,20 +1926,16 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot
if (mmap_is_in_reserved_area( low_64k, dosmem_size - 0x10000 ) != 1) if (mmap_is_in_reserved_area( low_64k, dosmem_size - 0x10000 ) != 1)
{ {
addr = wine_anon_mmap( low_64k, dosmem_size - 0x10000, unix_prot, 0 ); addr = anon_mmap_tryfixed( low_64k, dosmem_size - 0x10000, unix_prot, 0 );
if (addr != low_64k) if (addr == MAP_FAILED) return map_view( view, NULL, dosmem_size, FALSE, vprot, 0 );
{
if (addr != (void *)-1) munmap( addr, dosmem_size - 0x10000 );
return map_view( view, NULL, dosmem_size, FALSE, vprot, 0 );
}
} }
/* now try to allocate the low 64K too */ /* now try to allocate the low 64K too */
if (mmap_is_in_reserved_area( NULL, 0x10000 ) != 1) if (mmap_is_in_reserved_area( NULL, 0x10000 ) != 1)
{ {
addr = wine_anon_mmap( (void *)page_size, 0x10000 - page_size, unix_prot, 0 ); addr = anon_mmap_tryfixed( (void *)page_size, 0x10000 - page_size, unix_prot, 0 );
if (addr == (void *)page_size) if (addr != MAP_FAILED)
{ {
if (!anon_mmap_fixed( NULL, page_size, unix_prot, 0 )) if (!anon_mmap_fixed( NULL, page_size, unix_prot, 0 ))
{ {
...@@ -1941,7 +1946,6 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot ...@@ -1941,7 +1946,6 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot
} }
else else
{ {
if (addr != (void *)-1) munmap( addr, 0x10000 - page_size );
addr = low_64k; addr = low_64k;
TRACE( "failed to map low 64K range\n" ); TRACE( "failed to map low 64K range\n" );
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment