* child_info.h (CURR_CHILD_INFO_MAGIC): Update.
(class child_info_fork): Remove stacksize, add stackaddr and guardsize members. * dcrt0.cc (child_info_fork::alloc_stack_hard_way): Partial rewrite to regenerate the stack exactly as in the parent. (child_info_fork::alloc_stack): Set stackaddr to 0, rather than stacksize. (dll_crt0_1): Check for stackaddr before changing the stack addresses in the TEB. * fork.cc (frok::child): Check for stackaddr here. (frok::parent): Set ch.stackaddr and ch.guardsize if not called from the main thread. * init.cc (dll_entry): Replace pointer to NT_TIB with pointer to TEB. Fix incorrectly changed address test before removing _my_tls. Set StackLimit to NULL on Windows 2000. Explain why. * miscfuncs.cc (struct thread_wrapper_arg): Store stackbase rather than stacksize, store commitaddr, remove guardsize. Store all pointers as char * for easier address arithmetic. (thread_wrapper): Rewrite to remove OS stack before calling thread function. Add lots of comments to explain what we do. (CygwinCreateThread): Reserve our own stack in case we got no application stack. Add comments. * ntdll.h (struct _TEB): Extend defintion up to DeallocationStack member. * thread.cc (pthread_attr::pthread_attr): Use "(size_t) -1" rather then 0xffffffff. * wincap.h (wincaps::has_stack_size_param_is_a_reservation): New element. * wincap.cc: Implement above element throughout.
This commit is contained in:
parent
660302eb67
commit
89d3c72d51
|
@ -1,3 +1,35 @@
|
||||||
|
2011-05-20 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
|
* child_info.h (CURR_CHILD_INFO_MAGIC): Update.
|
||||||
|
(class child_info_fork): Remove stacksize, add stackaddr and guardsize
|
||||||
|
members.
|
||||||
|
* dcrt0.cc (child_info_fork::alloc_stack_hard_way): Partial rewrite
|
||||||
|
to regenerate the stack exactly as in the parent.
|
||||||
|
(child_info_fork::alloc_stack): Set stackaddr to 0, rather than
|
||||||
|
stacksize.
|
||||||
|
(dll_crt0_1): Check for stackaddr before changing the stack addresses
|
||||||
|
in the TEB.
|
||||||
|
* fork.cc (frok::child): Check for stackaddr here.
|
||||||
|
(frok::parent): Set ch.stackaddr and ch.guardsize if not called from
|
||||||
|
the main thread.
|
||||||
|
* init.cc (dll_entry): Replace pointer to NT_TIB with pointer to TEB.
|
||||||
|
Fix incorrectly changed address test before removing _my_tls.
|
||||||
|
Set StackLimit to NULL on Windows 2000. Explain why.
|
||||||
|
* miscfuncs.cc (struct thread_wrapper_arg): Store stackbase rather
|
||||||
|
than stacksize, store commitaddr, remove guardsize. Store all pointers
|
||||||
|
as char * for easier address arithmetic.
|
||||||
|
(thread_wrapper): Rewrite to remove OS stack before calling thread
|
||||||
|
function. Add lots of comments to explain what we do.
|
||||||
|
(CygwinCreateThread): Reserve our own stack in case we got no
|
||||||
|
application stack. Add comments.
|
||||||
|
* ntdll.h (struct _TEB): Extend defintion up to DeallocationStack
|
||||||
|
member.
|
||||||
|
* thread.cc (pthread_attr::pthread_attr): Use "(size_t) -1"
|
||||||
|
rather then 0xffffffff.
|
||||||
|
* wincap.h (wincaps::has_stack_size_param_is_a_reservation): New
|
||||||
|
element.
|
||||||
|
* wincap.cc: Implement above element throughout.
|
||||||
|
|
||||||
2011-05-19 Yaakov Selkowitz <yselkowitz@users.sourceforge.net>
|
2011-05-19 Yaakov Selkowitz <yselkowitz@users.sourceforge.net>
|
||||||
|
|
||||||
* thread.cc: Mark psiginfo and psignal as available in list of
|
* thread.cc: Mark psiginfo and psignal as available in list of
|
||||||
|
|
|
@ -37,7 +37,7 @@ enum child_status
|
||||||
#define EXEC_MAGIC_SIZE sizeof(child_info)
|
#define EXEC_MAGIC_SIZE sizeof(child_info)
|
||||||
|
|
||||||
/* Change this value if you get a message indicating that it is out-of-sync. */
|
/* Change this value if you get a message indicating that it is out-of-sync. */
|
||||||
#define CURR_CHILD_INFO_MAGIC 0xe850717aU
|
#define CURR_CHILD_INFO_MAGIC 0xbdf5842aU
|
||||||
|
|
||||||
/* NOTE: Do not make gratuitous changes to the names or organization of the
|
/* NOTE: Do not make gratuitous changes to the names or organization of the
|
||||||
below class. The layout is checksummed to determine compatibility between
|
below class. The layout is checksummed to determine compatibility between
|
||||||
|
@ -80,10 +80,12 @@ class child_info_fork: public child_info
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HANDLE forker_finished;// for synchronization with child
|
HANDLE forker_finished;// for synchronization with child
|
||||||
DWORD stacksize; // size of parent stack
|
|
||||||
jmp_buf jmp; // where child will jump to
|
jmp_buf jmp; // where child will jump to
|
||||||
|
void *stackaddr; // address of parent stack
|
||||||
void *stacktop; // location of top of parent stack
|
void *stacktop; // location of top of parent stack
|
||||||
void *stackbottom; // location of bottom of parent stack
|
void *stackbottom; // location of bottom of parent stack
|
||||||
|
size_t guardsize; // size of POSIX guard region or (size_t) -1 if
|
||||||
|
// user stack
|
||||||
char filler[4];
|
char filler[4];
|
||||||
child_info_fork ();
|
child_info_fork ();
|
||||||
void handle_fork () __attribute__ ((regparm (1)));;
|
void handle_fork () __attribute__ ((regparm (1)));;
|
||||||
|
|
|
@ -392,14 +392,11 @@ child_info NO_COPY *child_proc_info = NULL;
|
||||||
void
|
void
|
||||||
child_info_fork::alloc_stack_hard_way (volatile char *b)
|
child_info_fork::alloc_stack_hard_way (volatile char *b)
|
||||||
{
|
{
|
||||||
void *new_stack_pointer;
|
void *stack_ptr;
|
||||||
MEMORY_BASIC_INFORMATION m;
|
DWORD stacksize;
|
||||||
void *newbase;
|
|
||||||
int newlen;
|
|
||||||
bool guard;
|
|
||||||
|
|
||||||
/* First check if the requested stack area is part of the user heap
|
/* First check if the requested stack area is part of the user heap
|
||||||
or part of a mmaped region. If so, we have been started from a
|
or part of a mmapped region. If so, we have been started from a
|
||||||
pthread with an application-provided stack, and the stack has just
|
pthread with an application-provided stack, and the stack has just
|
||||||
to be used as is. */
|
to be used as is. */
|
||||||
if ((stacktop >= cygheap->user_heap.base
|
if ((stacktop >= cygheap->user_heap.base
|
||||||
|
@ -407,46 +404,33 @@ child_info_fork::alloc_stack_hard_way (volatile char *b)
|
||||||
|| is_mmapped_region ((caddr_t) stacktop, (caddr_t) stackbottom))
|
|| is_mmapped_region ((caddr_t) stacktop, (caddr_t) stackbottom))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!VirtualQuery ((LPCVOID) &b, &m, sizeof m))
|
/* First, try to reserve the entire stack. */
|
||||||
api_fatal ("fork: couldn't get stack info, %E");
|
stacksize = (char *) stackbottom - (char *) stackaddr;
|
||||||
|
if (!VirtualAlloc (stackaddr, stacksize, MEM_RESERVE, PAGE_NOACCESS))
|
||||||
LPBYTE curbot = (LPBYTE) m.BaseAddress + m.RegionSize;
|
|
||||||
|
|
||||||
if (stacktop > (LPBYTE) m.AllocationBase && stacktop < curbot)
|
|
||||||
{
|
|
||||||
newbase = curbot;
|
|
||||||
newlen = (LPBYTE) stackbottom - (LPBYTE) curbot;
|
|
||||||
guard = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newbase = (LPBYTE) stacktop - (128 * 1024);
|
|
||||||
newlen = (LPBYTE) stackbottom - (LPBYTE) newbase;
|
|
||||||
guard = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!VirtualAlloc (newbase, newlen, MEM_RESERVE, PAGE_NOACCESS))
|
|
||||||
api_fatal ("fork: can't reserve memory for stack %p - %p, %E",
|
api_fatal ("fork: can't reserve memory for stack %p - %p, %E",
|
||||||
stacktop, stackbottom);
|
stackaddr, stackbottom);
|
||||||
new_stack_pointer = (void *) ((LPBYTE) stackbottom - (stacksize += 8192));
|
stacksize = (char *) stackbottom - (char *) stacktop;
|
||||||
if (!VirtualAlloc (new_stack_pointer, stacksize, MEM_COMMIT,
|
stack_ptr = VirtualAlloc (stacktop, stacksize, MEM_COMMIT,
|
||||||
PAGE_EXECUTE_READWRITE))
|
PAGE_EXECUTE_READWRITE);
|
||||||
|
if (!stack_ptr)
|
||||||
api_fatal ("fork: can't commit memory for stack %p(%d), %E",
|
api_fatal ("fork: can't commit memory for stack %p(%d), %E",
|
||||||
new_stack_pointer, stacksize);
|
stacktop, stacksize);
|
||||||
if (!VirtualQuery ((LPCVOID) new_stack_pointer, &m, sizeof m))
|
if (guardsize != (size_t) -1)
|
||||||
api_fatal ("fork: couldn't get new stack info, %E");
|
|
||||||
|
|
||||||
if (guard)
|
|
||||||
{
|
{
|
||||||
m.BaseAddress = (LPBYTE) m.BaseAddress - 1;
|
/* Allocate PAGE_GUARD page if it still fits. */
|
||||||
if (!VirtualAlloc ((LPVOID) m.BaseAddress, 1, MEM_COMMIT,
|
if (stack_ptr > stackaddr)
|
||||||
|
{
|
||||||
|
stack_ptr = (void *) ((LPBYTE) stack_ptr
|
||||||
|
- wincap.page_size ());
|
||||||
|
if (!VirtualAlloc (stack_ptr, wincap.page_size (), MEM_COMMIT,
|
||||||
CYGWIN_GUARD))
|
CYGWIN_GUARD))
|
||||||
api_fatal ("fork: couldn't allocate new stack guard page %p, %E",
|
api_fatal ("fork: couldn't allocate new stack guard page %p, %E",
|
||||||
m.BaseAddress);
|
stack_ptr);
|
||||||
|
}
|
||||||
|
/* Allocate POSIX guard pages. */
|
||||||
|
if (guardsize > 0)
|
||||||
|
VirtualAlloc (stackaddr, guardsize, MEM_COMMIT, PAGE_NOACCESS);
|
||||||
}
|
}
|
||||||
if (!VirtualQuery ((LPCVOID) m.BaseAddress, &m, sizeof m))
|
|
||||||
api_fatal ("fork: couldn't get new stack info, %E");
|
|
||||||
stacktop = m.BaseAddress;
|
|
||||||
b[0] = '\0';
|
b[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,7 +457,7 @@ child_info_fork::alloc_stack ()
|
||||||
char *st = (char *) stacktop - 4096;
|
char *st = (char *) stacktop - 4096;
|
||||||
while (_tlstop >= st)
|
while (_tlstop >= st)
|
||||||
esp = getstack (esp);
|
esp = getstack (esp);
|
||||||
stacksize = 0;
|
stackaddr = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -811,7 +795,7 @@ dll_crt0_1 (void *)
|
||||||
|
|
||||||
NOTE: Don't do anything that involves the stack until you've completed
|
NOTE: Don't do anything that involves the stack until you've completed
|
||||||
this step. */
|
this step. */
|
||||||
if (fork_info->stacksize)
|
if (fork_info->stackaddr)
|
||||||
{
|
{
|
||||||
_tlsbase = (char *) fork_info->stackbottom;
|
_tlsbase = (char *) fork_info->stackbottom;
|
||||||
_tlstop = (char *) fork_info->stacktop;
|
_tlstop = (char *) fork_info->stacktop;
|
||||||
|
|
|
@ -25,6 +25,7 @@ details. */
|
||||||
#include "tls_pbuf.h"
|
#include "tls_pbuf.h"
|
||||||
#include "dll_init.h"
|
#include "dll_init.h"
|
||||||
#include "cygmalloc.h"
|
#include "cygmalloc.h"
|
||||||
|
#include "ntdll.h"
|
||||||
|
|
||||||
#define NPIDS_HELD 4
|
#define NPIDS_HELD 4
|
||||||
|
|
||||||
|
@ -178,7 +179,7 @@ frok::child (volatile char * volatile here)
|
||||||
/* If we've played with the stack, stacksize != 0. That means that
|
/* If we've played with the stack, stacksize != 0. That means that
|
||||||
fork() was invoked from other than the main thread. Make sure that
|
fork() was invoked from other than the main thread. Make sure that
|
||||||
the threadinfo information is properly set up. */
|
the threadinfo information is properly set up. */
|
||||||
if (fork_info->stacksize)
|
if (fork_info->stackaddr)
|
||||||
{
|
{
|
||||||
_main_tls = &_my_tls;
|
_main_tls = &_my_tls;
|
||||||
_main_tls->init_thread (NULL, NULL);
|
_main_tls->init_thread (NULL, NULL);
|
||||||
|
@ -327,10 +328,33 @@ frok::parent (volatile char * volatile stack_here)
|
||||||
ch.forker_finished = forker_finished;
|
ch.forker_finished = forker_finished;
|
||||||
|
|
||||||
ch.stackbottom = _tlsbase;
|
ch.stackbottom = _tlsbase;
|
||||||
ch.stacktop = (void *) stack_here;
|
ch.stacktop = (void *) _tlstop;
|
||||||
ch.stacksize = (char *) ch.stackbottom - (char *) stack_here;
|
ch.stackaddr = 0;
|
||||||
debug_printf ("stack - bottom %p, top %p, size %d",
|
ch.guardsize = 0;
|
||||||
ch.stackbottom, ch.stacktop, ch.stacksize);
|
if (&_my_tls != _main_tls)
|
||||||
|
{
|
||||||
|
/* We have not been started from the main thread. Fetch the
|
||||||
|
information required to set up the thread stack identically
|
||||||
|
in the child. */
|
||||||
|
PTEB teb = NtCurrentTeb ();
|
||||||
|
if (!teb->DeallocationStack)
|
||||||
|
{
|
||||||
|
/* Pthread with application-provided stack. Don't set up a
|
||||||
|
PAGE_GUARD page. guardsize == -1 is used in alloc_stack_hard_way
|
||||||
|
to recognize this type of stack. */
|
||||||
|
ch.stackaddr = _my_tls.tid->attr.stackaddr;
|
||||||
|
ch.guardsize = (size_t) -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ch.stackaddr = teb->DeallocationStack;
|
||||||
|
/* If it's a pthread, fetch guardsize from thread attributes. */
|
||||||
|
if (_my_tls.tid)
|
||||||
|
ch.guardsize = _my_tls.tid->attr.guardsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug_printf ("stack - bottom %p, top %p, addr %p, guardsize %p",
|
||||||
|
ch.stackbottom, ch.stacktop, ch.stackaddr, ch.guardsize);
|
||||||
|
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
STARTUPINFOW si;
|
STARTUPINFOW si;
|
||||||
|
|
|
@ -115,7 +115,7 @@ extern void __stdcall dll_crt0_0 ();
|
||||||
extern "C" BOOL WINAPI
|
extern "C" BOOL WINAPI
|
||||||
dll_entry (HANDLE h, DWORD reason, void *static_load)
|
dll_entry (HANDLE h, DWORD reason, void *static_load)
|
||||||
{
|
{
|
||||||
PNT_TIB tib;
|
PTEB teb;
|
||||||
|
|
||||||
switch (reason)
|
switch (reason)
|
||||||
{
|
{
|
||||||
|
@ -131,10 +131,10 @@ dll_entry (HANDLE h, DWORD reason, void *static_load)
|
||||||
the auto load address of DLLs?
|
the auto load address of DLLs?
|
||||||
Check if we're running in WOW64 on a 64 bit machine *and* are
|
Check if we're running in WOW64 on a 64 bit machine *and* are
|
||||||
spawned by a genuine 64 bit process. If so, respawn. */
|
spawned by a genuine 64 bit process. If so, respawn. */
|
||||||
tib = &NtCurrentTeb ()->Tib;
|
teb = NtCurrentTeb ();
|
||||||
if (wincap.is_wow64 ()
|
if (wincap.is_wow64 ()
|
||||||
&& tib->StackLimit >= (PBOOL) 0x400000
|
&& teb->Tib.StackLimit >= (PBOOL) 0x400000
|
||||||
&& tib->StackBase <= (PBOOL) 0x10000000)
|
&& teb->Tib.StackBase <= (PBOOL) 0x10000000)
|
||||||
respawn_wow64_process ();
|
respawn_wow64_process ();
|
||||||
|
|
||||||
dll_crt0_0 ();
|
dll_crt0_0 ();
|
||||||
|
@ -150,12 +150,19 @@ dll_entry (HANDLE h, DWORD reason, void *static_load)
|
||||||
munge_threadfunc ();
|
munge_threadfunc ();
|
||||||
break;
|
break;
|
||||||
case DLL_THREAD_DETACH:
|
case DLL_THREAD_DETACH:
|
||||||
tib = &NtCurrentTeb ()->Tib;
|
teb = NtCurrentTeb ();
|
||||||
if (dll_finished_loading
|
if (dll_finished_loading
|
||||||
&& (PVOID) &_my_tls >= tib->StackLimit
|
&& (PVOID) &teb >= teb->Tib.StackLimit
|
||||||
&& (PVOID) &_my_tls < tib->StackBase
|
&& (PVOID) &teb < teb->Tib.StackBase
|
||||||
&& _my_tls.isinitialized ())
|
&& _my_tls.isinitialized ())
|
||||||
_my_tls.remove (0);
|
_my_tls.remove (0);
|
||||||
|
/* Windows 2000 has a bug in NtTerminateThread. Instead of releasing
|
||||||
|
the stack at teb->DeallocationStack it uses the value of
|
||||||
|
teb->Tib.StackLimit to evaluate the stack address. So we just claim
|
||||||
|
there is no stack. */
|
||||||
|
if (teb->DeallocationStack == NULL
|
||||||
|
&& !wincap.has_stack_size_param_is_a_reservation ())
|
||||||
|
teb->Tib.StackLimit = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,8 @@ details. */
|
||||||
#include "fhandler.h"
|
#include "fhandler.h"
|
||||||
#include "dtable.h"
|
#include "dtable.h"
|
||||||
#include "cygheap.h"
|
#include "cygheap.h"
|
||||||
|
#include "pinfo.h"
|
||||||
|
#include "exception.h"
|
||||||
|
|
||||||
long tls_ix = -1;
|
long tls_ix = -1;
|
||||||
|
|
||||||
|
@ -392,72 +394,109 @@ struct thread_wrapper_arg
|
||||||
{
|
{
|
||||||
LPTHREAD_START_ROUTINE func;
|
LPTHREAD_START_ROUTINE func;
|
||||||
PVOID arg;
|
PVOID arg;
|
||||||
PVOID stackaddr;
|
char *stackaddr;
|
||||||
ULONG stacksize;
|
char *stackbase;
|
||||||
ULONG guardsize;
|
char *commitaddr;
|
||||||
};
|
};
|
||||||
|
|
||||||
DWORD WINAPI
|
DWORD WINAPI
|
||||||
thread_wrapper (VOID *arg)
|
thread_wrapper (VOID *arg)
|
||||||
{
|
{
|
||||||
|
/* Just plain paranoia. */
|
||||||
if (!arg)
|
if (!arg)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
/* Fetch thread wrapper info and free from cygheap. */
|
||||||
thread_wrapper_arg wrapper_arg = *(thread_wrapper_arg *) arg;
|
thread_wrapper_arg wrapper_arg = *(thread_wrapper_arg *) arg;
|
||||||
cfree (arg);
|
cfree (arg);
|
||||||
|
|
||||||
if (wrapper_arg.stackaddr)
|
/* Remove _cygtls from this stack since it won't be used anymore. */
|
||||||
{
|
_cygtls *tls;
|
||||||
/* If the application provided the stack, we must make sure that
|
tls = &_my_tls;
|
||||||
it's actually used by the thread function. So what we do here is
|
|
||||||
to compute the stackbase of the application-provided stack, move
|
|
||||||
_my_tls to the stackbase, and change the stack pointer accordingly. */
|
|
||||||
_my_tls.remove (0);
|
_my_tls.remove (0);
|
||||||
wrapper_arg.stackaddr = (PVOID) ((PBYTE) wrapper_arg.stackaddr
|
|
||||||
+ wrapper_arg.stacksize);
|
|
||||||
_tlsbase = (char *) wrapper_arg.stackaddr;
|
|
||||||
wrapper_arg.stackaddr = (PVOID) ((PBYTE) wrapper_arg.stackaddr
|
|
||||||
- CYGTLS_PADSIZE);
|
|
||||||
_tlstop = (char *) wrapper_arg.stackaddr;
|
|
||||||
_my_tls.init_thread ((char *) wrapper_arg.stackaddr,
|
|
||||||
(DWORD (*)(void*, void*)) wrapper_arg.func);
|
|
||||||
wrapper_arg.stackaddr = (PVOID) (_tlstop - sizeof (PVOID));
|
|
||||||
__asm__ ("\n\
|
|
||||||
movl %[WRAPPER_ARG], %%ebx # Load &wrapper_arg into ebx \n\
|
|
||||||
movl (%%ebx), %%eax # Load thread func into eax \n\
|
|
||||||
movl 4(%%ebx), %%ecx # Load thread arg into ecx \n\
|
|
||||||
movl 8(%%ebx), %%edx # Load stackbase into edx \n\
|
|
||||||
xorl %%ebp, %%ebp # Set ebp to 0 \n\
|
|
||||||
movl %%edx, %%esp # Set esp to stackbase \n\
|
|
||||||
pushl %%ecx # Push thread arg onto stack \n\
|
|
||||||
pushl %%eax # Push thread func onto stack \n\
|
|
||||||
jmp *%%eax # Jump to thread func \n"
|
|
||||||
: : [WRAPPER_ARG] "r" (&wrapper_arg));
|
|
||||||
|
|
||||||
}
|
/* Set stack values in TEB */
|
||||||
if (wrapper_arg.guardsize)
|
PTEB teb = NtCurrentTeb ();
|
||||||
|
teb->Tib.StackBase = wrapper_arg.stackbase;
|
||||||
|
teb->Tib.StackLimit = wrapper_arg.commitaddr ?: wrapper_arg.stackaddr;
|
||||||
|
/* Set DeallocationStack value. If we have an application-provided stack,
|
||||||
|
we set DeallocationStack to NULL, so NtTerminateThread does not deallocate
|
||||||
|
any stack. If we created the stack in CygwinCreateThread, we set
|
||||||
|
DeallocationStack to the stackaddr of our own stack, so it's automatically
|
||||||
|
deallocated when the thread is terminated. */
|
||||||
|
char *dealloc_addr = (char *) teb->DeallocationStack;
|
||||||
|
teb->DeallocationStack = wrapper_arg.commitaddr ? wrapper_arg.stackaddr
|
||||||
|
: NULL;
|
||||||
|
/* Store the OS-provided DeallocationStack address in wrapper_arg.stackaddr.
|
||||||
|
The below assembler code will release the OS stack after switching to our
|
||||||
|
new stack. */
|
||||||
|
wrapper_arg.stackaddr = dealloc_addr;
|
||||||
|
|
||||||
|
/* Initialize new _cygtls. */
|
||||||
|
_my_tls.init_thread (wrapper_arg.stackbase - CYGTLS_PADSIZE,
|
||||||
|
(DWORD (*)(void*, void*)) wrapper_arg.func);
|
||||||
|
|
||||||
|
/* Copy exception list over to new stack. I'm not quite sure how the
|
||||||
|
exception list is extended by Windows itself. What's clear is that it
|
||||||
|
always grows downwards and that it starts right at the stackbase.
|
||||||
|
Therefore we first count the number of exception records and place
|
||||||
|
the copy at the stackbase, too, so there's still a lot of room to
|
||||||
|
extend the list up to where our _cygtls region starts. */
|
||||||
|
_exception_list *old_start = (_exception_list *) teb->Tib.ExceptionList;
|
||||||
|
unsigned count = 0;
|
||||||
|
teb->Tib.ExceptionList = NULL;
|
||||||
|
for (_exception_list *e_ptr = old_start;
|
||||||
|
e_ptr && e_ptr != (_exception_list *) -1;
|
||||||
|
e_ptr = e_ptr->prev)
|
||||||
|
++count;
|
||||||
|
if (count)
|
||||||
{
|
{
|
||||||
/* Set up POSIX guard pages. Note that this is not the same as the
|
_exception_list *new_start = (_exception_list *) wrapper_arg.stackbase
|
||||||
PAGE_GUARD protection. Rather, the POSIX guard pages are a
|
- count;
|
||||||
PAGE_NOACCESS protected area which is supposed to guard against
|
teb->Tib.ExceptionList = (struct _EXCEPTION_REGISTRATION_RECORD *)
|
||||||
stack overflow and to trigger a SIGSEGV if that happens. */
|
new_start;
|
||||||
PNT_TIB tib = &NtCurrentTeb ()->Tib;
|
while (true)
|
||||||
wrapper_arg.stackaddr = (PVOID) ((PBYTE) tib->StackBase
|
{
|
||||||
- wrapper_arg.stacksize);
|
new_start->handler = old_start->handler;
|
||||||
if (!VirtualAlloc (wrapper_arg.stackaddr, wrapper_arg.guardsize,
|
if (old_start->prev == (_exception_list *) -1)
|
||||||
MEM_COMMIT, PAGE_NOACCESS))
|
{
|
||||||
system_printf ("VirtualAlloc, %E");
|
new_start->prev = (_exception_list *) -1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
new_start->prev = new_start + 1;
|
||||||
|
new_start = new_start->prev;
|
||||||
|
old_start = old_start->prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
__asm__ ("\n\
|
__asm__ ("\n\
|
||||||
movl %[WRAPPER_ARG], %%ebx # Load &wrapper_arg into ebx \n\
|
movl %[WRAPPER_ARG], %%ebx # Load &wrapper_arg into ebx \n\
|
||||||
movl (%%ebx), %%eax # Load thread func into eax \n\
|
movl (%%ebx), %%eax # Load thread func into eax \n\
|
||||||
movl 4(%%ebx), %%ecx # Load thread arg into ecx \n\
|
movl 4(%%ebx), %%ecx # Load thread arg into ecx \n\
|
||||||
|
movl 8(%%ebx), %%edx # Load stackaddr into edx \n\
|
||||||
|
movl 12(%%ebx), %%ebx # Load stackbase into ebx \n\
|
||||||
|
subl %[CYGTLS], %%ebx # Subtract CYGTLS_PADSIZE \n\
|
||||||
|
subl $4, %%ebx # Subtract another 4 bytes \n\
|
||||||
|
movl %%ebx, %%esp # Set esp \n\
|
||||||
|
xorl %%ebp, %%ebp # Set ebp to 0 \n\
|
||||||
|
# Now we moved to the new stack. Save thread func address\n\
|
||||||
|
# and thread arg on new stack \n\
|
||||||
pushl %%ecx # Push thread arg onto stack \n\
|
pushl %%ecx # Push thread arg onto stack \n\
|
||||||
pushl %%eax # Push thread func onto stack \n\
|
pushl %%eax # Push thread func onto stack \n\
|
||||||
jmp *%%eax # Jump to thread func \n"
|
# Now it's safe to release the OS stack. \n\
|
||||||
: : [WRAPPER_ARG] "r" (&wrapper_arg));
|
pushl $0x8000 # dwFreeType: MEM_RELEASE \n\
|
||||||
/* Never reached. */
|
pushl $0x0 # dwSize: 0 \n\
|
||||||
return ERROR_INVALID_FUNCTION;
|
pushl %%edx # lpAddress: stackaddr \n\
|
||||||
|
call _VirtualFree@12 # Shoot \n\
|
||||||
|
# All set. We can pop the thread function address from \n\
|
||||||
|
# the stack and call it. The thread arg is still on the \n\
|
||||||
|
# stack in the expected spot. \n\
|
||||||
|
popl %%eax # Pop thread_func address \n\
|
||||||
|
call *%%eax # Call thread func \n"
|
||||||
|
: : [WRAPPER_ARG] "r" (&wrapper_arg),
|
||||||
|
[CYGTLS] "i" (CYGTLS_PADSIZE));
|
||||||
|
/* Never return from here. */
|
||||||
|
ExitThread (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: This should be settable via setrlimit (RLIMIT_STACK). */
|
/* FIXME: This should be settable via setrlimit (RLIMIT_STACK). */
|
||||||
|
@ -468,9 +507,11 @@ CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg,
|
||||||
PVOID stackaddr, ULONG stacksize, ULONG guardsize,
|
PVOID stackaddr, ULONG stacksize, ULONG guardsize,
|
||||||
DWORD creation_flags, LPDWORD thread_id)
|
DWORD creation_flags, LPDWORD thread_id)
|
||||||
{
|
{
|
||||||
|
PVOID real_stackaddr = NULL;
|
||||||
ULONG real_stacksize = 0;
|
ULONG real_stacksize = 0;
|
||||||
ULONG real_guardsize = 0;
|
ULONG real_guardsize = 0;
|
||||||
thread_wrapper_arg *wrapper_arg;
|
thread_wrapper_arg *wrapper_arg;
|
||||||
|
HANDLE thread = NULL;
|
||||||
|
|
||||||
wrapper_arg = (thread_wrapper_arg *) ccalloc (HEAP_STR, 1,
|
wrapper_arg = (thread_wrapper_arg *) ccalloc (HEAP_STR, 1,
|
||||||
sizeof *wrapper_arg);
|
sizeof *wrapper_arg);
|
||||||
|
@ -488,9 +529,8 @@ CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg,
|
||||||
real_stacksize = PTHREAD_STACK_MIN;
|
real_stacksize = PTHREAD_STACK_MIN;
|
||||||
if (stackaddr)
|
if (stackaddr)
|
||||||
{
|
{
|
||||||
wrapper_arg->stackaddr = stackaddr;
|
wrapper_arg->stackaddr = (char *) stackaddr;
|
||||||
wrapper_arg->stacksize = real_stacksize;
|
wrapper_arg->stackbase = (char *) stackaddr + real_stacksize;
|
||||||
real_stacksize = PTHREAD_STACK_MIN;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -498,25 +538,68 @@ CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg,
|
||||||
real_stacksize = roundup2 (real_stacksize, wincap.page_size ());
|
real_stacksize = roundup2 (real_stacksize, wincap.page_size ());
|
||||||
/* If no guardsize has been specified by the application, use the
|
/* If no guardsize has been specified by the application, use the
|
||||||
system pagesize as default. */
|
system pagesize as default. */
|
||||||
real_guardsize = (guardsize != 0xffffffff)
|
real_guardsize = (guardsize != (ULONG) -1)
|
||||||
? guardsize : wincap.page_size ();
|
? guardsize : wincap.page_size ();
|
||||||
if (real_guardsize)
|
if (real_guardsize)
|
||||||
real_guardsize = roundup2 (real_guardsize, wincap.page_size ());
|
real_guardsize = roundup2 (real_guardsize, wincap.page_size ());
|
||||||
/* If the default stacksize is used and guardsize has not been specified,
|
/* If the default stacksize is used and guardsize has not been specified,
|
||||||
don't add a guard page to the size. */
|
don't add a guard page to the size. Same if stacksize is only
|
||||||
if (stacksize && guardsize != 0xffffffff)
|
PTHREAD_STACK_MIN. */
|
||||||
|
if (stacksize && guardsize != (ULONG) -1
|
||||||
|
&& real_stacksize > PTHREAD_STACK_MIN)
|
||||||
real_stacksize += real_guardsize;
|
real_stacksize += real_guardsize;
|
||||||
/* Now roundup the result to the next allocation boundary. */
|
/* Now roundup the result to the next allocation boundary. */
|
||||||
real_stacksize = roundup2 (real_stacksize,
|
real_stacksize = roundup2 (real_stacksize,
|
||||||
wincap.allocation_granularity ());
|
wincap.allocation_granularity ());
|
||||||
|
/* Reserve stack.
|
||||||
wrapper_arg->stacksize = real_stacksize;
|
FIXME? If the TOP_DOWN method tends to collide too much with
|
||||||
wrapper_arg->guardsize = real_guardsize;
|
other stuff, we should provide our own mechanism to find a
|
||||||
|
suitable place for the stack. Top down from the start of
|
||||||
|
the Cygwin DLL comes to mind. */
|
||||||
|
real_stackaddr = VirtualAlloc (NULL, real_stacksize,
|
||||||
|
MEM_RESERVE | MEM_TOP_DOWN,
|
||||||
|
PAGE_EXECUTE_READWRITE);
|
||||||
|
if (!real_stackaddr)
|
||||||
|
return NULL;
|
||||||
|
/* Set up committed region. In contrast to the OS we commit 64K and
|
||||||
|
set up just a single guard page at the end. */
|
||||||
|
char *commitaddr = (char *) real_stackaddr
|
||||||
|
+ real_stacksize
|
||||||
|
- wincap.allocation_granularity ();
|
||||||
|
if (!VirtualAlloc (commitaddr, wincap.page_size (), MEM_COMMIT,
|
||||||
|
PAGE_EXECUTE_READWRITE | PAGE_GUARD))
|
||||||
|
goto err;
|
||||||
|
commitaddr += wincap.page_size ();
|
||||||
|
if (!VirtualAlloc (commitaddr, wincap.allocation_granularity ()
|
||||||
|
- wincap.page_size (), MEM_COMMIT,
|
||||||
|
PAGE_EXECUTE_READWRITE))
|
||||||
|
goto err;
|
||||||
|
if (real_guardsize)
|
||||||
|
VirtualAlloc (real_stackaddr, real_guardsize, MEM_COMMIT,
|
||||||
|
PAGE_NOACCESS);
|
||||||
|
wrapper_arg->stackaddr = (char *) real_stackaddr;
|
||||||
|
wrapper_arg->stackbase = (char *) real_stackaddr + real_stacksize;
|
||||||
|
wrapper_arg->commitaddr = commitaddr;
|
||||||
}
|
}
|
||||||
/* Use the STACK_SIZE_PARAM_IS_A_RESERVATION parameter to make sure the
|
/* Use the STACK_SIZE_PARAM_IS_A_RESERVATION parameter so only the
|
||||||
stack size is exactly the size we want. */
|
minimum size for a thread stack is reserved by the OS. This doesn't
|
||||||
return CreateThread (&sec_none_nih, real_stacksize, thread_wrapper,
|
work on Windows 2000, but we deallocate the OS stack in thread_wrapper
|
||||||
wrapper_arg,
|
anyway, so this should be a problem only in a tight memory condition.
|
||||||
|
Note that we reserve a 256K stack, not 64K, otherwise the thread creation
|
||||||
|
might crash the process due to a stack overflow. */
|
||||||
|
thread = CreateThread (&sec_none_nih, 4 * PTHREAD_STACK_MIN,
|
||||||
|
thread_wrapper, wrapper_arg,
|
||||||
creation_flags | STACK_SIZE_PARAM_IS_A_RESERVATION,
|
creation_flags | STACK_SIZE_PARAM_IS_A_RESERVATION,
|
||||||
thread_id);
|
thread_id);
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (!thread && real_stackaddr)
|
||||||
|
{
|
||||||
|
/* Don't report the wrong error even though VirtualFree is very unlikely
|
||||||
|
to fail. */
|
||||||
|
DWORD err = GetLastError ();
|
||||||
|
VirtualFree (real_stackaddr, 0, MEM_RELEASE);
|
||||||
|
SetLastError (err);
|
||||||
|
}
|
||||||
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
|
@ -622,7 +622,7 @@ typedef struct _PEB
|
||||||
ULONG SessionId;
|
ULONG SessionId;
|
||||||
} PEB, *PPEB;
|
} PEB, *PPEB;
|
||||||
|
|
||||||
/* Simplified definition, just to get the TIB and the PEB pointer. */
|
/* Simplified definition, just to get stuff we're interested in. */
|
||||||
typedef struct _TEB
|
typedef struct _TEB
|
||||||
{
|
{
|
||||||
NT_TIB Tib;
|
NT_TIB Tib;
|
||||||
|
@ -631,6 +631,32 @@ typedef struct _TEB
|
||||||
PVOID ActiveRpcHandle;
|
PVOID ActiveRpcHandle;
|
||||||
PVOID ThreadLocalStoragePointer;
|
PVOID ThreadLocalStoragePointer;
|
||||||
PPEB Peb;
|
PPEB Peb;
|
||||||
|
ULONG LastErrorValue;
|
||||||
|
ULONG CountOfOwnedCriticalSections;
|
||||||
|
PVOID _reserved1[2];
|
||||||
|
ULONG _reserved2[31];
|
||||||
|
PVOID WOW32Reserved;
|
||||||
|
ULONG CurrentLocale;
|
||||||
|
ULONG FpSoftwareStatusRegister;
|
||||||
|
PVOID SystemReserved1[54];
|
||||||
|
LONG ExceptionCode;
|
||||||
|
PVOID ActivationContextStackPointer;
|
||||||
|
UCHAR SpareBytes1[36];
|
||||||
|
ULONG TxFsContext;
|
||||||
|
ULONG GdiTebBatch[312];
|
||||||
|
CLIENT_ID RealClientId;
|
||||||
|
PVOID GdiCachedProcessHandle;
|
||||||
|
ULONG GdiClientPID;
|
||||||
|
ULONG GdiClientTID;
|
||||||
|
PVOID GdiThreadLocalInfo;
|
||||||
|
ULONG Win32ClientInfo[62];
|
||||||
|
PVOID glDispatchTable[233];
|
||||||
|
ULONG glReserved1[29];
|
||||||
|
PVOID glReserved2[6];
|
||||||
|
ULONG LastStatusValue;
|
||||||
|
UNICODE_STRING StaticUnicodeString;
|
||||||
|
WCHAR StaticUnicodeBuffer[261];
|
||||||
|
PVOID DeallocationStack;
|
||||||
/* A lot more follows... */
|
/* A lot more follows... */
|
||||||
} TEB, *PTEB;
|
} TEB, *PTEB;
|
||||||
|
|
||||||
|
|
|
@ -1089,7 +1089,7 @@ pthread::resume ()
|
||||||
pthread_attr::pthread_attr ():verifyable_object (PTHREAD_ATTR_MAGIC),
|
pthread_attr::pthread_attr ():verifyable_object (PTHREAD_ATTR_MAGIC),
|
||||||
joinable (PTHREAD_CREATE_JOINABLE), contentionscope (PTHREAD_SCOPE_PROCESS),
|
joinable (PTHREAD_CREATE_JOINABLE), contentionscope (PTHREAD_SCOPE_PROCESS),
|
||||||
inheritsched (PTHREAD_INHERIT_SCHED), stackaddr (NULL), stacksize (0),
|
inheritsched (PTHREAD_INHERIT_SCHED), stackaddr (NULL), stacksize (0),
|
||||||
guardsize (0xffffffff)
|
guardsize ((size_t) -1)
|
||||||
{
|
{
|
||||||
schedparam.sched_priority = 0;
|
schedparam.sched_priority = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
has_fast_cwd:false,
|
has_fast_cwd:false,
|
||||||
has_restricted_raw_disk_access:false,
|
has_restricted_raw_disk_access:false,
|
||||||
use_dont_resolve_hack:false,
|
use_dont_resolve_hack:false,
|
||||||
|
has_stack_size_param_is_a_reservation:false,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
|
@ -81,6 +82,7 @@ wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) =
|
||||||
has_fast_cwd:false,
|
has_fast_cwd:false,
|
||||||
has_restricted_raw_disk_access:false,
|
has_restricted_raw_disk_access:false,
|
||||||
use_dont_resolve_hack:false,
|
use_dont_resolve_hack:false,
|
||||||
|
has_stack_size_param_is_a_reservation:false,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = {
|
wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
|
@ -111,6 +113,7 @@ wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
has_fast_cwd:false,
|
has_fast_cwd:false,
|
||||||
has_restricted_raw_disk_access:false,
|
has_restricted_raw_disk_access:false,
|
||||||
use_dont_resolve_hack:true,
|
use_dont_resolve_hack:true,
|
||||||
|
has_stack_size_param_is_a_reservation:true,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
|
@ -141,6 +144,7 @@ wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
has_fast_cwd:false,
|
has_fast_cwd:false,
|
||||||
has_restricted_raw_disk_access:false,
|
has_restricted_raw_disk_access:false,
|
||||||
use_dont_resolve_hack:true,
|
use_dont_resolve_hack:true,
|
||||||
|
has_stack_size_param_is_a_reservation:true,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
|
@ -171,6 +175,7 @@ wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
has_fast_cwd:false,
|
has_fast_cwd:false,
|
||||||
has_restricted_raw_disk_access:false,
|
has_restricted_raw_disk_access:false,
|
||||||
use_dont_resolve_hack:true,
|
use_dont_resolve_hack:true,
|
||||||
|
has_stack_size_param_is_a_reservation:true,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
|
@ -201,6 +206,7 @@ wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
has_fast_cwd:false,
|
has_fast_cwd:false,
|
||||||
has_restricted_raw_disk_access:false,
|
has_restricted_raw_disk_access:false,
|
||||||
use_dont_resolve_hack:true,
|
use_dont_resolve_hack:true,
|
||||||
|
has_stack_size_param_is_a_reservation:true,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
|
wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
|
@ -231,6 +237,7 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
has_fast_cwd:true,
|
has_fast_cwd:true,
|
||||||
has_restricted_raw_disk_access:true,
|
has_restricted_raw_disk_access:true,
|
||||||
use_dont_resolve_hack:false,
|
use_dont_resolve_hack:false,
|
||||||
|
has_stack_size_param_is_a_reservation:true,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
|
@ -261,6 +268,7 @@ wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
||||||
has_fast_cwd:true,
|
has_fast_cwd:true,
|
||||||
has_restricted_raw_disk_access:true,
|
has_restricted_raw_disk_access:true,
|
||||||
use_dont_resolve_hack:false,
|
use_dont_resolve_hack:false,
|
||||||
|
has_stack_size_param_is_a_reservation:true,
|
||||||
};
|
};
|
||||||
|
|
||||||
wincapc wincap __attribute__((section (".cygwin_dll_common"), shared));
|
wincapc wincap __attribute__((section (".cygwin_dll_common"), shared));
|
||||||
|
|
|
@ -41,6 +41,7 @@ struct wincaps
|
||||||
unsigned has_fast_cwd : 1;
|
unsigned has_fast_cwd : 1;
|
||||||
unsigned has_restricted_raw_disk_access : 1;
|
unsigned has_restricted_raw_disk_access : 1;
|
||||||
unsigned use_dont_resolve_hack : 1;
|
unsigned use_dont_resolve_hack : 1;
|
||||||
|
unsigned has_stack_size_param_is_a_reservation : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
class wincapc
|
class wincapc
|
||||||
|
@ -90,6 +91,7 @@ public:
|
||||||
bool IMPLEMENT (has_fast_cwd)
|
bool IMPLEMENT (has_fast_cwd)
|
||||||
bool IMPLEMENT (has_restricted_raw_disk_access)
|
bool IMPLEMENT (has_restricted_raw_disk_access)
|
||||||
bool IMPLEMENT (use_dont_resolve_hack)
|
bool IMPLEMENT (use_dont_resolve_hack)
|
||||||
|
bool IMPLEMENT (has_stack_size_param_is_a_reservation)
|
||||||
|
|
||||||
#undef IMPLEMENT
|
#undef IMPLEMENT
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue