Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/winsup
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2011-12-16 15:58:03 +0400
committerCorinna Vinschen <corinna@vinschen.de>2011-12-16 15:58:03 +0400
commit344e68b166462eada01704cc27251a34a5e32398 (patch)
tree779f68fb88897ec4a00d2cb2d13e982507d5d948 /winsup
parent6625879aa6d76736eff81f2fb862a99330ae5c28 (diff)
* 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.
Diffstat (limited to 'winsup')
-rw-r--r--winsup/cygwin/ChangeLog25
-rw-r--r--winsup/cygwin/Makefile.in2
-rw-r--r--winsup/cygwin/dcrt0.cc138
-rw-r--r--winsup/cygwin/wow64.cc165
-rw-r--r--winsup/cygwin/wow64.h15
5 files changed, 258 insertions, 87 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index b3c7c8842..49a50cf34 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -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
diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index 3932bbdeb..c3310cf01 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -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=,\
diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc
index b6392acf2..a3900372d 100644
--- a/winsup/cygwin/dcrt0.cc
+++ b/winsup/cygwin/dcrt0.cc
@@ -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
diff --git a/winsup/cygwin/wow64.cc b/winsup/cygwin/wow64.cc
new file mode 100644
index 000000000..6908cf8a4
--- /dev/null
+++ b/winsup/cygwin/wow64.cc
@@ -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);
+}
diff --git a/winsup/cygwin/wow64.h b/winsup/cygwin/wow64.h
new file mode 100644
index 000000000..bd0564f1f
--- /dev/null
+++ b/winsup/cygwin/wow64.h
@@ -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 ();