203 lines
8.1 KiB
C++
203 lines
8.1 KiB
C++
/* 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"
|
|
#include <sys/param.h>
|
|
|
|
#define PTR_ADD(p,o) ((PVOID)((PBYTE)(p)+(o)))
|
|
|
|
bool NO_COPY wow64_has_64bit_parent = false;
|
|
|
|
static void
|
|
wow64_eval_expected_main_stack (PVOID &allocbase, PVOID &stackbase)
|
|
{
|
|
PIMAGE_DOS_HEADER dosheader;
|
|
PIMAGE_NT_HEADERS32 ntheader;
|
|
DWORD size;
|
|
|
|
dosheader = (PIMAGE_DOS_HEADER) GetModuleHandle (NULL);
|
|
ntheader = (PIMAGE_NT_HEADERS32) ((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 to date, unless the stacksize requested
|
|
by the StackReserve field in the PE/COFF header is so big that the stack
|
|
doesn't fit in the area between 0x30000 and the start of the image. In
|
|
case of a conflict, the OS allocates the stack right after the image. */
|
|
allocbase = (PVOID) 0x30000;
|
|
/* Stack size. The OS always rounds the size up to allocation granularity
|
|
and it never allocates less than 256K. */
|
|
size = roundup2 (ntheader->OptionalHeader.SizeOfStackReserve,
|
|
wincap.allocation_granularity ());
|
|
if (size < 256 * 1024)
|
|
size = 256 * 1024;
|
|
/* If the stack doesn't fit in the area before the image, it's allocated
|
|
right after the image, rounded up to allocation granularity boundary. */
|
|
if (PTR_ADD (allocbase, size) > (PVOID) ntheader->OptionalHeader.ImageBase)
|
|
allocbase = PTR_ADD (ntheader->OptionalHeader.ImageBase,
|
|
ntheader->OptionalHeader.SizeOfImage);
|
|
allocbase = (PVOID) roundup2 ((uintptr_t) allocbase,
|
|
wincap.allocation_granularity ());
|
|
stackbase = PTR_ADD (allocbase, size);
|
|
debug_printf ("expected allocbase: %p, stackbase: %p", allocbase, stackbase);
|
|
}
|
|
|
|
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 its
|
|
main thread stack not where it should be. Rather, it uses another
|
|
stack while the original stack is used for other purposes.
|
|
The problem is, the child has its stack in the usual spot 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.
|
|
What we do here is to check if the current stack is the excpected 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_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;
|
|
PVOID allocbase, stackbase;
|
|
|
|
ULONG wow64 = TRUE; /* Opt on the safe side. */
|
|
|
|
/* 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)
|
|
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. */
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
PVOID stackbase;
|
|
|
|
wow64_eval_expected_main_stack (allocationbase, stackbase);
|
|
|
|
/* The stack is allocated in a single call, so the entire stack has the
|
|
same AllocationBase. At the start we expect a reserved region big
|
|
enough still to host as the main stack. The OS apparently reserves
|
|
always at least 256K for the main thread stack. We err on the side
|
|
of caution so we test here for a reserved region of at least 256K.
|
|
That should be enough (knock on wood). */
|
|
VirtualQuery (allocationbase, &mbi, sizeof mbi);
|
|
if (mbi.State != MEM_RESERVE || mbi.RegionSize < 256 * 1024)
|
|
return NULL;
|
|
|
|
/* Next we expect a guard page. We fetch the size of the guard area to
|
|
see how big it is. Apparently the guard area on 64 bit systems spans
|
|
2 pages. */
|
|
PVOID addr = PTR_ADD (mbi.BaseAddress, mbi.RegionSize);
|
|
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;
|
|
|
|
/* Next we expect a committed R/W region, the in-use area of that stack.
|
|
This is just a sanity check. */
|
|
addr = PTR_ADD (mbi.BaseAddress, mbi.RegionSize);
|
|
VirtualQuery (addr, &mbi, sizeof mbi);
|
|
if (mbi.AllocationBase != allocationbase
|
|
|| PTR_ADD (mbi.BaseAddress, mbi.RegionSize) != stackbase
|
|
|| 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);
|
|
}
|