From ca3bfd8318420bd59bca5508c37eb1a4d06e6958 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 29 Aug 2003 22:23:42 +0000 Subject: [PATCH] Export SYSDEPS_SwitchToThreadStack() functionality from libwine as wine_switch_to_stack(). --- dlls/ntdll/sysdeps.c | 74 +------------- include/thread.h | 1 - include/wine/library.h | 5 +- libs/wine/Makefile.in | 3 +- libs/wine/loader.c | 131 ------------------------ libs/wine/port.c | 220 +++++++++++++++++++++++++++++++++++++++++ scheduler/fiber.c | 3 +- scheduler/process.c | 2 +- 8 files changed, 228 insertions(+), 211 deletions(-) create mode 100644 libs/wine/port.c diff --git a/dlls/ntdll/sysdeps.c b/dlls/ntdll/sysdeps.c index 8dee2ce65cd..3c1971507c3 100644 --- a/dlls/ntdll/sysdeps.c +++ b/dlls/ntdll/sysdeps.c @@ -115,7 +115,7 @@ void SYSDEPS_SetCurThread( TEB *teb ) inline static char *get_temp_stack(void) { unsigned int next = interlocked_xchg_add( &next_temp_stack, 1 ); - return temp_stacks[next % NB_TEMP_STACKS]; + return temp_stacks[next % NB_TEMP_STACKS] + TEMP_STACK_SIZE; } @@ -192,73 +192,6 @@ int SYSDEPS_SpawnThread( void (*func)(TEB *), TEB *teb ) } -/*********************************************************************** - * SYSDEPS_SwitchToThreadStack - * - * Switch to the stack specified in the current thread TEB - * and call the specified function. - */ -void DECLSPEC_NORETURN SYSDEPS_SwitchToThreadStack( void (*func)(void *), void *arg ); -#ifdef __i386__ -# ifdef __GNUC__ -__ASM_GLOBAL_FUNC( SYSDEPS_SwitchToThreadStack, - "movl 4(%esp),%ecx\n\t" /* func */ - "movl 8(%esp),%edx\n\t" /* arg */ - ".byte 0x64\n\tmovl 0x04,%esp\n\t" /* teb->Tib.StackBase */ - "pushl %edx\n\t" - "xorl %ebp,%ebp\n\t" - "call *%ecx\n\t" - "int $3" /* we never return here */ ); -# elif defined(_MSC_VER) -__declspec(naked) void SYSDEPS_SwitchToThreadStack( void (*func)(void *), void *arg ) -{ - __asm mov ecx, 4[esp]; - __asm mov edx, 8[esp]; - __asm mov fs:[0x04], esp; - __asm push edx; - __asm xor ebp, ebp; - __asm call [ecx]; - __asm int 3; -} -# endif /* defined(__GNUC__) || defined(_MSC_VER) */ -#elif defined(__sparc__) && defined(__GNUC__) -__ASM_GLOBAL_FUNC( SYSDEPS_SwitchToThreadStack, - "mov %o0, %l0\n\t" /* store first argument */ - "call " __ASM_NAME("NtCurrentTeb") ", 0\n\t" - "mov %o1, %l1\n\t" /* delay slot: store second argument */ - "ld [%o0+4], %sp\n\t" /* teb->Tib.StackBase */ - "call %l0, 0\n\t" /* call func */ - "mov %l1, %o0\n\t" /* delay slot: arg for func */ - "ta 0x01\n\t"); /* breakpoint - we never get here */ -#elif defined(__powerpc__) && defined(__APPLE__) -/* Darwin SYSDEPS_SwitchToThreadStack - Function Pointer to call is on r3, Args to pass on r4 and stack on r1 */ -__ASM_GLOBAL_FUNC( SYSDEPS_SwitchToThreadStack, - "stw r1, 0x4(r13)\n\t" /* teb->Tib.StackBase */ - "mr r12,r3\n\t" - "mtctr r12\n\t" /* func->ctr */ - "mr r3,r4\n\t" /* args->function param 1 (r3) */ - "bctr\n\t" /* call ctr */ - "b _SYSDEPS_SwitchToThreadStack+24\n\t"); /* loop */ -#elif defined(__powerpc__) && defined(__GNUC__) -/* Linux SYSDEPS_SwitchToThreadStack - Function Pointer to call is on r3, Args to pass on r4 and stack on r1 */ -__ASM_GLOBAL_FUNC( SYSDEPS_SwitchToThreadStack, - "stw 1, 0x4(13)\n\t" /* teb->Tib.StackBase */ - "mr 12,3\n\t" - "mtctr 12\n\t" /* func->ctr */ - "mr 3,4\n\t" /* args->function param 1 (r3) */ - "bctr\n\t" /* call ctr */ - "b _SYSDEPS_SwitchToThreadStack+24\n\t"); /* loop */ -#else /* !powerpc, !sparc, !i386 */ -void SYSDEPS_SwitchToThreadStack( void (*func)(void *), void *arg ) -{ - func( arg ); - while(1); /* avoid warning */ -} -#endif /* !defined(__i386__) && !defined(__sparc__) */ - - /*********************************************************************** * SYSDEPS_ExitThread * @@ -285,7 +218,6 @@ void SYSDEPS_ExitThread( int status ) ptr = free_teb->DeallocationStack; NtFreeVirtualMemory( GetCurrentProcess(), &ptr, &size, MEM_RELEASE ); } - SIGNAL_Block(); SYSDEPS_AbortThread( status ); #else struct thread_cleanup_info info; @@ -305,9 +237,7 @@ void SYSDEPS_ExitThread( int status ) close( teb->reply_fd ); close( teb->request_fd ); SIGNAL_Reset(); - teb->Tib.StackLimit = get_temp_stack(); - teb->Tib.StackBase = (char *)teb->Tib.StackLimit + TEMP_STACK_SIZE; - SYSDEPS_SwitchToThreadStack( cleanup_thread, &info ); + wine_switch_to_stack( cleanup_thread, &info, get_temp_stack() ); #endif } diff --git a/include/thread.h b/include/thread.h index f163977bc57..c1fa03e0fa2 100644 --- a/include/thread.h +++ b/include/thread.h @@ -149,7 +149,6 @@ extern void SYSDEPS_SetCurThread( TEB *teb ); extern int SYSDEPS_GetUnixTid(void); extern void DECLSPEC_NORETURN SYSDEPS_ExitThread( int status ); extern void DECLSPEC_NORETURN SYSDEPS_AbortThread( int status ); -extern void DECLSPEC_NORETURN SYSDEPS_SwitchToThreadStack( void (*func)(void *), void *arg ); /* signal handling */ extern BOOL SIGNAL_Init(void); diff --git a/include/wine/library.h b/include/wine/library.h index 2cd1aca58cf..b501bf46f2b 100644 --- a/include/wine/library.h +++ b/include/wine/library.h @@ -61,12 +61,9 @@ extern int wine_dbg_parse_options( const char *str ); /* portability */ +extern void DECLSPEC_NORETURN wine_switch_to_stack( void (*func)(void *), void *arg, void *stack ); extern void *wine_anon_mmap( void *start, size_t size, int prot, int flags ); -/* errno support */ -extern int* (*wine_errno_location)(void); -extern int* (*wine_h_errno_location)(void); - /* LDT management */ extern void wine_ldt_init_locking( void (*lock_func)(void), void (*unlock_func)(void) ); diff --git a/libs/wine/Makefile.in b/libs/wine/Makefile.in index 3a1b0400261..65a6051a625 100644 --- a/libs/wine/Makefile.in +++ b/libs/wine/Makefile.in @@ -11,7 +11,8 @@ C_SRCS = \ config.c \ debug.c \ ldt.c \ - loader.c + loader.c \ + port.c @MAKE_LIB_RULES@ diff --git a/libs/wine/loader.c b/libs/wine/loader.c index efc7a7e94d0..93a1beb28f3 100644 --- a/libs/wine/loader.c +++ b/libs/wine/loader.c @@ -33,9 +33,6 @@ #ifdef HAVE_UNISTD_H # include #endif -#ifdef HAVE_STDINT_H -# include -#endif #define NONAMELESSUNION #define NONAMELESSSTRUCT @@ -428,134 +425,6 @@ void wine_init( int argc, char *argv[], char *error, int error_size ) } -#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 -} - - /* * These functions provide wrappers around dlopen() and associated * functions. They work around a bug in glibc 2.1.x where calling diff --git a/libs/wine/port.c b/libs/wine/port.c new file mode 100644 index 00000000000..8a6f97b3a43 --- /dev/null +++ b/libs/wine/port.c @@ -0,0 +1,220 @@ +/* + * 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 +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#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 +} diff --git a/scheduler/fiber.c b/scheduler/fiber.c index de73fdf032e..dfc93aa78f1 100644 --- a/scheduler/fiber.c +++ b/scheduler/fiber.c @@ -27,6 +27,7 @@ #include "winbase.h" #include "winerror.h" #include "wine/exception.h" +#include "wine/library.h" #include "thread.h" struct fiber_data @@ -190,7 +191,7 @@ void WINAPI SwitchToFiber( LPVOID fiber ) NtCurrentTeb()->Tib.StackLimit = new_fiber->stack_limit; NtCurrentTeb()->DeallocationStack = new_fiber->stack_allocation; if (new_fiber->start) /* first time */ - SYSDEPS_SwitchToThreadStack( start_fiber, new_fiber ); + wine_switch_to_stack( start_fiber, new_fiber, new_fiber->stack_base ); else longjmp( new_fiber->jmpbuf, 1 ); } diff --git a/scheduler/process.c b/scheduler/process.c index cb9c9cbd7d5..d593f0686de 100644 --- a/scheduler/process.c +++ b/scheduler/process.c @@ -586,7 +586,7 @@ void __wine_process_init( int argc, char *argv[] ) if (!THREAD_InitStack( NtCurrentTeb(), stack_size )) goto error; /* switch to the new stack */ - SYSDEPS_SwitchToThreadStack( start_process, NULL ); + wine_switch_to_stack( start_process, NULL, NtCurrentTeb()->Tib.StackBase ); error: ExitProcess( GetLastError() );