Always allocate main thread stack from pthread stack area on x86_64.

* dcrt0.cc: Semi-revert commit 12743c2d5d.
        (dll_crt0_0): Drop setting wow64_needs_stack_adjustment on 64 bit.
        (_dll_crt0): Split out 64 bit code again and always create new main
        thread stack, unless forked off from the non main thread in the parent.
        Call create_new_main_thread_stack with parent stack commitsize if
        started from the parent's main thread.
        Only call child_info_fork::alloc_stack for the latter case on 64 bit.
        Slightly rearrange moving rsp and rbp to new stack and document how.
        Revert 32 bit wow64 handling to its former self.
        * miscfunc.cc (create_new_main_thread_stack): Take a commitsize
        parameter and use it if it's not 0.  Don't set _main_tls here, it's
        done in the caller _dll_crt0 anyway.  Return stackbase - 16 bytes,
        rather than stacklimit (which was very wrong anyway).
        * miscfuncs.h (create_new_main_thread_stack): Accommodate declaration
        to aforementioned change.
        * wincap.h (wincaps::has_3264_stack_broken): Remove element.
        * wincap.cc: Ditto, throughout.
        * wow64.cc: Semi-revert to pre-12743c2d5d2721f3a80b4d7671a349be03c1f520
        but keep architecture-agnostic type changes intact.  Fix formatting.
        * wow64.h: Revert to pre-12743c2d5d2721f3a80b4d7671a349be03c1f520.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2015-12-07 16:10:55 +01:00
parent 5aa8817e3a
commit e753e4129a
8 changed files with 96 additions and 79 deletions

View File

@ -1,3 +1,26 @@
2015-12-07 Corinna Vinschen <corinna@vinschen.de>
* dcrt0.cc: Semi-revert commit 12743c2d5d2721f3a80b4d7671a349be03c1f520.
(dll_crt0_0): Drop setting wow64_needs_stack_adjustment on 64 bit.
(_dll_crt0): Split out 64 bit code again and always create new main
thread stack, unless forked off from the non main thread in the parent.
Call create_new_main_thread_stack with parent stack commitsize if
started from the parent's main thread.
Only call child_info_fork::alloc_stack for the latter case on 64 bit.
Slightly rearrange moving rsp and rbp to new stack and document how.
Revert 32 bit wow64 handling to its former self.
* miscfunc.cc (create_new_main_thread_stack): Take a commitsize
parameter and use it if it's not 0. Don't set _main_tls here, it's
done in the caller _dll_crt0 anyway. Return stackbase - 16 bytes,
rather than stacklimit (which was very wrong anyway).
* miscfuncs.h (create_new_main_thread_stack): Accommodate declaration
to aforementioned change.
* wincap.h (wincaps::has_3264_stack_broken): Remove element.
* wincap.cc: Ditto, throughout.
* wow64.cc: Semi-revert to pre-12743c2d5d2721f3a80b4d7671a349be03c1f520
but keep architecture-agnostic type changes intact. Fix formatting.
* wow64.h: Revert to pre-12743c2d5d2721f3a80b4d7671a349be03c1f520.
2015-12-06 Corinna Vinschen <corinna@vinschen.de>
* include/sys/cygwin.h (CCP_PROC_CYGDRIVE): New flag.

View File

@ -782,11 +782,6 @@ dll_crt0_0 ()
description in wow64_test_for_64bit_parent. */
if (wincap.wow64_has_secondary_stack ())
wow64_needs_stack_adjustment = wow64_test_for_64bit_parent ();
#else
/* Windows 10 1511 has a stack move when a 64 bit process is started from
a 32 bit process, just as it was vice versa in XP/2003. */
if (wincap.has_3264_stack_broken ())
wow64_needs_stack_adjustment = !wow64_test_for_64bit_parent ();
#endif /* !__x86_64__ */
}
else
@ -1068,59 +1063,76 @@ extern "C" void __stdcall
_dll_crt0 ()
{
#ifdef __x86_64__
/* Handle 64 bit process on Windows 10 rel 1511 which has been started from
32 bit WOW64 process. See comment in wow64_test_for_64bit_parent for a
problem description. Unfortunately the areas the stacks would have to
be moved to are both taken by "something else"(tm) in both, forker and
forkee, so we can't use the wow64_revert_to_original_stack method as in
the 32 bit case. Rather, we move the main thread stack to the stack area
reserved for pthread stacks for this process. */
#define CREATE_STACK(a) create_new_main_thread_stack(a)
#define FIX_STACK(s) __asm__ ("\n" \
"movq %[ADDR], %%rsp \n" \
"movq %%rsp, %%rbp \n" \
: : [ADDR] "r" (s))
/* Starting with Windows 10 rel 1511, the main stack of an application is
not reproducible if a 64 bit process has been started from a 32 bit
process. Given that we have enough virtual address space on 64 bit
anyway, we now always move the main thread stack to the stack area
reserved for pthread stacks. This allows a reproducible stack space
under our own control and avoids collision with the OS. */
if (!dynamically_loaded)
{
if (!in_forkee || fork_info->from_main)
{
/* Must be static since it's referenced after the stack and frame
pointer registers have been changed. */
static PVOID allocationbase;
SIZE_T commitsize = in_forkee ? (PBYTE) fork_info->stackbase
- (PBYTE) fork_info->stacklimit
: 0;
PVOID stackaddr = create_new_main_thread_stack (allocationbase,
commitsize);
if (stackaddr)
{
/* Set stack pointer to new address. Set frame pointer to
stack pointer and subtract 32 bytes for shadow space. */
__asm__ ("\n\
movq %[ADDR], %%rsp \n\
movq %%rsp, %%rbp \n\
subq $32,%%rsp \n"
: : [ADDR] "r" (stackaddr));
/* We're on the new stack now. Free up space taken by the former
main thread stack and set DeallocationStack correctly. */
VirtualFree (NtCurrentTeb ()->DeallocationStack, 0, MEM_RELEASE);
NtCurrentTeb ()->DeallocationStack = allocationbase;
}
}
else
fork_info->alloc_stack ();
}
#else
/* Handle WOW64 process on XP/2K3 which has been started from native 64 bit
process. See comment in wow64_test_for_64bit_parent for a full problem
description. */
#define CREATE_STACK(a) wow64_revert_to_original_stack(a)
#define FIX_STACK(s) __asm__ ("\n" \
"movl %[ADDR], %%esp \n" \
"xorl %%ebp, %%ebp \n" \
: : [ADDR] "r" (s))
#endif
if (wow64_needs_stack_adjustment && !dynamically_loaded)
{
/* Must be static since it's referenced after the stack and frame
pointer registers have been changed. */
static PVOID allocationbase;
PVOID stackaddr = CREATE_STACK (allocationbase);
PVOID stackaddr = wow64_revert_to_original_stack (allocationbase);
if (stackaddr)
{
/* 2nd half of the stack move. Set stack pointer to new address.
Set frame pointer to 0. */
FIX_STACK (stackaddr);
/* Now we're back on the original stack. Free up space taken by the
/* Set stack pointer to new address. Set frame pointer to 0. */
__asm__ ("\n\
movl %[ADDR], %%esp \n\
xorl %%ebp, %%ebp \n"
: : [ADDR] "r" (stackaddr));
/* We're back on the original stack now. Free up space taken by the
former main thread stack and set DeallocationStack correctly. */
VirtualFree (NtCurrentTeb ()->DeallocationStack, 0, MEM_RELEASE);
NtCurrentTeb ()->DeallocationStack = allocationbase;
}
else
/* Fall back to respawning if creating a new stack fails. */
wow64_respawn_process ();
wow64_respawn_process();
}
_feinitialise ();
#ifndef __x86_64__
main_environ = user_data->envptr;
#endif
if (in_forkee)
{
fork_info->alloc_stack ();
_main_tls = &_my_tls;
}
fork_info->alloc_stack ();
#endif
_feinitialise ();
_main_tls = &_my_tls;
_main_tls->call ((DWORD (*) (void *, void *)) dll_crt0_1, NULL);
}

View File

@ -765,13 +765,13 @@ thread_allocator thr_alloc NO_COPY;
maintained by the thr_alloc class. See the description in the x86_64-only
code in _dll_crt0 to understand why we have to do this. */
PVOID
create_new_main_thread_stack (PVOID &allocationbase)
create_new_main_thread_stack (PVOID &allocationbase, SIZE_T parent_commitsize)
{
PIMAGE_DOS_HEADER dosheader;
PIMAGE_NT_HEADERS ntheader;
SIZE_T stacksize;
ULONG guardsize;
ULONG commitsize;
SIZE_T commitsize;
PBYTE stacklimit;
dosheader = (PIMAGE_DOS_HEADER) GetModuleHandle (NULL);
@ -783,7 +783,10 @@ create_new_main_thread_stack (PVOID &allocationbase)
allocationbase
= thr_alloc.alloc (ntheader->OptionalHeader.SizeOfStackReserve);
guardsize = wincap.def_guard_page_size ();
commitsize = ntheader->OptionalHeader.SizeOfStackCommit;
if (parent_commitsize)
commitsize = (SIZE_T) parent_commitsize;
else
commitsize = ntheader->OptionalHeader.SizeOfStackCommit;
commitsize = roundup2 (commitsize, wincap.page_size ());
if (commitsize > stacksize - guardsize - wincap.page_size ())
commitsize = stacksize - guardsize - wincap.page_size ();
@ -798,8 +801,7 @@ create_new_main_thread_stack (PVOID &allocationbase)
return NULL;
NtCurrentTeb()->Tib.StackBase = ((PBYTE) allocationbase + stacksize);
NtCurrentTeb()->Tib.StackLimit = stacklimit;
_main_tls = &_my_tls;
return stacklimit - 64;
return ((PBYTE) allocationbase + stacksize - 16);
}
#endif

View File

@ -71,7 +71,8 @@ ssize_t __reg3 check_iovec (const struct iovec *, int, bool);
#define check_iovec_for_write(a, b) check_iovec ((a), (b), true)
#ifdef __x86_64__
extern PVOID create_new_main_thread_stack (PVOID &allocationbase);
extern PVOID create_new_main_thread_stack (PVOID &allocationbase,
SIZE_T parent_commitsize);
#endif
extern "C" DWORD WINAPI pthread_wrapper (PVOID arg);

View File

@ -52,7 +52,6 @@ wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_processor_groups:false,
has_broken_prefetchvm:false,
has_new_pebteb_region:false,
has_3264_stack_broken:false,
};
wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -87,7 +86,6 @@ wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_processor_groups:false,
has_broken_prefetchvm:false,
has_new_pebteb_region:false,
has_3264_stack_broken:false,
};
wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -122,7 +120,6 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
has_processor_groups:false,
has_broken_prefetchvm:false,
has_new_pebteb_region:false,
has_3264_stack_broken:false,
};
wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -157,7 +154,6 @@ wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_processor_groups:true,
has_broken_prefetchvm:false,
has_new_pebteb_region:false,
has_3264_stack_broken:false,
};
wincaps wincap_8 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -192,7 +188,6 @@ wincaps wincap_8 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_processor_groups:true,
has_broken_prefetchvm:false,
has_new_pebteb_region:false,
has_3264_stack_broken:false,
};
wincaps wincap_10 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -227,7 +222,6 @@ wincaps wincap_10 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_processor_groups:true,
has_broken_prefetchvm:true,
has_new_pebteb_region:false,
has_3264_stack_broken:false,
};
wincaps wincap_10_1511 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -262,7 +256,6 @@ wincaps wincap_10_1511 __attribute__((section (".cygwin_dll_common"), shared)) =
has_processor_groups:true,
has_broken_prefetchvm:false,
has_new_pebteb_region:true,
has_3264_stack_broken:true,
};
wincapc wincap __attribute__((section (".cygwin_dll_common"), shared));
@ -330,7 +323,6 @@ wincapc::init ()
/* Windows 10 1511 has a stack move when a 64 bit process is started from
a 32 bit process, just as it was vice versa in XP/2003. Reset the flag
here for 32 bit. */
((wincaps *)caps)->has_3264_stack_broken = false;
if (NT_SUCCESS (NtQueryInformationProcess (NtCurrentProcess (),
ProcessWow64Information,
&wow64, sizeof wow64, NULL))

View File

@ -45,7 +45,6 @@ struct wincaps
unsigned has_processor_groups : 1;
unsigned has_broken_prefetchvm : 1;
unsigned has_new_pebteb_region : 1;
unsigned has_3264_stack_broken : 1;
};
class wincapc
@ -105,7 +104,6 @@ public:
bool IMPLEMENT (has_processor_groups)
bool IMPLEMENT (has_broken_prefetchvm)
bool IMPLEMENT (has_new_pebteb_region)
bool IMPLEMENT (has_3264_stack_broken)
#undef IMPLEMENT
};

View File

@ -8,6 +8,10 @@ This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifndef __x86_64__
/* WOW64 only plays a role in the 32 bit version. Don't use any of this
in the 64 bit version. */
#include "winsup.h"
#include "cygtls.h"
#include "ntdll.h"
@ -37,15 +41,8 @@ wow64_eval_expected_main_stack (PVOID &allocbase, PVOID &stackbase)
stack address on Vista/2008 64 bit is 0x80000 and on W7/2K8R2 64 bit
it is 0x90000. However, this is no problem because the system sticks
to that address for all WOW64 processes, not only for the first one
started from a 64 bit parent.
On 64 bit W10 1511 the stack starts at 0x400000 by default. See comment
in wow64_test_for_64bit_parent. */
#ifdef __x86_64__
allocbase = (PVOID) 0x400000;
#else
started from a 64 bit parent. */
allocbase = (PVOID) 0x30000;
#endif
/* Stack size. The OS always rounds the size up to allocation granularity
and it never allocates less than 256K. */
size = roundup2 (ntheader->OptionalHeader.SizeOfStackReserve,
@ -79,14 +76,6 @@ wow64_test_for_64bit_parent ()
process here. If so, we note this fact in wow64_needs_stack_adjustment
so we can workaround the stack problem in _dll_crt0. See there for how
we go along. */
/* Amazing but true: Starting with Windows 10 1511 this problem has been
reintroduced, just in the opposite direction: If a 64 bit process is
created from a 32 bit WOW64 process, the main thread stack in the 64
bit child gets moved to another location than the default. In the
forked child, the stack is back where it usually is when started from
another 64 bit process. Therefore we have to be able to recognize
this scenarion now on 64 bit as well. We I don't believe it... */
NTSTATUS ret;
PROCESS_BASIC_INFORMATION pbi;
HANDLE parent;
@ -118,8 +107,6 @@ wow64_test_for_64bit_parent ()
return !wow64;
}
#ifndef __x86_64__
PVOID
wow64_revert_to_original_stack (PVOID &allocationbase)
{
@ -184,14 +171,12 @@ wow64_revert_to_original_stack (PVOID &allocationbase)
accordingly, and return the new, 16 byte aligned address for the
stack pointer. The second half of the stack move is done by the
caller _dll_crt0. */
NtCurrentTeb()->Tib.StackBase = (char *) newbase;
NtCurrentTeb()->Tib.StackLimit = (char *) newtop;
NtCurrentTeb ()->Tib.StackBase = (char *) newbase;
NtCurrentTeb ()->Tib.StackLimit = (char *) newtop;
_main_tls = &_my_tls;
return PTR_ADD (NtCurrentTeb()->Tib.StackBase, -16);
return PTR_ADD (NtCurrentTeb ()->Tib.StackBase, -16);
}
#endif /* !__x86_64__ */
/* Respawn WOW64 process. This is only called if we can't reuse the original
stack. See comment in wow64_revert_to_original_stack for details. See
_dll_crt0 for the call of this function.
@ -226,3 +211,5 @@ wow64_respawn_process ()
TerminateProcess (GetCurrentProcess (), ret);
ExitProcess (ret);
}
#endif /* !__x86_64__ */

View File

@ -8,12 +8,14 @@ This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#ifndef __x86_64__
/* WOW64 only plays a role in the 32 bit version. Don't use any of this
in the 64 bit version. */
extern bool NO_COPY wow64_needs_stack_adjustment;
extern bool wow64_test_for_64bit_parent ();
extern PVOID wow64_revert_to_original_stack (PVOID &allocationbase);
extern void wow64_respawn_process () __attribute__ ((noreturn));
#ifndef __x86_64__
extern PVOID wow64_revert_to_original_stack (PVOID &allocationbase);
#endif /* !__x86_64__ */