On 64 bit, only create new thread stack if started from 32 bit process on affected platforms
* dcrt0.cc (dll_crt0_0): On 64 bit, set wow64_needs_stack_adjustment if not started from a 64 bit process. (_dll_crt0): Enable wow64_needs_stack_adjustment branch on 64 bit as well. Remove 64 bit only code. Introduce CREATE_STACK and FIX_STACK macros to conditionalize the code. Rearrange and partially rewrite comments. * wincap.h (wincaps::has_3264_stack_broken): New element. * wincap.cc: Implement above element throughout. (wincapc::init): Set has_3264_stack_broken to false on 32 bit. * wow64.cc: Enable functionality on 64 bit architecture, except for wow64_revert_to_original_stack. Enhance comments to explain. (wow64_eval_expected_main_stack): Make 64 bit clean. (wow64_test_for_64bit_parent): Ditto. * wow64.h: Export wow64_revert_to_original_stack on 32 bit only, everything else on all architectures. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
parent
8f4da28eb6
commit
12743c2d5d
|
@ -1,3 +1,21 @@
|
|||
2015-12-03 Corinna Vinschen <corinna@vinschen.de>
|
||||
|
||||
* dcrt0.cc (dll_crt0_0): On 64 bit, set wow64_needs_stack_adjustment
|
||||
if not started from a 64 bit process.
|
||||
(_dll_crt0): Enable wow64_needs_stack_adjustment branch on 64 bit
|
||||
as well. Remove 64 bit only code. Introduce CREATE_STACK and
|
||||
FIX_STACK macros to conditionalize the code. Rearrange and
|
||||
partially rewrite comments.
|
||||
* wincap.h (wincaps::has_3264_stack_broken): New element.
|
||||
* wincap.cc: Implement above element throughout.
|
||||
(wincapc::init): Set has_3264_stack_broken to false on 32 bit.
|
||||
* wow64.cc: Enable functionality on 64 bit architecture, except for
|
||||
wow64_revert_to_original_stack. Enhance comments to explain.
|
||||
(wow64_eval_expected_main_stack): Make 64 bit clean.
|
||||
(wow64_test_for_64bit_parent): Ditto.
|
||||
* wow64.h: Export wow64_revert_to_original_stack on 32 bit only,
|
||||
everything else on all architectures.
|
||||
|
||||
2015-12-03 Corinna Vinschen <corinna@vinschen.de>
|
||||
|
||||
* fhandler_process.cc (thread_info::thread_info): Accommodate the fact
|
||||
|
|
|
@ -782,6 +782,11 @@ 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
|
||||
|
@ -1062,67 +1067,50 @@ __cygwin_exit_return: \n\
|
|||
extern "C" void __stdcall
|
||||
_dll_crt0 ()
|
||||
{
|
||||
#ifndef __x86_64__
|
||||
#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))
|
||||
#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 = 0;
|
||||
static PVOID allocationbase;
|
||||
|
||||
/* Check if we just move the stack. If so, wow64_revert_to_original_stack
|
||||
returns a non-NULL, 16 byte aligned address. See comments in
|
||||
wow64_revert_to_original_stack for the gory details. */
|
||||
PVOID stackaddr = wow64_revert_to_original_stack (allocationbase);
|
||||
PVOID stackaddr = CREATE_STACK (allocationbase);
|
||||
if (stackaddr)
|
||||
{
|
||||
/* 2nd half of the stack move. Set stack pointer to new address.
|
||||
Set frame pointer to 0. */
|
||||
__asm__ ("\n\
|
||||
movl %[ADDR], %%esp \n\
|
||||
xorl %%ebp, %%ebp \n"
|
||||
: : [ADDR] "r" (stackaddr));
|
||||
FIX_STACK (stackaddr);
|
||||
/* Now we're back on the original stack. 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 respawn if wow64_revert_to_original_stack fails. */
|
||||
/* Fall back to respawning if creating a new stack fails. */
|
||||
wow64_respawn_process ();
|
||||
}
|
||||
#else
|
||||
/* 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 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 (!in_forkee && !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_new_main_thread_stack (allocationbase);
|
||||
if (stackaddr)
|
||||
{
|
||||
/* 2nd half of the stack move. Set stack pointer to new address.
|
||||
Don't set frame pointer to 0 since x86_64 uses the stack while
|
||||
evaluating NtCurrentTeb (). */
|
||||
__asm__ ("\n\
|
||||
movq %[ADDR], %%rsp \n\
|
||||
movq %%rsp, %%rbp \n"
|
||||
: : [ADDR] "r" (stackaddr));
|
||||
/* Now we're back on the new stack. Free up space taken by the
|
||||
former main thread stack and set DeallocationStack correctly. */
|
||||
VirtualFree (NtCurrentTeb ()->DeallocationStack, 0, MEM_RELEASE);
|
||||
NtCurrentTeb ()->DeallocationStack = allocationbase;
|
||||
}
|
||||
}
|
||||
#endif /* !__x86_64__ */
|
||||
_feinitialise ();
|
||||
#ifndef __x86_64__
|
||||
main_environ = user_data->envptr;
|
||||
|
|
|
@ -52,6 +52,7 @@ 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)) = {
|
||||
|
@ -86,6 +87,7 @@ 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)) = {
|
||||
|
@ -120,6 +122,7 @@ 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)) = {
|
||||
|
@ -154,6 +157,7 @@ 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)) = {
|
||||
|
@ -188,6 +192,7 @@ 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)) = {
|
||||
|
@ -222,6 +227,7 @@ 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)) = {
|
||||
|
@ -256,6 +262,7 @@ 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));
|
||||
|
@ -320,6 +327,10 @@ wincapc::init ()
|
|||
target process on 64 bit XP/2003 in native 64 bit mode only. Reset the
|
||||
flag here for 32 bit. */
|
||||
((wincaps *)caps)->has_broken_rtl_query_process_debug_information = false;
|
||||
/* 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))
|
||||
|
|
|
@ -45,6 +45,7 @@ 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
|
||||
|
@ -104,6 +105,7 @@ 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
|
||||
};
|
||||
|
|
|
@ -8,10 +8,6 @@ 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"
|
||||
|
@ -25,11 +21,11 @@ static void
|
|||
wow64_eval_expected_main_stack (PVOID &allocbase, PVOID &stackbase)
|
||||
{
|
||||
PIMAGE_DOS_HEADER dosheader;
|
||||
PIMAGE_NT_HEADERS32 ntheader;
|
||||
DWORD size;
|
||||
PIMAGE_NT_HEADERS ntheader;
|
||||
SIZE_T size;
|
||||
|
||||
dosheader = (PIMAGE_DOS_HEADER) GetModuleHandle (NULL);
|
||||
ntheader = (PIMAGE_NT_HEADERS32) ((PBYTE) dosheader + dosheader->e_lfanew);
|
||||
ntheader = (PIMAGE_NT_HEADERS) ((PBYTE) dosheader + dosheader->e_lfanew);
|
||||
/* The main thread stack is expected to be located at 0x30000, which is the
|
||||
case for all observed NT systems up to Server 2003 R2, unless the
|
||||
stacksize requested by the StackReserve field in the PE/COFF header is
|
||||
|
@ -41,8 +37,15 @@ 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. */
|
||||
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
|
||||
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,
|
||||
|
@ -71,11 +74,19 @@ wow64_test_for_64bit_parent ()
|
|||
we have to "alloc_stack_hard_way". However, this fails in almost all
|
||||
cases because the stack slot of the parent process is taken by something
|
||||
else in the child process.
|
||||
What we do here is to check if the current stack is the excpected main
|
||||
What we do here is to check if the current stack is the expected main
|
||||
thread stack and if not, if we really have been started from a 64 bit
|
||||
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;
|
||||
|
@ -86,7 +97,7 @@ wow64_test_for_64bit_parent ()
|
|||
/* First check if the current stack is where it belongs. If so, we don't
|
||||
have to do anything special. This is the case on Vista and later. */
|
||||
wow64_eval_expected_main_stack (allocbase, stackbase);
|
||||
if (&wow64 >= (PULONG) allocbase && &wow64 < (PULONG) stackbase)
|
||||
if (&wow64 >= (PULONG_PTR) allocbase && &wow64 < (PULONG_PTR) stackbase)
|
||||
return false;
|
||||
|
||||
/* Check if the parent is a native 64 bit process. Unfortunately there's
|
||||
|
@ -107,6 +118,8 @@ wow64_test_for_64bit_parent ()
|
|||
return !wow64;
|
||||
}
|
||||
|
||||
#ifndef __x86_64__
|
||||
|
||||
PVOID
|
||||
wow64_revert_to_original_stack (PVOID &allocationbase)
|
||||
{
|
||||
|
@ -177,6 +190,8 @@ wow64_revert_to_original_stack (PVOID &allocationbase)
|
|||
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.
|
||||
|
@ -211,5 +226,3 @@ wow64_respawn_process ()
|
|||
TerminateProcess (GetCurrentProcess (), ret);
|
||||
ExitProcess (ret);
|
||||
}
|
||||
|
||||
#endif /* !__x86_64__ */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* wow64.h
|
||||
|
||||
Copyright 2011, 2012 Red Hat, Inc.
|
||||
Copyright 2011, 2012, 2015 Red Hat, Inc.
|
||||
|
||||
This file is part of Cygwin.
|
||||
|
||||
|
@ -8,14 +8,12 @@ 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__ */
|
||||
|
|
Loading…
Reference in New Issue