mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-02-21 00:07:36 +08:00
* Makefile.in (DLL_OFILES): Add wow64.o.
* dcrt0.cc (CYGWIN_GUARD): Drop execute permission for stack, it's not used for stacks by the OS either. (child_info_fork::alloc_stack_hard_way): Ditto. (child_info_fork::alloc_stack): Don't alloc_stack_hard_way under WOW64 if forked from a 64 bit parent. Set child's StackBase to parent's StackBase. Add comments to explain why. (wow64_respawn): Move to wow64.cc. (wow64_started_from_native64): Move to wow64.cc. (respawn_wow64_process): Move to wow64.cc. (dll_crt0_0): Drop wow64_test_stack_marker and move stack test into wow64_test_for_64bit_parent function. Don't return early if WOW64 process has been started from native 64 bit process. (_dll_crt0): Implement moving stack for WOW64 processes started from native 64 bit process. * wow64.cc: New file. (wow64_has_64bit_parent): Rename from wow64_respawn. (wow64_test_for_64bit_parent): Rename from wow64_started_from_native64. Change comment. (wow64_revert_to_original_stack): New function. (wow64_respawn_process): Rename from respawn_wow64_process for symmetry. * wow64.h: New file.
This commit is contained in:
parent
6625879aa6
commit
344e68b166
@ -1,3 +1,28 @@
|
||||
2011-12-16 Corinna Vinschen <vinschen@redhat.com>
|
||||
|
||||
* Makefile.in (DLL_OFILES): Add wow64.o.
|
||||
* dcrt0.cc (CYGWIN_GUARD): Drop execute permission for stack, it's
|
||||
not used for stacks by the OS either.
|
||||
(child_info_fork::alloc_stack_hard_way): Ditto.
|
||||
(child_info_fork::alloc_stack): Don't alloc_stack_hard_way under WOW64
|
||||
if forked from a 64 bit parent. Set child's StackBase to parent's
|
||||
StackBase. Add comments to explain why.
|
||||
(wow64_respawn): Move to wow64.cc.
|
||||
(wow64_started_from_native64): Move to wow64.cc.
|
||||
(respawn_wow64_process): Move to wow64.cc.
|
||||
(dll_crt0_0): Drop wow64_test_stack_marker and move stack test into
|
||||
wow64_test_for_64bit_parent function. Don't return early if WOW64
|
||||
process has been started from native 64 bit process.
|
||||
(_dll_crt0): Implement moving stack for WOW64 processes started from
|
||||
native 64 bit process.
|
||||
* wow64.cc: New file.
|
||||
(wow64_has_64bit_parent): Rename from wow64_respawn.
|
||||
(wow64_test_for_64bit_parent): Rename from wow64_started_from_native64.
|
||||
Change comment.
|
||||
(wow64_revert_to_original_stack): New function.
|
||||
(wow64_respawn_process): Rename from respawn_wow64_process for symmetry.
|
||||
* wow64.h: New file.
|
||||
|
||||
2011-12-16 Christopher Faylor <me.cygwin2011@cgf.cx>
|
||||
|
||||
* exceptions.cc (_cygtls::call_signal_handler): Fix debugging to not go
|
||||
|
@ -157,7 +157,7 @@ DLL_OFILES:=advapi32.o assert.o autoload.o bsdlib.o ctype.o cxx.o cygheap.o \
|
||||
smallprint.o spawn.o strace.o strfmon.o strfuncs.o strptime.o strsep.o \
|
||||
strsig.o sync.o syscalls.o sysconf.o syslog.o termios.o thread.o \
|
||||
timer.o times.o tls_pbuf.o tty.o uinfo.o uname.o wait.o wincap.o \
|
||||
window.o winf.o xsique.o \
|
||||
window.o winf.o wow64.o xsique.o \
|
||||
$(EXTRA_DLL_OFILES) $(EXTRA_OFILES) $(MALLOC_OFILES) $(MT_SAFE_OBJECTS)
|
||||
|
||||
EXCLUDE_STATIC_OFILES:=$(addprefix --exclude=,\
|
||||
|
@ -37,6 +37,7 @@ details. */
|
||||
#include "cygxdr.h"
|
||||
#include "fenv.h"
|
||||
#include "ntdll.h"
|
||||
#include "wow64.h"
|
||||
|
||||
#define MAX_AT_FILE_LEVEL 10
|
||||
|
||||
@ -385,7 +386,7 @@ check_sanity_and_sync (per_process *p)
|
||||
|
||||
child_info NO_COPY *child_proc_info = NULL;
|
||||
|
||||
#define CYGWIN_GUARD (PAGE_EXECUTE_READWRITE | PAGE_GUARD)
|
||||
#define CYGWIN_GUARD (PAGE_READWRITE | PAGE_GUARD)
|
||||
|
||||
void
|
||||
child_info_fork::alloc_stack_hard_way (volatile char *b)
|
||||
@ -408,8 +409,7 @@ child_info_fork::alloc_stack_hard_way (volatile char *b)
|
||||
api_fatal ("fork: can't reserve memory for stack %p - %p, %E",
|
||||
stackaddr, stackbottom);
|
||||
stacksize = (char *) stackbottom - (char *) stacktop;
|
||||
stack_ptr = VirtualAlloc (stacktop, stacksize, MEM_COMMIT,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
stack_ptr = VirtualAlloc (stacktop, stacksize, MEM_COMMIT, PAGE_READWRITE);
|
||||
if (!stack_ptr)
|
||||
abort ("can't commit memory for stack %p(%d), %E", stacktop, stacksize);
|
||||
if (guardsize != (size_t) -1)
|
||||
@ -447,7 +447,15 @@ child_info_fork::alloc_stack ()
|
||||
{
|
||||
volatile char * volatile esp;
|
||||
__asm__ volatile ("movl %%esp,%0": "=r" (esp));
|
||||
if (_tlsbase != stackbottom)
|
||||
/* Make sure not to try a hard allocation if we have been forked off from
|
||||
the main thread of a Cygwin process which has been started from a 64 bit
|
||||
parent. In that case the _tlsbase of the forked child is not the same
|
||||
as the _tlsbase of the parent (== stackbottom), but only because the
|
||||
stack of the parent has been slightly rearranged. See comment in
|
||||
wow64_revert_to_original_stack for details. We just check here if the
|
||||
stack is in the usual range for the main thread stack. */
|
||||
if (_tlsbase != stackbottom
|
||||
&& (!wincap.is_wow64 () || stackbottom > (char *) 0x400000))
|
||||
alloc_stack_hard_way (esp);
|
||||
else
|
||||
{
|
||||
@ -455,6 +463,11 @@ child_info_fork::alloc_stack ()
|
||||
while (_tlstop >= st)
|
||||
esp = getstack (esp);
|
||||
stackaddr = 0;
|
||||
/* This only affects forked children of a process started from a native
|
||||
64 bit process, but it doesn't hurt to do it unconditionally. Fix
|
||||
StackBase in the child to be the same as in the parent, so that the
|
||||
computation of _my_tls is correct. */
|
||||
_tlsbase = (char *) stackbottom;
|
||||
}
|
||||
}
|
||||
|
||||
@ -657,75 +670,6 @@ init_windows_system_directory ()
|
||||
}
|
||||
}
|
||||
|
||||
static bool NO_COPY wow64_respawn = false;
|
||||
|
||||
inline static bool
|
||||
wow64_started_from_native64 ()
|
||||
{
|
||||
/* On Windows XP 64 and 2003 64 there's a problem with processes running
|
||||
under WOW64. The first process started from a 64 bit process has an
|
||||
unusual stack address for the main thread. That is, an address which
|
||||
is in the usual space occupied by the process image, but below the auto
|
||||
load address of DLLs. If we encounter this situation, check if we
|
||||
really have been started from a 64 bit process here. If so, we exit
|
||||
early from dll_crt0_0 and respawn first thing in dll_crt0_1. This
|
||||
ping-pong game is necessary to workaround a problem observed on
|
||||
Windows 2003 R2 64. Starting with Cygwin 1.7.10 we don't link against
|
||||
advapi32.dll anymore. However, *any* process linked against advapi32,
|
||||
directly or indirectly, now fails to respawn if respawn_wow_64_process
|
||||
is called during DLL_PROCESS_ATTACH initialization. In that case
|
||||
CreateProcessW returns with ERROR_ACCESS_DENIED for some reason.
|
||||
Calling CreateProcessW later, inside dll_crt0_1 and so outside of
|
||||
dll initialization works as before, though. */
|
||||
NTSTATUS ret;
|
||||
PROCESS_BASIC_INFORMATION pbi;
|
||||
HANDLE parent;
|
||||
|
||||
ULONG wow64 = TRUE; /* Opt on the safe side. */
|
||||
|
||||
/* Unfortunately there's no simpler way to retrieve the
|
||||
parent process in NT, as far as I know. Hints welcome. */
|
||||
ret = NtQueryInformationProcess (NtCurrentProcess (),
|
||||
ProcessBasicInformation,
|
||||
&pbi, sizeof pbi, NULL);
|
||||
if (NT_SUCCESS (ret)
|
||||
&& (parent = OpenProcess (PROCESS_QUERY_INFORMATION,
|
||||
FALSE,
|
||||
pbi.InheritedFromUniqueProcessId)))
|
||||
{
|
||||
NtQueryInformationProcess (parent, ProcessWow64Information,
|
||||
&wow64, sizeof wow64, NULL);
|
||||
CloseHandle (parent);
|
||||
}
|
||||
return !wow64;
|
||||
}
|
||||
|
||||
inline static void
|
||||
respawn_wow64_process ()
|
||||
{
|
||||
/* The parent is a real 64 bit process. Respawn. */
|
||||
WCHAR path[PATH_MAX];
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFOW si;
|
||||
DWORD ret = 0;
|
||||
|
||||
GetModuleFileNameW (NULL, path, PATH_MAX);
|
||||
GetStartupInfoW (&si);
|
||||
if (!CreateProcessW (path, GetCommandLineW (), NULL, NULL, TRUE,
|
||||
CREATE_DEFAULT_ERROR_MODE
|
||||
| GetPriorityClass (GetCurrentProcess ()),
|
||||
NULL, NULL, &si, &pi))
|
||||
api_fatal ("Failed to create process <%W> <%W>, %E",
|
||||
path, GetCommandLineW ());
|
||||
CloseHandle (pi.hThread);
|
||||
if (WaitForSingleObject (pi.hProcess, INFINITE) == WAIT_FAILED)
|
||||
api_fatal ("Waiting for process %d failed, %E", pi.dwProcessId);
|
||||
GetExitCodeProcess (pi.hProcess, &ret);
|
||||
CloseHandle (pi.hProcess);
|
||||
TerminateProcess (GetCurrentProcess (), ret);
|
||||
ExitProcess (ret);
|
||||
}
|
||||
|
||||
void
|
||||
dll_crt0_0 ()
|
||||
{
|
||||
@ -759,15 +703,11 @@ dll_crt0_0 ()
|
||||
if (!child_proc_info)
|
||||
{
|
||||
memory_init (true);
|
||||
/* WOW64 bit process with stack at unusual address? Check if we
|
||||
have been started from 64 bit process ans set wow64_respawn.
|
||||
Full description in wow64_started_from_native64 above. */
|
||||
BOOL wow64_test_stack_marker;
|
||||
if (wincap.is_wow64 ()
|
||||
&& &wow64_test_stack_marker >= (PBOOL) 0x400000
|
||||
&& &wow64_test_stack_marker <= (PBOOL) 0x10000000
|
||||
&& (wow64_respawn = wow64_started_from_native64 ()))
|
||||
return;
|
||||
/* WOW64 process? Check if we have been started from 64 bit process
|
||||
and if our stack is at an unusual address. Set wow64_has_64bit_parent
|
||||
if so. Problem description in wow64_test_for_64bit_parent. */
|
||||
if (wincap.is_wow64 ())
|
||||
wow64_has_64bit_parent = wow64_test_for_64bit_parent ();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1003,10 +943,36 @@ __cygwin_exit_return: \n\
|
||||
extern "C" void __stdcall
|
||||
_dll_crt0 ()
|
||||
{
|
||||
/* Respawn WOW64 process started from native 64 bit process. See comment
|
||||
in wow64_started_from_native64 above for a full description. */
|
||||
if (wow64_respawn)
|
||||
respawn_wow64_process ();
|
||||
/* Handle WOW64 process started from native 64 bit process. See comment
|
||||
in wow64_test_for_64bit_parent for a full problem description. */
|
||||
if (wow64_has_64bit_parent && !dynamically_loaded)
|
||||
{
|
||||
/* Must be static since it's referenced after the stack pointers have
|
||||
been moved. */
|
||||
static PVOID allocationbase = 0;
|
||||
|
||||
/* Check if we just move the stack. See comment in
|
||||
wow64_revert_to_original_stack for the gory details. */
|
||||
PVOID stackaddr = wow64_revert_to_original_stack (allocationbase);
|
||||
if (stackaddr)
|
||||
{
|
||||
/* 2nd half of the stack move. First set stack pointers to
|
||||
our new address. */
|
||||
__asm__ ("\n\
|
||||
movl %[ADDR], %%esp \n\
|
||||
movl %%esp, %%ebp \n"
|
||||
: : [ADDR] "r" (stackaddr));
|
||||
/* Now we're back on the original stack. Free up space taken by the
|
||||
former main thread stack and... */
|
||||
VirtualFree (NtCurrentTeb ()->DeallocationStack,
|
||||
0, MEM_RELEASE);
|
||||
/* ...set DeallocationStack correctly. */
|
||||
NtCurrentTeb ()->DeallocationStack = allocationbase;
|
||||
}
|
||||
else
|
||||
/* Fall back to respawn if wow64_revert_to_original_stack fails. */
|
||||
wow64_respawn_process ();
|
||||
}
|
||||
#ifdef __i386__
|
||||
_feinitialise ();
|
||||
#endif
|
||||
|
165
winsup/cygwin/wow64.cc
Normal file
165
winsup/cygwin/wow64.cc
Normal file
@ -0,0 +1,165 @@
|
||||
/* wow64.cc
|
||||
|
||||
Copyright 2011 Red Hat, Inc.
|
||||
|
||||
This file is part of Cygwin.
|
||||
|
||||
This software is a copyrighted work licensed under the terms of the
|
||||
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
||||
details. */
|
||||
|
||||
#include "winsup.h"
|
||||
#include "cygtls.h"
|
||||
#include "ntdll.h"
|
||||
|
||||
#define PTR_ADD(p,o) ((PVOID)((PBYTE)(p)+(o)))
|
||||
|
||||
bool NO_COPY wow64_has_64bit_parent = false;
|
||||
|
||||
bool
|
||||
wow64_test_for_64bit_parent ()
|
||||
{
|
||||
/* On Windows XP 64 and 2003 64 there's a problem with processes running
|
||||
under WOW64. The first process started from a 64 bit process has an
|
||||
unusual stack address for the main thread. That is, an address which
|
||||
is in the usual space occupied by the process image, but below the auto
|
||||
load address of DLLs. If this process forks, the child has its stack
|
||||
in the usual memory slot again, thus 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.
|
||||
|
||||
If we encounter this situation, check if we really have been started
|
||||
from a 64 bit process here. If so, we note this fact in
|
||||
wow64_has_64bit_parent so we can workaround the stack problem in
|
||||
_dll_crt0. See there for how we go along. */
|
||||
NTSTATUS ret;
|
||||
PROCESS_BASIC_INFORMATION pbi;
|
||||
HANDLE parent;
|
||||
|
||||
ULONG wow64 = TRUE; /* Opt on the safe side. */
|
||||
|
||||
/* First check if the stack is where it belongs. If so, we don't have to
|
||||
do anything special. This is the case on Vista and later. */
|
||||
if (&wow64 < (PULONG) 0x400000)
|
||||
return false;
|
||||
/* Check if the parent is a native 64 bit process. Unfortunately there's
|
||||
no simpler way to retrieve the parent process in NT, as far as I know.
|
||||
Hints welcome. */
|
||||
ret = NtQueryInformationProcess (NtCurrentProcess (),
|
||||
ProcessBasicInformation,
|
||||
&pbi, sizeof pbi, NULL);
|
||||
if (NT_SUCCESS (ret)
|
||||
&& (parent = OpenProcess (PROCESS_QUERY_INFORMATION,
|
||||
FALSE,
|
||||
pbi.InheritedFromUniqueProcessId)))
|
||||
{
|
||||
NtQueryInformationProcess (parent, ProcessWow64Information,
|
||||
&wow64, sizeof wow64, NULL);
|
||||
CloseHandle (parent);
|
||||
}
|
||||
return !wow64;
|
||||
}
|
||||
|
||||
PVOID
|
||||
wow64_revert_to_original_stack (PVOID &allocationbase)
|
||||
{
|
||||
/* Test if the original stack exists and has been set up as usual. Even
|
||||
though the stack of the WOW64 process is at an unusual address, it appears
|
||||
that the "normal" stack has been created as usual. It's partially in use
|
||||
by the 32->64 bit transition layer of the OS to help along the WOW64
|
||||
process, but it's otherwise mostly unused.
|
||||
The original stack is expected to be located at 0x30000, up to 0x230000.
|
||||
The assumption here is that the default main thread stack size is 2 Megs,
|
||||
but we expect lower stacksizes up to 1 Megs. What we do here is to start
|
||||
about in the middle, but below the 1 Megs stack size. The stack is
|
||||
allocated in a single call, so the entire stack has the same
|
||||
AllocationBase. */
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
PVOID addr = (PVOID) 0x100000;
|
||||
|
||||
/* First fetch the AllocationBase. */
|
||||
VirtualQuery (addr, &mbi, sizeof mbi);
|
||||
allocationbase = mbi.AllocationBase;
|
||||
/* At the start we expect a reserved region big enough still to host as
|
||||
the main stack. 512K should be ok (knock on wood). */
|
||||
VirtualQuery (allocationbase, &mbi, sizeof mbi);
|
||||
if (mbi.State != MEM_RESERVE || mbi.RegionSize < 512 * 1024)
|
||||
return NULL;
|
||||
|
||||
addr = PTR_ADD (mbi.BaseAddress, mbi.RegionSize);
|
||||
/* Next we expect a guard page. */
|
||||
VirtualQuery (addr, &mbi, sizeof mbi);
|
||||
if (mbi.AllocationBase != allocationbase
|
||||
|| mbi.State != MEM_COMMIT
|
||||
|| !(mbi.Protect & PAGE_GUARD))
|
||||
return NULL;
|
||||
|
||||
PVOID guardaddr = mbi.BaseAddress;
|
||||
SIZE_T guardsize = mbi.RegionSize;
|
||||
addr = PTR_ADD (mbi.BaseAddress, mbi.RegionSize);
|
||||
/* Next we expect a committed R/W region, the in-use area of that stack. */
|
||||
VirtualQuery (addr, &mbi, sizeof mbi);
|
||||
if (mbi.AllocationBase != allocationbase
|
||||
|| mbi.State != MEM_COMMIT
|
||||
|| mbi.Protect != PAGE_READWRITE)
|
||||
return NULL;
|
||||
|
||||
/* The original stack is used by the OS. Leave enough space for the OS
|
||||
to be happy (another 64K) and constitute a second stack within the so
|
||||
far reserved stack area. */
|
||||
PVOID newbase = PTR_ADD (guardaddr, -wincap.allocation_granularity ());
|
||||
PVOID newtop = PTR_ADD (newbase, -wincap.allocation_granularity ());
|
||||
guardaddr = PTR_ADD (newtop, -guardsize);
|
||||
if (!VirtualAlloc (newtop, wincap.allocation_granularity (),
|
||||
MEM_COMMIT, PAGE_READWRITE))
|
||||
return NULL;
|
||||
if (!VirtualAlloc (guardaddr, guardsize, MEM_COMMIT,
|
||||
PAGE_READWRITE | PAGE_GUARD))
|
||||
return NULL;
|
||||
|
||||
/* We're going to reuse the original stack. Yay, no more respawn!
|
||||
Set the StackBase and StackLimit values in the TEB, set _main_tls
|
||||
accordingly, and return the new address for the stack pointer.
|
||||
The second half of the stack move is done by the caller _dll_crt0. */
|
||||
_tlsbase = (char *) newbase;
|
||||
_tlstop = (char *) newtop;
|
||||
_main_tls = &_my_tls;
|
||||
return PTR_ADD (_main_tls, -4);
|
||||
}
|
||||
|
||||
/* 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.
|
||||
|
||||
Historical note:
|
||||
|
||||
Originally we just always respawned, right from dll_entry. This stopped
|
||||
working with Cygwin 1.7.10 on Windows 2003 R2 64. Starting with Cygwin
|
||||
1.7.10 we don't link against advapi32.dll anymore. However, any process
|
||||
linked against advapi32, directly or indirectly, failed to respawn when
|
||||
trying respawning during DLL_PROCESS_ATTACH initialization. In that
|
||||
case CreateProcessW returns with ERROR_ACCESS_DENIED for some reason. */
|
||||
void
|
||||
wow64_respawn_process ()
|
||||
{
|
||||
WCHAR path[PATH_MAX];
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFOW si;
|
||||
DWORD ret = 0;
|
||||
|
||||
GetModuleFileNameW (NULL, path, PATH_MAX);
|
||||
GetStartupInfoW (&si);
|
||||
if (!CreateProcessW (path, GetCommandLineW (), NULL, NULL, TRUE,
|
||||
CREATE_DEFAULT_ERROR_MODE
|
||||
| GetPriorityClass (GetCurrentProcess ()),
|
||||
NULL, NULL, &si, &pi))
|
||||
api_fatal ("Failed to create process <%W> <%W>, %E",
|
||||
path, GetCommandLineW ());
|
||||
CloseHandle (pi.hThread);
|
||||
if (WaitForSingleObject (pi.hProcess, INFINITE) == WAIT_FAILED)
|
||||
api_fatal ("Waiting for process %d failed, %E", pi.dwProcessId);
|
||||
GetExitCodeProcess (pi.hProcess, &ret);
|
||||
CloseHandle (pi.hProcess);
|
||||
TerminateProcess (GetCurrentProcess (), ret);
|
||||
ExitProcess (ret);
|
||||
}
|
15
winsup/cygwin/wow64.h
Normal file
15
winsup/cygwin/wow64.h
Normal file
@ -0,0 +1,15 @@
|
||||
/* wow64.h
|
||||
|
||||
Copyright 2011 Red Hat, Inc.
|
||||
|
||||
This file is part of Cygwin.
|
||||
|
||||
This software is a copyrighted work licensed under the terms of the
|
||||
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
||||
details. */
|
||||
|
||||
extern bool NO_COPY wow64_has_64bit_parent;
|
||||
|
||||
extern bool wow64_test_for_64bit_parent ();
|
||||
extern PVOID wow64_revert_to_original_stack (PVOID &allocationbase);
|
||||
extern void wow64_respawn_process ();
|
Loading…
x
Reference in New Issue
Block a user