221 lines
7.1 KiB
C
221 lines
7.1 KiB
C
/*
|
|
* Wine portability routines
|
|
*
|
|
* Copyright 2000 Alexandre Julliard
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_SYS_MMAN_H
|
|
#include <sys/mman.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_STDINT_H
|
|
# include <stdint.h>
|
|
#endif
|
|
|
|
#include "wine/library.h"
|
|
|
|
/***********************************************************************
|
|
* wine_switch_to_stack
|
|
*
|
|
* Switch to the specified stack and call the function.
|
|
*/
|
|
void DECLSPEC_NORETURN wine_switch_to_stack( void (*func)(void *), void *arg, void *stack );
|
|
#if defined(__i386__) && defined(__GNUC__)
|
|
__ASM_GLOBAL_FUNC( wine_switch_to_stack,
|
|
"movl 4(%esp),%ecx\n\t" /* func */
|
|
"movl 8(%esp),%edx\n\t" /* arg */
|
|
"movl 12(%esp),%esp\n\t" /* stack */
|
|
"pushl %edx\n\t"
|
|
"xorl %ebp,%ebp\n\t"
|
|
"call *%ecx\n\t"
|
|
"int $3" /* we never return here */ );
|
|
#elif defined(__i386__) && defined(_MSC_VER)
|
|
__declspec(naked) void wine_switch_to_stack( void (*func)(void *), void *arg, void *stack )
|
|
{
|
|
__asm mov ecx, 4[esp];
|
|
__asm mov edx, 8[esp];
|
|
__asm mov esp, 12[esp];
|
|
__asm push edx;
|
|
__asm xor ebp, ebp;
|
|
__asm call [ecx];
|
|
__asm int 3;
|
|
}
|
|
#elif defined(__sparc__) && defined(__GNUC__)
|
|
__ASM_GLOBAL_FUNC( wine_switch_to_stack,
|
|
"mov %o0, %l0\n\t" /* store first argument */
|
|
"mov %o1, %l1\n\t" /* store second argument */
|
|
"mov %o2, %sp\n\t" /* store stack */
|
|
"call %l0, 0\n\t" /* call func */
|
|
"mov %l1, %o0\n\t" /* delay slot: arg for func */
|
|
"ta 0x01"); /* breakpoint - we never get here */
|
|
#elif defined(__powerpc__) && defined(__APPLE__)
|
|
__ASM_GLOBAL_FUNC( wine_switch_to_stack,
|
|
"mtctr r3\n\t" /* func -> ctr */
|
|
"mr r3,r4\n\t" /* args -> function param 1 (r3) */
|
|
"mr r1,r5\n\t" /* stack */
|
|
"bctr\n" /* call ctr */
|
|
"1:\tb 1b"); /* loop */
|
|
#elif defined(__powerpc__) && defined(__GNUC__)
|
|
__ASM_GLOBAL_FUNC( wine_switch_to_stack,
|
|
"mtctr 3\n\t" /* func -> ctr */
|
|
"mr 3,4\n\t" /* args -> function param 1 (r3) */
|
|
"mr 1,5\n\t" /* stack */
|
|
"bctr\n\t" /* call ctr */
|
|
"1:\tb 1b"); /* loop */
|
|
#else
|
|
#error You must implement wine_switch_to_stack for your platform
|
|
#endif
|
|
|
|
|
|
#if (defined(__svr4__) || defined(__NetBSD__)) && !defined(MAP_TRYFIXED)
|
|
/***********************************************************************
|
|
* try_mmap_fixed
|
|
*
|
|
* 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 try_mmap_fixed (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 0;
|
|
if ( (uintptr_t)addr & (pagesize-1) )
|
|
return 0;
|
|
if ( flags & MAP_FIXED )
|
|
return 0;
|
|
|
|
/* 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("try_mmap_fixed: 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 /* (__svr4__ || __NetBSD__) && !MAP_TRYFIXED */
|
|
|
|
|
|
/***********************************************************************
|
|
* wine_anon_mmap
|
|
*
|
|
* Portable wrapper for anonymous mmaps
|
|
*/
|
|
void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
|
|
{
|
|
#ifdef HAVE_MMAP
|
|
static int fdzero = -1;
|
|
|
|
#ifdef MAP_ANON
|
|
flags |= MAP_ANON;
|
|
#else
|
|
if (fdzero == -1)
|
|
{
|
|
if ((fdzero = open( "/dev/zero", O_RDONLY )) == -1)
|
|
{
|
|
perror( "/dev/zero: open" );
|
|
exit(1);
|
|
}
|
|
}
|
|
#endif /* MAP_ANON */
|
|
|
|
#ifdef MAP_SHARED
|
|
flags &= ~MAP_SHARED;
|
|
#endif
|
|
|
|
/* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
|
|
#ifdef MAP_PRIVATE
|
|
flags |= MAP_PRIVATE;
|
|
#endif
|
|
|
|
#ifdef MAP_TRYFIXED
|
|
/* If available, this will attempt a fixed mapping in-kernel */
|
|
flags |= MAP_TRYFIXED;
|
|
#elif defined(__svr4__) || defined(__NetBSD__)
|
|
if ( try_mmap_fixed( start, size, prot, flags, fdzero, 0 ) )
|
|
return start;
|
|
#endif
|
|
|
|
return mmap( start, size, prot, flags, fdzero, 0 );
|
|
#else
|
|
return (void *)-1;
|
|
#endif
|
|
}
|