From c52a11efa534fecc76af2034f37a11b32490dfca Mon Sep 17 00:00:00 2001 From: Ulrich Weigand <weigand@immd1.informatik.uni-erlangen.de> Date: Fri, 7 Sep 2001 18:46:14 +0000 Subject: [PATCH] Emulate the behaviour of Linux mmap() on Solaris. Based on ideas and sample code due to Erik Boasson <eboasson@signaal.nl> and John Wehle <john@feith.com>. --- library/port.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/library/port.c b/library/port.c index 03ada140f72..0acf9d499d1 100644 --- a/library/port.c +++ b/library/port.c @@ -396,6 +396,87 @@ int getrlimit (int resource, struct rlimit *rlim) } #endif /* HAVE_GETRLIMIT */ + +#ifdef __svr4__ +/*********************************************************************** + * solaris_try_mmap + * + * The purpose of this routine is to emulate the behaviour of + * the Linux mmap() routine if a non-NULL address is passed, + * but the MAP_FIXED flag is not set. Linux in this case tries + * to place the mapping at the specified address, *unless* the + * range is already in use. Solaris, however, completely ignores + * the address argument in this case. + * + * As Wine code occasionally relies on the Linux behaviour, e.g. to + * be able to map non-relocateable PE executables to their proper + * start addresses, or to map the DOS memory to 0, this routine + * emulates the Linux behaviour by checking whether the desired + * address range is still available, and placing the mapping there + * using MAP_FIXED if so. + */ +static int solaris_try_mmap (void *addr, size_t len, int prot, int flags, + int fildes, off_t off) +{ + char * volatile result = NULL; + int pagesize = getpagesize(); + pid_t pid; + + /* We only try to map to a fixed address if + addr is non-NULL and properly aligned, + and MAP_FIXED isn't already specified. */ + + if ( !addr ) + return FALSE; + if ( (uintptr_t)addr & (pagesize-1) ) + return FALSE; + if ( flags & MAP_FIXED ) + return FALSE; + + /* We use vfork() to freeze all threads of the + current process. This allows us to check without + race condition whether the desired memory range is + already in use. Note that because vfork() shares + the address spaces between parent and child, we + can actually perform the mapping in the child. */ + + if ( (pid = vfork()) == -1 ) + { + perror("solaris_try_mmap: vfork"); + exit(1); + } + if ( pid == 0 ) + { + int i; + char vec; + + /* We call mincore() for every page in the desired range. + If any of these calls succeeds, the page is already + mapped and we must fail. */ + for ( i = 0; i < len; i += pagesize ) + if ( mincore( (caddr_t)addr + i, pagesize, &vec ) != -1 ) + _exit(1); + + /* Perform the mapping with MAP_FIXED set. This is safe + now, as none of the pages is currently in use. */ + result = mmap( addr, len, prot, flags | MAP_FIXED, fildes, off ); + if ( result == addr ) + _exit(0); + + if ( result != (void *) -1 ) /* This should never happen ... */ + munmap( result, len ); + + _exit(1); + } + + /* vfork() lets the parent continue only after the child + has exited. Furthermore, Wine sets SIGCHLD to SIG_IGN, + so we don't need to wait for the child. */ + + return result == addr; +} +#endif + /*********************************************************************** * wine_anon_mmap * @@ -427,6 +508,11 @@ void *wine_anon_mmap( void *start, size_t size, int prot, int flags ) flags |= MAP_PRIVATE; #endif +#ifdef __svr4__ + if ( solaris_try_mmap( start, size, prot, flags, fdzero, 0 ) ) + return start; +#endif + return mmap( start, size, prot, flags, fdzero, 0 ); } -- GitLab