From cdce73f183d46a202c632486d5bd82d515cb05be Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Mon, 12 Dec 2011 10:16:53 +0000 Subject: [PATCH] * dcrt0.cc (wow64_respawn): New static variable. (wow64_started_from_native64): New function to check if a WOW64 process got started from a native 64 bit process. (respawn_wow64_process): New function to respawn process. (dll_crt0_0): When started from a native parent, check if parent is a 64 bit process. If so, return early. (_dll_crt0): Respawn WOW64 process here if required. * init.cc (respawn_wow64_process): Remove. (dll_entry): Rename wow64_test_stack_marker to test_stack_marker. Drop WOW64 test here. --- winsup/cygwin/ChangeLog | 14 +++++++ winsup/cygwin/dcrt0.cc | 86 ++++++++++++++++++++++++++++++++++++++++- winsup/cygwin/init.cc | 60 +--------------------------- 3 files changed, 101 insertions(+), 59 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index a2789f494..ef7fa4d04 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,17 @@ +2011-12-12 Corinna Vinschen + Christopher Faylor + + * dcrt0.cc (wow64_respawn): New static variable. + (wow64_started_from_native64): New function to check if a WOW64 + process got started from a native 64 bit process. + (respawn_wow64_process): New function to respawn process. + (dll_crt0_0): When started from a native parent, check if parent + is a 64 bit process. If so, return early. + (_dll_crt0): Respawn WOW64 process here if required. + * init.cc (respawn_wow64_process): Remove. + (dll_entry): Rename wow64_test_stack_marker to test_stack_marker. + Drop WOW64 test here. + 2011-12-11 Christopher Faylor * pipe.cc (fhandler_pipe::create): Use debug_printf to print debugging diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 6c13eff95..5364e4dd8 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -646,6 +646,75 @@ init_windows_system_directory () windows_system_directory[windows_system_directory_length] = L'\0'; } +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 () { @@ -677,7 +746,18 @@ dll_crt0_0 () cygthread::init (); if (!child_proc_info) - memory_init (true); + { + 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; + } else { cygwin_user_h = child_proc_info->user_h; @@ -912,6 +992,10 @@ __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 (); #ifdef __i386__ _feinitialise (); #endif diff --git a/winsup/cygwin/init.cc b/winsup/cygwin/init.cc index 107d037f9..5b0416125 100644 --- a/winsup/cygwin/init.cc +++ b/winsup/cygwin/init.cc @@ -64,58 +64,12 @@ munge_threadfunc () } } -inline static void -respawn_wow64_process () -{ - 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); - } - - /* The parent is a real 64 bit process? Respawn! */ - if (!wow64) - { - PROCESS_INFORMATION pi; - STARTUPINFOW si; - DWORD ret = 0; - - GetStartupInfoW (&si); - if (!CreateProcessW (NULL, GetCommandLineW (), NULL, NULL, TRUE, - CREATE_DEFAULT_ERROR_MODE - | GetPriorityClass (GetCurrentProcess ()), - NULL, NULL, &si, &pi)) - api_fatal ("Failed to create process <%s>, %E", GetCommandLineA ()); - 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); - ExitProcess (ret); - } -} - void dll_crt0_0 (); extern "C" BOOL WINAPI dll_entry (HANDLE h, DWORD reason, void *static_load) { - BOOL wow64_test_stack_marker; + BOOL test_stack_marker; switch (reason) { @@ -126,16 +80,6 @@ dll_entry (HANDLE h, DWORD reason, void *static_load) cygwin_hmodule = (HMODULE) h; dynamically_loaded = (static_load == NULL); - /* Is the stack at an unusual address? That is, an address which - is in the usual space occupied by the process image, but below - the auto load address of DLLs? - Check if we're running in WOW64 on a 64 bit machine *and* are - spawned by a genuine 64 bit process. If so, respawn. */ - if (wincap.is_wow64 () - && &wow64_test_stack_marker >= (PBOOL) 0x400000 - && &wow64_test_stack_marker <= (PBOOL) 0x10000000) - respawn_wow64_process (); - dll_crt0_0 (); _my_oldfunc = TlsAlloc (); dll_finished_loading = true; @@ -150,7 +94,7 @@ dll_entry (HANDLE h, DWORD reason, void *static_load) break; case DLL_THREAD_DETACH: if (dll_finished_loading - && (PVOID) &_my_tls > (PVOID) &wow64_test_stack_marker + && (PVOID) &_my_tls > (PVOID) &test_stack_marker && _my_tls.isinitialized ()) _my_tls.remove (0); /* Windows 2000 has a bug in NtTerminateThread. Instead of releasing