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
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/cygwin/wow64.cc
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/cygwin/wow64.cc')
-rw-r--r--winsup/cygwin/wow64.cc165
1 files changed, 165 insertions, 0 deletions
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);
+}