af062818b47340eef15700d2f0211576ba3506eevboxsync * Wine memory mappings support
af062818b47340eef15700d2f0211576ba3506eevboxsync * Copyright 2000, 2004 Alexandre Julliard
af062818b47340eef15700d2f0211576ba3506eevboxsync * This library is free software; you can redistribute it and/or
af062818b47340eef15700d2f0211576ba3506eevboxsync * modify it under the terms of the GNU Lesser General Public
af062818b47340eef15700d2f0211576ba3506eevboxsync * License as published by the Free Software Foundation; either
af062818b47340eef15700d2f0211576ba3506eevboxsync * version 2.1 of the License, or (at your option) any later version.
af062818b47340eef15700d2f0211576ba3506eevboxsync * This library is distributed in the hope that it will be useful,
af062818b47340eef15700d2f0211576ba3506eevboxsync * but WITHOUT ANY WARRANTY; without even the implied warranty of
af062818b47340eef15700d2f0211576ba3506eevboxsync * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
af062818b47340eef15700d2f0211576ba3506eevboxsync * Lesser General Public License for more details.
af062818b47340eef15700d2f0211576ba3506eevboxsync * You should have received a copy of the GNU Lesser General Public
af062818b47340eef15700d2f0211576ba3506eevboxsync * License along with this library; if not, write to the Free Software
af062818b47340eef15700d2f0211576ba3506eevboxsync * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
4b9d6701570cb98fd36e209314239d104ec584d3vboxsync * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
4b9d6701570cb98fd36e209314239d104ec584d3vboxsync * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
b955672b950093ff7416d1269dd4d3b69983bd8fvboxsync * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
b955672b950093ff7416d1269dd4d3b69983bd8fvboxsync * a choice of LGPL license versions is made available with the language indicating
b955672b950093ff7416d1269dd4d3b69983bd8fvboxsync * that LGPLv2 or any later version may be used, or where a choice of which version
b955672b950093ff7416d1269dd4d3b69983bd8fvboxsync * of the LGPL is applied is otherwise unspecified.
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic struct list reserved_areas = LIST_INIT(reserved_areas);
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic const unsigned int granularity_mask = 0xffff; /* reserved areas have 64k granularity */
af062818b47340eef15700d2f0211576ba3506eevboxsync#if (defined(__svr4__) || defined(__NetBSD__)) && !defined(MAP_TRYFIXED)
af062818b47340eef15700d2f0211576ba3506eevboxsync/***********************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync * try_mmap_fixed
af062818b47340eef15700d2f0211576ba3506eevboxsync * The purpose of this routine is to emulate the behaviour of
af062818b47340eef15700d2f0211576ba3506eevboxsync * the Linux mmap() routine if a non-NULL address is passed,
af062818b47340eef15700d2f0211576ba3506eevboxsync * but the MAP_FIXED flag is not set. Linux in this case tries
af062818b47340eef15700d2f0211576ba3506eevboxsync * to place the mapping at the specified address, *unless* the
af062818b47340eef15700d2f0211576ba3506eevboxsync * range is already in use. Solaris, however, completely ignores
af062818b47340eef15700d2f0211576ba3506eevboxsync * the address argument in this case.
af062818b47340eef15700d2f0211576ba3506eevboxsync * As Wine code occasionally relies on the Linux behaviour, e.g. to
af062818b47340eef15700d2f0211576ba3506eevboxsync * be able to map non-relocatable PE executables to their proper
af062818b47340eef15700d2f0211576ba3506eevboxsync * start addresses, or to map the DOS memory to 0, this routine
af062818b47340eef15700d2f0211576ba3506eevboxsync * emulates the Linux behaviour by checking whether the desired
af062818b47340eef15700d2f0211576ba3506eevboxsync * address range is still available, and placing the mapping there
af062818b47340eef15700d2f0211576ba3506eevboxsync * using MAP_FIXED if so.
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
af062818b47340eef15700d2f0211576ba3506eevboxsync /* We only try to map to a fixed address if
af062818b47340eef15700d2f0211576ba3506eevboxsync addr is non-NULL and properly aligned,
af062818b47340eef15700d2f0211576ba3506eevboxsync and MAP_FIXED isn't already specified. */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* We use vfork() to freeze all threads of the
af062818b47340eef15700d2f0211576ba3506eevboxsync current process. This allows us to check without
af062818b47340eef15700d2f0211576ba3506eevboxsync race condition whether the desired memory range is
af062818b47340eef15700d2f0211576ba3506eevboxsync already in use. Note that because vfork() shares
af062818b47340eef15700d2f0211576ba3506eevboxsync the address spaces between parent and child, we
af062818b47340eef15700d2f0211576ba3506eevboxsync can actually perform the mapping in the child. */
af062818b47340eef15700d2f0211576ba3506eevboxsync if ( pid == 0 )
af062818b47340eef15700d2f0211576ba3506eevboxsync /* We call mincore() for every page in the desired range.
af062818b47340eef15700d2f0211576ba3506eevboxsync If any of these calls succeeds, the page is already
af062818b47340eef15700d2f0211576ba3506eevboxsync mapped and we must fail. */
af062818b47340eef15700d2f0211576ba3506eevboxsync if ( mincore( (caddr_t)addr + i, pagesize, &vec ) != -1 )
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Perform the mapping with MAP_FIXED set. This is safe
af062818b47340eef15700d2f0211576ba3506eevboxsync now, as none of the pages is currently in use. */
af062818b47340eef15700d2f0211576ba3506eevboxsync result = mmap( addr, len, prot, flags | MAP_FIXED, fildes, off );
af062818b47340eef15700d2f0211576ba3506eevboxsync if ( result != (void *) -1 ) /* This should never happen ... */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* vfork() lets the parent continue only after the child
af062818b47340eef15700d2f0211576ba3506eevboxsync has exited. Furthermore, Wine sets SIGCHLD to SIG_IGN,
af062818b47340eef15700d2f0211576ba3506eevboxsync so we don't need to wait for the child. */
af062818b47340eef15700d2f0211576ba3506eevboxsync * On Darwin, we can use the Mach call vm_allocate to allocate
af062818b47340eef15700d2f0211576ba3506eevboxsync * anonymous memory at the specified address, and then use mmap with
af062818b47340eef15700d2f0211576ba3506eevboxsync * MAP_FIXED to replace the mapping.
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
af062818b47340eef15700d2f0211576ba3506eevboxsync if (mmap( (void *)result, len, prot, flags | MAP_FIXED, fildes, off ) != MAP_FAILED)
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif /* (__svr4__ || __NetBSD__) && !MAP_TRYFIXED */
af062818b47340eef15700d2f0211576ba3506eevboxsync/***********************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync * wine_anon_mmap
af062818b47340eef15700d2f0211576ba3506eevboxsync * Portable wrapper for anonymous mmaps
af062818b47340eef15700d2f0211576ba3506eevboxsyncvoid *wine_anon_mmap( void *start, size_t size, int prot, int flags )
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
af062818b47340eef15700d2f0211576ba3506eevboxsync#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Even FreeBSD 5.3 does not properly support NULL here. */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* If available, this will attempt a fixed mapping in-kernel */
af062818b47340eef15700d2f0211576ba3506eevboxsync#elif defined(__svr4__) || defined(__NetBSD__) || defined(__APPLE__)
af062818b47340eef15700d2f0211576ba3506eevboxsync if ( try_mmap_fixed( start, size, prot, flags, get_fdzero(), 0 ) )
af062818b47340eef15700d2f0211576ba3506eevboxsync return mmap( start, size, prot, flags, get_fdzero(), 0 );
af062818b47340eef15700d2f0211576ba3506eevboxsync/***********************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync * mmap_reserve
af062818b47340eef15700d2f0211576ba3506eevboxsync * mmap wrapper used for reservations, only maps the specified address
af062818b47340eef15700d2f0211576ba3506eevboxsyncstatic inline int mmap_reserve( void *addr, size_t size )
af062818b47340eef15700d2f0211576ba3506eevboxsync int flags = MAP_PRIVATE | MAP_ANON | MAP_NORESERVE;
af062818b47340eef15700d2f0211576ba3506eevboxsync return try_mmap_fixed( addr, size, PROT_NONE, flags, get_fdzero(), 0 );
af062818b47340eef15700d2f0211576ba3506eevboxsync ptr = mmap( addr, size, PROT_NONE, flags, get_fdzero(), 0 );
af062818b47340eef15700d2f0211576ba3506eevboxsync if (ptr != addr && ptr != (void *)-1) munmap( ptr, size );
af062818b47340eef15700d2f0211576ba3506eevboxsync/***********************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync * reserve_area
af062818b47340eef15700d2f0211576ba3506eevboxsync * Reserve as much memory as possible in the given area.
af062818b47340eef15700d2f0211576ba3506eevboxsync#if (defined(__svr4__) || defined(__NetBSD__)) && !defined(MAP_TRYFIXED)
af062818b47340eef15700d2f0211576ba3506eevboxsync /* try_mmap_fixed is inefficient when using vfork, so we need a different algorithm here */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* we assume no other thread is running at this point */
af062818b47340eef15700d2f0211576ba3506eevboxsync if (mincore( (caddr_t)addr + i, pagesize, &vec ) != -1) break;
af062818b47340eef15700d2f0211576ba3506eevboxsync if (i && mmap( addr, i, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
af062818b47340eef15700d2f0211576ba3506eevboxsync if ((char *)addr + i < (char *)addr) break; /* overflow */
af062818b47340eef15700d2f0211576ba3506eevboxsync if (!size) return;
af062818b47340eef15700d2f0211576ba3506eevboxsync/***********************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync * reserve_malloc_space
af062818b47340eef15700d2f0211576ba3506eevboxsync * Solaris malloc is not smart enough to obtain space through mmap(), so try to make
af062818b47340eef15700d2f0211576ba3506eevboxsync * sure that there is some available sbrk() space before we reserve other things.
af062818b47340eef15700d2f0211576ba3506eevboxsync if (!ptrs) return;
af062818b47340eef15700d2f0211576ba3506eevboxsync for (i = 0; i < count; i++) if (!(ptrs[i] = malloc( 1024 ))) break;
af062818b47340eef15700d2f0211576ba3506eevboxsync if (i--) /* free everything except the last one */
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif /* __i386__ */
af062818b47340eef15700d2f0211576ba3506eevboxsync/***********************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync * reserve_dos_area
af062818b47340eef15700d2f0211576ba3506eevboxsync * Reserve the DOS area (0x00000000-0x00110000).
af062818b47340eef15700d2f0211576ba3506eevboxsync /* first page has to be handled specially */
af062818b47340eef15700d2f0211576ba3506eevboxsync ptr = wine_anon_mmap( (void *)page_size, dos_area_size - page_size, PROT_NONE, MAP_NORESERVE );
af062818b47340eef15700d2f0211576ba3506eevboxsync if (ptr != (void *)-1) munmap( ptr, dos_area_size - page_size );
af062818b47340eef15700d2f0211576ba3506eevboxsync /* now add first page with MAP_FIXED */
af062818b47340eef15700d2f0211576ba3506eevboxsync wine_anon_mmap( NULL, page_size, PROT_NONE, MAP_NORESERVE|MAP_FIXED );
af062818b47340eef15700d2f0211576ba3506eevboxsync wine_mmap_add_reserved_area( NULL, dos_area_size );
af062818b47340eef15700d2f0211576ba3506eevboxsync/***********************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync * mmap_init
5112e32d7072e280613921c982a6672f2c859cf3vboxsync /* if we don't have a preloader, try to reserve some space below 2Gb */
5112e32d7072e280613921c982a6672f2c859cf3vboxsync reserve_area( (void *)0x00110000, (void *)0x40000000 );
af062818b47340eef15700d2f0211576ba3506eevboxsync /* check for a reserved area starting at the user space limit */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* to avoid wasting time trying to allocate it again */
af062818b47340eef15700d2f0211576ba3506eevboxsync area = LIST_ENTRY( ptr, struct reserved_area, entry );
af062818b47340eef15700d2f0211576ba3506eevboxsync if ((char *)area->base + area->size > user_space_limit)
af062818b47340eef15700d2f0211576ba3506eevboxsync user_space_limit = (char *)area->base + area->size;
af062818b47340eef15700d2f0211576ba3506eevboxsync char *base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) - (granularity_mask + 1);
af062818b47340eef15700d2f0211576ba3506eevboxsync if (base > user_space_limit) reserve_area( user_space_limit, base );
af062818b47340eef15700d2f0211576ba3506eevboxsync base = stack_ptr - ((unsigned int)stack_ptr & granularity_mask) + (granularity_mask + 1);
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync /* Heuristic: assume the stack is near the end of the address */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* space, this avoids a lot of futile allocation attempts */
af062818b47340eef15700d2f0211576ba3506eevboxsync end = (char *)(((unsigned long)base + 0x0fffffff) & 0xf0000000);
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif /* __i386__ */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* reserve the DOS area if not already done */
af062818b47340eef15700d2f0211576ba3506eevboxsync area = LIST_ENTRY( ptr, struct reserved_area, entry );
af062818b47340eef15700d2f0211576ba3506eevboxsync/***********************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync * wine_mmap_add_reserved_area
af062818b47340eef15700d2f0211576ba3506eevboxsync * Add an address range to the list of reserved areas.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Caller must have made sure the range is not used by anything else.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Note: the reserved areas functions are not reentrant, caller is
af062818b47340eef15700d2f0211576ba3506eevboxsync * responsible for proper locking.
af062818b47340eef15700d2f0211576ba3506eevboxsyncvoid wine_mmap_add_reserved_area( void *addr, size_t size )
af062818b47340eef15700d2f0211576ba3506eevboxsync if (!((char *)addr + size)) size--; /* avoid wrap-around */
af062818b47340eef15700d2f0211576ba3506eevboxsync area = LIST_ENTRY( ptr, struct reserved_area, entry );
af062818b47340eef15700d2f0211576ba3506eevboxsync /* try to merge with the next one */
af062818b47340eef15700d2f0211576ba3506eevboxsync else if ((char *)area->base + area->size == (char *)addr)
af062818b47340eef15700d2f0211576ba3506eevboxsync /* merge with the previous one */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* try to merge with the next one too */
af062818b47340eef15700d2f0211576ba3506eevboxsync struct reserved_area *next = LIST_ENTRY( ptr, struct reserved_area, entry );
af062818b47340eef15700d2f0211576ba3506eevboxsync/***********************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync * wine_mmap_remove_reserved_area
af062818b47340eef15700d2f0211576ba3506eevboxsync * Remove an address range from the list of reserved areas.
af062818b47340eef15700d2f0211576ba3506eevboxsync * If 'unmap' is non-zero the range is unmapped too.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Note: the reserved areas functions are not reentrant, caller is
af062818b47340eef15700d2f0211576ba3506eevboxsync * responsible for proper locking.
af062818b47340eef15700d2f0211576ba3506eevboxsyncvoid wine_mmap_remove_reserved_area( void *addr, size_t size, int unmap )
af062818b47340eef15700d2f0211576ba3506eevboxsync if (!((char *)addr + size)) size--; /* avoid wrap-around */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* find the first area covering address */
af062818b47340eef15700d2f0211576ba3506eevboxsync area = LIST_ENTRY( ptr, struct reserved_area, entry );
af062818b47340eef15700d2f0211576ba3506eevboxsync if ((char *)area->base >= (char *)addr + size) break; /* outside the range */
af062818b47340eef15700d2f0211576ba3506eevboxsync if ((char *)area->base + area->size > (char *)addr) /* overlaps range */
af062818b47340eef15700d2f0211576ba3506eevboxsync if ((char *)area->base + area->size > (char *)addr + size)
af062818b47340eef15700d2f0211576ba3506eevboxsync /* range overlaps beginning of area only -> shrink area */
af062818b47340eef15700d2f0211576ba3506eevboxsync if (unmap) munmap( area->base, (char *)addr + size - (char *)area->base );
af062818b47340eef15700d2f0211576ba3506eevboxsync area->size -= (char *)addr + size - (char *)area->base;
af062818b47340eef15700d2f0211576ba3506eevboxsync /* range contains the whole area -> remove area completely */
af062818b47340eef15700d2f0211576ba3506eevboxsync if ((char *)area->base + area->size > (char *)addr + size)
af062818b47340eef15700d2f0211576ba3506eevboxsync /* range is in the middle of area -> split area in two */
af062818b47340eef15700d2f0211576ba3506eevboxsync struct reserved_area *new_area = malloc( sizeof(*new_area) );
af062818b47340eef15700d2f0211576ba3506eevboxsync new_area->size = (char *)area->base + area->size - (char *)new_area->base;
af062818b47340eef15700d2f0211576ba3506eevboxsync else size = (char *)area->base + area->size - (char *)addr;
af062818b47340eef15700d2f0211576ba3506eevboxsync /* range overlaps end of area only -> shrink area */
af062818b47340eef15700d2f0211576ba3506eevboxsync if (unmap) munmap( addr, (char *)area->base + area->size - (char *)addr );
af062818b47340eef15700d2f0211576ba3506eevboxsync/***********************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync * wine_mmap_is_in_reserved_area
af062818b47340eef15700d2f0211576ba3506eevboxsync * Check if the specified range is included in a reserved area.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Returns 1 if range is fully included, 0 if range is not included
af062818b47340eef15700d2f0211576ba3506eevboxsync * at all, and -1 if it is only partially included.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Note: the reserved areas functions are not reentrant, caller is
af062818b47340eef15700d2f0211576ba3506eevboxsync * responsible for proper locking.
af062818b47340eef15700d2f0211576ba3506eevboxsyncint wine_mmap_is_in_reserved_area( void *addr, size_t size )
af062818b47340eef15700d2f0211576ba3506eevboxsync area = LIST_ENTRY( ptr, struct reserved_area, entry );
af062818b47340eef15700d2f0211576ba3506eevboxsync if ((char *)area->base + area->size <= (char *)addr) continue;
af062818b47340eef15700d2f0211576ba3506eevboxsync /* area must contain block completely */
af062818b47340eef15700d2f0211576ba3506eevboxsync if ((char *)area->base + area->size < (char *)addr + size) return -1;
af062818b47340eef15700d2f0211576ba3506eevboxsync/***********************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync * wine_mmap_enum_reserved_areas
af062818b47340eef15700d2f0211576ba3506eevboxsync * Enumerate the list of reserved areas, sorted by addresses.
af062818b47340eef15700d2f0211576ba3506eevboxsync * If enum_func returns a non-zero value, enumeration is stopped and the value is returned.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Note: the reserved areas functions are not reentrant, caller is
af062818b47340eef15700d2f0211576ba3506eevboxsync * responsible for proper locking.
af062818b47340eef15700d2f0211576ba3506eevboxsyncint wine_mmap_enum_reserved_areas( int (*enum_func)(void *base, size_t size, void *arg), void *arg,
af062818b47340eef15700d2f0211576ba3506eevboxsync for (ptr = reserved_areas.prev; ptr != &reserved_areas; ptr = ptr->prev)
af062818b47340eef15700d2f0211576ba3506eevboxsync struct reserved_area *area = LIST_ENTRY( ptr, struct reserved_area, entry );
af062818b47340eef15700d2f0211576ba3506eevboxsync if ((ret = enum_func( area->base, area->size, arg ))) break;
af062818b47340eef15700d2f0211576ba3506eevboxsync for (ptr = reserved_areas.next; ptr != &reserved_areas; ptr = ptr->next)
af062818b47340eef15700d2f0211576ba3506eevboxsync struct reserved_area *area = LIST_ENTRY( ptr, struct reserved_area, entry );
af062818b47340eef15700d2f0211576ba3506eevboxsync if ((ret = enum_func( area->base, area->size, arg ))) break;
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync#else /* HAVE_MMAP */