Allocate the TEB and signal stack separately from the main stack.

Dynamically allocate the initial TEB too so that it is properly
page-aligned.
This commit is contained in:
Alexandre Julliard 2003-11-04 04:50:18 +00:00
parent bcb09c198d
commit 7924f421e9
9 changed files with 179 additions and 147 deletions

View file

@ -55,32 +55,22 @@ WINE_DECLARE_DEBUG_CHANNEL(relay);
*/
TEB *THREAD_InitStack( TEB *teb, DWORD stack_size )
{
DWORD old_prot, total_size;
DWORD old_prot;
DWORD page_size = getpagesize();
void *base;
/* Memory layout in allocated block:
*
* size contents
* SIGNAL_STACK_SIZE signal stack
* stack_size normal stack (including a PAGE_GUARD page at the bottom)
* 1 page TEB (except for initial thread)
*/
stack_size = (stack_size + (page_size - 1)) & ~(page_size - 1);
total_size = stack_size + SIGNAL_STACK_SIZE;
if (!(base = VirtualAlloc( NULL, total_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE )))
if (!(base = VirtualAlloc( NULL, stack_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE )))
return NULL;
teb->DeallocationStack = base;
teb->Tib.StackBase = (char *)base + SIGNAL_STACK_SIZE + stack_size;
teb->Tib.StackBase = (char *)base + stack_size;
teb->Tib.StackLimit = base; /* note: limit is lower than base since the stack grows down */
/* Setup guard pages */
VirtualProtect( (char *)base + SIGNAL_STACK_SIZE, 1,
PAGE_EXECUTE_READWRITE | PAGE_GUARD, &old_prot );
VirtualProtect( base, 1, PAGE_EXECUTE_READWRITE | PAGE_GUARD, &old_prot );
return teb;
}

View file

@ -1933,16 +1933,7 @@ void __wine_process_init( int argc, char *argv[] )
ANSI_STRING func_name;
void (* DECLSPEC_NORETURN init_func)();
/* setup the server connection */
wine_server_init_process();
wine_server_init_thread();
/* create the process heap */
if (!(NtCurrentTeb()->Peb->ProcessHeap = RtlCreateHeap( HEAP_GROWABLE, NULL, 0, 0, NULL, NULL )))
{
MESSAGE( "wine: failed to create the process heap\n" );
exit(1);
}
thread_init();
/* setup the load callback and create ntdll modref */
wine_dll_set_callback( load_builtin_callback );

View file

@ -31,6 +31,13 @@
#include "thread.h"
#include "wine/server.h"
/* The per-thread signal stack size */
#ifdef __i386__
#define SIGNAL_STACK_SIZE 4096
#else
#define SIGNAL_STACK_SIZE 0 /* we don't need a signal stack on non-i386 */
#endif
/* debug helper */
extern LPCSTR debugstr_us( const UNICODE_STRING *str );
extern void dump_ObjectAttributes (const OBJECT_ATTRIBUTES *ObjectAttributes);
@ -38,7 +45,11 @@ extern void dump_ObjectAttributes (const OBJECT_ATTRIBUTES *ObjectAttributes);
extern void NTDLL_get_server_timeout( abs_time_t *when, const LARGE_INTEGER *timeout );
extern NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UINT flags,
const LARGE_INTEGER *timeout );
/* init routines */
extern void wine_server_init_process(void);
extern void wine_server_init_thread(void);
extern void thread_init(void);
/* module handling */
extern BOOL MODULE_GetSystemDirectory( UNICODE_STRING *sysdir );

View file

@ -466,7 +466,7 @@ static inline int get_error_code( const SIGCONTEXT *sigcontext )
*/
static inline void *get_signal_stack(void)
{
return NtCurrentTeb()->DeallocationStack;
return (char *)NtCurrentTeb() + 4096;
}
@ -831,19 +831,20 @@ static EXCEPTION_RECORD *setup_exception( SIGCONTEXT *sigcontext, raise_func fun
SYSDEPS_AbortThread(1);
}
if ((char *)(stack - 1) < (char *)NtCurrentTeb()->Tib.StackLimit + SIGNAL_STACK_SIZE + 4096 ||
if ((char *)(stack - 1) < (char *)NtCurrentTeb()->Tib.StackLimit + 4096 ||
(char *)stack > (char *)NtCurrentTeb()->Tib.StackBase)
{
UINT diff = (char *)NtCurrentTeb()->Tib.StackLimit + SIGNAL_STACK_SIZE + 4096 - (char *)stack;
UINT diff = (char *)NtCurrentTeb()->Tib.StackLimit + 4096 - (char *)stack;
if (diff < 4096)
{
ERR( "stack overflow %u bytes in thread %04lx eip %08lx esp %08lx stack %p-%p\n",
diff, GetCurrentThreadId(), EIP_sig(sigcontext), ESP_sig(sigcontext),
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
else
ERR( "exception outside of stack limits in thread %04lx eip %08lx esp %08lx stack %p-%p\n",
GetCurrentThreadId(), EIP_sig(sigcontext), ESP_sig(sigcontext),
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
SYSDEPS_AbortThread(1);
SYSDEPS_AbortThread(1);
}
else WARN( "exception outside of stack limits in thread %04lx eip %08lx esp %08lx stack %p-%p\n",
GetCurrentThreadId(), EIP_sig(sigcontext), ESP_sig(sigcontext),
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
}
stack--; /* push the stack_layout structure */

View file

@ -58,7 +58,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(thread);
struct thread_cleanup_info
{
void *stack_base;
int stack_size;
ULONG stack_size;
void *teb_base;
ULONG teb_size;
int status;
};
@ -126,6 +128,7 @@ static void cleanup_thread( void *ptr )
/* copy the info structure since it is on the stack we will free */
struct thread_cleanup_info info = *(struct thread_cleanup_info *)ptr;
munmap( info.stack_base, info.stack_size );
munmap( info.teb_base, info.teb_size );
wine_ldt_free_fs( wine_get_fs() );
#ifdef HAVE__LWP_CREATE
_lwp_exit();
@ -196,15 +199,13 @@ int SYSDEPS_SpawnThread( void (*func)(TEB *), TEB *teb )
*/
void SYSDEPS_ExitThread( int status )
{
TEB *teb = NtCurrentTeb();
DWORD size = 0;
#ifdef HAVE_NPTL
static TEB *teb_to_free;
TEB *free_teb;
if ((free_teb = interlocked_xchg_ptr( (void **)&teb_to_free, teb )) != NULL)
if ((free_teb = interlocked_xchg_ptr( (void **)&teb_to_free, NtCurrentTeb() )) != NULL)
{
DWORD size = 0;
void *ptr;
TRACE("freeing prev teb %p stack %p fs %04x\n",
@ -214,26 +215,28 @@ void SYSDEPS_ExitThread( int status )
wine_ldt_free_fs( free_teb->teb_sel );
ptr = free_teb->DeallocationStack;
NtFreeVirtualMemory( GetCurrentProcess(), &ptr, &size, MEM_RELEASE );
ptr = free_teb;
NtFreeVirtualMemory( GetCurrentProcess(), &ptr, &size, MEM_RELEASE | MEM_SYSTEM );
munmap( ptr, size );
}
SYSDEPS_AbortThread( status );
#else
struct thread_cleanup_info info;
MEMORY_BASIC_INFORMATION meminfo;
NtQueryVirtualMemory( GetCurrentProcess(), teb->Tib.StackBase, MemoryBasicInformation,
&meminfo, sizeof(meminfo), NULL );
info.stack_base = meminfo.AllocationBase;
info.stack_size = meminfo.RegionSize + ((char *)teb->Tib.StackBase - (char *)meminfo.AllocationBase);
info.status = status;
SIGNAL_Block();
size = 0;
NtFreeVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, &size, MEM_RELEASE | MEM_SYSTEM );
close( teb->wait_fd[0] );
close( teb->wait_fd[1] );
close( teb->reply_fd );
close( teb->request_fd );
SIGNAL_Reset();
info.status = status;
info.stack_base = NtCurrentTeb()->DeallocationStack;
info.stack_size = 0;
NtFreeVirtualMemory( GetCurrentProcess(), &info.stack_base,
&info.stack_size, MEM_RELEASE | MEM_SYSTEM );
info.teb_base = NtCurrentTeb();
info.teb_size = 0;
NtFreeVirtualMemory( GetCurrentProcess(), &info.teb_base,
&info.teb_size, MEM_RELEASE | MEM_SYSTEM );
close( NtCurrentTeb()->wait_fd[0] );
close( NtCurrentTeb()->wait_fd[1] );
close( NtCurrentTeb()->reply_fd );
close( NtCurrentTeb()->request_fd );
wine_switch_to_stack( cleanup_thread, &info, get_temp_stack() );
#endif
}

View file

@ -21,15 +21,68 @@
#include "config.h"
#include "wine/port.h"
#include <sys/types.h>
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include "ntstatus.h"
#include "thread.h"
#include "winternl.h"
#include "wine/library.h"
#include "wine/server.h"
#include "wine/debug.h"
#include "ntdll_misc.h"
WINE_DEFAULT_DEBUG_CHANNEL(thread);
static PEB peb;
static PEB_LDR_DATA ldr;
static RTL_USER_PROCESS_PARAMETERS params; /* default parameters if no parent */
static RTL_BITMAP tls_bitmap;
static LIST_ENTRY tls_links;
static struct debug_info info; /* debug info for initial thread */
/***********************************************************************
* alloc_teb
*/
static TEB *alloc_teb( ULONG *size )
{
TEB *teb;
*size = SIGNAL_STACK_SIZE + sizeof(TEB);
teb = wine_anon_mmap( NULL, *size, PROT_READ | PROT_WRITE | PROT_EXEC, 0 );
if (teb == (TEB *)-1) return NULL;
if (!(teb->teb_sel = wine_ldt_alloc_fs()))
{
munmap( teb, *size );
return NULL;
}
teb->Tib.ExceptionList = (void *)~0UL;
teb->Tib.Self = &teb->Tib;
teb->Peb = &peb;
teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer;
teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer);
return teb;
}
/***********************************************************************
* free_teb
*/
static inline void free_teb( TEB *teb )
{
ULONG size = 0;
void *addr = teb;
if (teb->DeallocationStack)
NtFreeVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, &size, MEM_RELEASE );
NtFreeVirtualMemory( GetCurrentProcess(), &addr, &size, MEM_RELEASE );
wine_ldt_free_fs( teb->teb_sel );
munmap( teb, SIGNAL_STACK_SIZE + sizeof(TEB) );
}
/***********************************************************************
* thread_init
@ -38,35 +91,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(thread);
*
* NOTES: The first allocated TEB on NT is at 0x7ffde000.
*/
DECL_GLOBAL_CONSTRUCTOR(thread_init)
void thread_init(void)
{
static TEB teb;
static PEB peb;
static PEB_LDR_DATA ldr;
static RTL_USER_PROCESS_PARAMETERS params; /* default parameters if no parent */
static RTL_BITMAP tls_bitmap;
static struct debug_info info; /* debug info for initial thread */
if (teb.Tib.Self) return; /* do it only once */
TEB *teb;
void *addr;
ULONG size;
info.str_pos = info.strings;
info.out_pos = info.output;
teb.Tib.ExceptionList = (void *)~0UL;
teb.Tib.StackBase = (void *)~0UL;
teb.Tib.Self = &teb.Tib;
teb.Peb = &peb;
teb.tibflags = TEBF_WIN32;
teb.request_fd = -1;
teb.reply_fd = -1;
teb.wait_fd[0] = -1;
teb.wait_fd[1] = -1;
teb.teb_sel = wine_ldt_alloc_fs();
teb.debug_info = &info;
teb.StaticUnicodeString.MaximumLength = sizeof(teb.StaticUnicodeBuffer);
teb.StaticUnicodeString.Buffer = teb.StaticUnicodeBuffer;
InitializeListHead( &teb.TlsLinks );
peb.ProcessParameters = &params;
peb.TlsBitmap = &tls_bitmap;
peb.LdrData = &ldr;
@ -74,12 +107,42 @@ DECL_GLOBAL_CONSTRUCTOR(thread_init)
InitializeListHead( &ldr.InLoadOrderModuleList );
InitializeListHead( &ldr.InMemoryOrderModuleList );
InitializeListHead( &ldr.InInitializationOrderModuleList );
InitializeListHead( &tls_links );
SYSDEPS_SetCurThread( &teb );
teb = alloc_teb( &size );
teb->Tib.StackBase = (void *)~0UL;
teb->tibflags = TEBF_WIN32;
teb->request_fd = -1;
teb->reply_fd = -1;
teb->wait_fd[0] = -1;
teb->wait_fd[1] = -1;
teb->debug_info = &info;
InsertHeadList( &tls_links, &teb->TlsLinks );
SYSDEPS_SetCurThread( teb );
/* setup the server connection */
wine_server_init_process();
wine_server_init_thread();
/* create a memory view for the TEB */
NtAllocateVirtualMemory( GetCurrentProcess(), &addr, teb, &size,
MEM_SYSTEM, PAGE_EXECUTE_READWRITE );
/* create the process heap */
if (!(peb.ProcessHeap = RtlCreateHeap( HEAP_GROWABLE, NULL, 0, 0, NULL, NULL )))
{
MESSAGE( "wine: failed to create the process heap\n" );
exit(1);
}
}
/* startup routine for a newly created thread */
/***********************************************************************
* start_thread
*
* Startup routine for a newly created thread.
*/
static void start_thread( TEB *teb )
{
LPTHREAD_START_ROUTINE func = (LPTHREAD_START_ROUTINE)teb->entry_point;
@ -93,6 +156,10 @@ static void start_thread( TEB *teb )
SIGNAL_Init();
wine_server_init_thread();
RtlAcquirePebLock();
InsertHeadList( &tls_links, &teb->TlsLinks );
RtlReleasePebLock();
NtTerminateThread( GetCurrentThread(), func( NtCurrentTeb()->entry_arg ) );
}
@ -107,11 +174,10 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
HANDLE *handle_ptr, CLIENT_ID *id )
{
HANDLE handle = 0;
TEB *teb;
TEB *teb = NULL;
DWORD tid = 0;
SIZE_T total_size;
SIZE_T page_size = getpagesize();
void *ptr, *base = NULL;
ULONG size;
void *base;
int request_pipe[2];
NTSTATUS status;
@ -135,49 +201,13 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
if (status) goto error;
if (!stack_reserve || !stack_commit)
if (!(teb = alloc_teb( &size )))
{
IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress );
if (!stack_reserve) stack_reserve = nt->OptionalHeader.SizeOfStackReserve;
if (!stack_commit) stack_commit = nt->OptionalHeader.SizeOfStackCommit;
}
if (stack_reserve < stack_commit) stack_reserve = stack_commit;
stack_reserve = (stack_reserve + 0xffff) & ~0xffff; /* round to 64K boundary */
/* Memory layout in allocated block:
*
* size contents
* SIGNAL_STACK_SIZE signal stack
* stack_size normal stack (including a PAGE_GUARD page at the bottom)
* 1 page TEB (except for initial thread)
*/
total_size = stack_reserve + SIGNAL_STACK_SIZE + page_size;
if ((status = NtAllocateVirtualMemory( GetCurrentProcess(), &base, NULL, &total_size,
MEM_COMMIT, PAGE_EXECUTE_READWRITE )) != STATUS_SUCCESS)
goto error;
teb = (TEB *)((char *)base + total_size - page_size);
if (!(teb->teb_sel = wine_ldt_alloc_fs()))
{
status = STATUS_TOO_MANY_THREADS;
status = STATUS_NO_MEMORY;
goto error;
}
teb->Tib.ExceptionList = (void *)~0UL;
teb->Tib.StackBase = (char *)base + SIGNAL_STACK_SIZE + stack_reserve;
teb->Tib.StackLimit = base; /* limit is lower than base since the stack grows down */
teb->Tib.Self = &teb->Tib;
teb->ClientId.UniqueProcess = (HANDLE)GetCurrentProcessId();
teb->ClientId.UniqueThread = (HANDLE)tid;
teb->Peb = NtCurrentTeb()->Peb;
teb->DeallocationStack = base;
teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer;
teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer);
RtlAcquirePebLock();
InsertHeadList( &NtCurrentTeb()->TlsLinks, &teb->TlsLinks );
RtlReleasePebLock();
teb->ClientId.UniqueProcess = (HANDLE)GetCurrentProcessId();
teb->ClientId.UniqueThread = (HANDLE)tid;
teb->tibflags = TEBF_WIN32;
teb->exit_code = STILL_ACTIVE;
@ -189,17 +219,33 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
teb->entry_arg = param;
teb->htask16 = NtCurrentTeb()->htask16;
NtAllocateVirtualMemory( GetCurrentProcess(), &base, teb, &size,
MEM_SYSTEM, PAGE_EXECUTE_READWRITE );
if (!stack_reserve || !stack_commit)
{
IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress );
if (!stack_reserve) stack_reserve = nt->OptionalHeader.SizeOfStackReserve;
if (!stack_commit) stack_commit = nt->OptionalHeader.SizeOfStackCommit;
}
if (stack_reserve < stack_commit) stack_reserve = stack_commit;
stack_reserve = (stack_reserve + 0xffff) & ~0xffff; /* round to 64K boundary */
status = NtAllocateVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, NULL,
&stack_reserve, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if (status != STATUS_SUCCESS) goto error;
/* limit is lower than base since the stack grows down */
teb->Tib.StackBase = (char *)teb->DeallocationStack + stack_reserve;
teb->Tib.StackLimit = teb->DeallocationStack;
/* setup the guard page */
ptr = (char *)base + SIGNAL_STACK_SIZE;
NtProtectVirtualMemory( GetCurrentProcess(), &ptr, &page_size,
size = 1;
NtProtectVirtualMemory( GetCurrentProcess(), &teb->DeallocationStack, &size,
PAGE_EXECUTE_READWRITE | PAGE_GUARD, NULL );
if (SYSDEPS_SpawnThread( start_thread, teb ) == -1)
{
RtlAcquirePebLock();
RemoveEntryList( &teb->TlsLinks );
RtlReleasePebLock();
wine_ldt_free_fs( teb->teb_sel );
status = STATUS_TOO_MANY_THREADS;
goto error;
}
@ -211,11 +257,7 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
return STATUS_SUCCESS;
error:
if (base)
{
total_size = 0;
NtFreeVirtualMemory( GetCurrentProcess(), &base, &total_size, MEM_RELEASE );
}
if (teb) free_teb( teb );
if (handle) NtClose( handle );
close( request_pipe[1] );
return status;
@ -444,19 +486,18 @@ NTSTATUS WINAPI NtSetInformationThread( HANDLE handle, THREADINFOCLASS class,
case ThreadZeroTlsCell:
if (handle == GetCurrentThread())
{
LIST_ENTRY *entry = &NtCurrentTeb()->TlsLinks;
LIST_ENTRY *entry;
DWORD index;
if (length != sizeof(DWORD)) return STATUS_INVALID_PARAMETER;
index = *(DWORD *)data;
if (index >= 64) return STATUS_INVALID_PARAMETER;
RtlAcquirePebLock();
do
for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink)
{
TEB *teb = CONTAINING_RECORD(entry, TEB, TlsLinks);
teb->TlsSlots[index] = 0;
entry = entry->Flink;
} while (entry != &NtCurrentTeb()->TlsLinks);
}
RtlReleasePebLock();
return STATUS_SUCCESS;
}

View file

@ -834,7 +834,7 @@ DWORD VIRTUAL_HandleFault( LPCVOID addr )
{
BYTE vprot = view->prot[((char *)addr - (char *)view->base) >> page_shift];
void *page = (void *)((UINT_PTR)addr & ~page_mask);
char *stack = (char *)NtCurrentTeb()->DeallocationStack + SIGNAL_STACK_SIZE;
char *stack = NtCurrentTeb()->Tib.StackLimit;
if (vprot & VPROT_GUARD)
{
VIRTUAL_SetProt( view, page, page_mask + 1, vprot & ~VPROT_GUARD );
@ -1101,8 +1101,12 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, ULONG *siz
if (type & MEM_SYSTEM)
{
/* return the values that the caller should use to unmap the area */
*addr_ptr = view->base;
*size_ptr = view->size;
view->flags |= VFLAG_SYSTEM;
type &= ~MEM_SYSTEM;
VIRTUAL_DeleteView( view );
return STATUS_SUCCESS;
}
if ((type != MEM_DECOMMIT) && (type != MEM_RELEASE))

View file

@ -140,14 +140,6 @@ typedef struct _TEB
#define TEBF_WIN32 0x0001
#define TEBF_TRAP 0x0002
/* The per-thread signal stack size */
#ifdef __i386__
#define SIGNAL_STACK_SIZE 0x100000 /* 1Mb FIXME: should be much smaller than that */
#else
#define SIGNAL_STACK_SIZE 0 /* we don't need a signal stack on non-i386 */
#endif
/* scheduler/thread.c */
extern TEB *THREAD_InitStack( TEB *teb, DWORD stack_size );

View file

@ -56,7 +56,6 @@ extern void wine_server_send_fd( int fd );
extern int wine_server_fd_to_handle( int fd, unsigned int access, int inherit, obj_handle_t *handle );
extern int wine_server_handle_to_fd( obj_handle_t handle, unsigned int access, int *unix_fd,
enum fd_type *type, int *flags );
extern void wine_server_init_thread(void);
/* do a server call and set the last error code */
inline static unsigned int wine_server_call_err( void *req_ptr )