* cygheap.h (cwdstuff::override_win32_cwd): Declare.

* ntdll.h (struct _PEB): Add members accessed by the fast cwd method
	starting with Vista.
	(struct _KUSER_SHARED_DATA): Define with only the DismountCount.
	(RtlAllocateHeap): Declare.
	(RtlEnterCriticalSection): Declare.
	(RtlFreeHeap): Declare.
	(RtlLeaveCriticalSection): Declare.
	* path.cc (get_user_proc_parms): Remove.
	(struct _FAST_CWD): New structure.
	(fast_cwd_ptr): Define.
	(SharedUserData): Define.
	(peek32): Define.
	(find_fast_cwd_pointers): New function to find the global pointer
	to the current FAST_CWD structure.
	(copy_cwd_str): New helper function.
	(cwdstuff::override_win32_cwd): New method to set the Win32 CWD.
	(cwdstuff::init): Just call override_win32_cwd from here when
	started from native Win32 parent.
	(cwdstuff::set): Access Win32 CWD via PEB reference instead of using
	get_user_proc_parms function.  Memorize old DismountCount before
	opening directory handle.  Call override_win32_cwd to set up Win32 CWD.
	Be more verbose in comments.
	* wincap.h (wincaps::has_fast_cwd): New element.
	* wincap.cc: Implement has_fast_cwd element throughout.
This commit is contained in:
Corinna Vinschen 2010-10-09 10:54:13 +00:00
parent d8c5f616ef
commit 9c154abedb
6 changed files with 354 additions and 22 deletions

View File

@ -1,3 +1,32 @@
2010-09-08 John Carey <aeolus@electric-cloud.com>
Corinna Vinschen <corinna@vinschen.de>
* cygheap.h (cwdstuff::override_win32_cwd): Declare.
* ntdll.h (struct _PEB): Add members accessed by the fast cwd method
starting with Vista.
(struct _KUSER_SHARED_DATA): Define with only the DismountCount.
(RtlAllocateHeap): Declare.
(RtlEnterCriticalSection): Declare.
(RtlFreeHeap): Declare.
(RtlLeaveCriticalSection): Declare.
* path.cc (get_user_proc_parms): Remove.
(struct _FAST_CWD): New structure.
(fast_cwd_ptr): Define.
(SharedUserData): Define.
(peek32): Define.
(find_fast_cwd_pointers): New function to find the global pointer
to the current FAST_CWD structure.
(copy_cwd_str): New helper function.
(cwdstuff::override_win32_cwd): New method to set the Win32 CWD.
(cwdstuff::init): Just call override_win32_cwd from here when
started from native Win32 parent.
(cwdstuff::set): Access Win32 CWD via PEB reference instead of using
get_user_proc_parms function. Memorize old DismountCount before
opening directory handle. Call override_win32_cwd to set up Win32 CWD.
Be more verbose in comments.
* wincap.h (wincaps::has_fast_cwd): New element.
* wincap.cc: Implement has_fast_cwd element throughout.
2010-09-08 Marco Atzeri <marco_atzeri@yahoo.it>
Corinna Vinschen <corinna@vinschen.de>

View File

@ -217,6 +217,8 @@ private:
a native Win32 application. See cwdstuff::set for
how it gets set. See spawn_guts for how it's
evaluated. */
void override_win32_cwd (bool, ULONG);
public:
UNICODE_STRING win32;
static muto cwd_lock;

View File

@ -584,7 +584,10 @@ typedef struct _PEB
BYTE Reserved2[9];
PVOID LoaderData;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
BYTE Reserved3[448];
BYTE Reserved3[4];
PVOID ProcessHeap;
PRTL_CRITICAL_SECTION FastPebLock;
BYTE Reserved4[436];
ULONG SessionId;
} PEB, *PPEB;
@ -596,6 +599,13 @@ typedef struct _TEB
/* A lot more follows... */
} TEB, *PTEB;
typedef struct _KUSER_SHARED_DATA
{
BYTE Reserved1[0x2dc];
ULONG DismountCount;
/* A lot more follows... */
} KUSER_SHARED_DATA, *PKUSER_SHARED_DATA;
typedef struct _PROCESS_BASIC_INFORMATION
{
NTSTATUS ExitStatus;
@ -979,10 +989,11 @@ extern "C"
NTSTATUS NTAPI NtWriteFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
PIO_STATUS_BLOCK, PVOID, ULONG, PLARGE_INTEGER,
PULONG);
VOID NTAPI RtlAcquirePebLock ();
PVOID NTAPI RtlAllocateHeap (PVOID, ULONG, SIZE_T);
NTSTATUS NTAPI RtlAppendUnicodeToString (PUNICODE_STRING, PCWSTR);
NTSTATUS NTAPI RtlAppendUnicodeStringToString (PUNICODE_STRING,
PUNICODE_STRING);
VOID NTAPI RtlAcquirePebLock ();
NTSTATUS NTAPI RtlAnsiStringToUnicodeString (PUNICODE_STRING, PANSI_STRING,
BOOLEAN);
LONG NTAPI RtlCompareUnicodeString (PUNICODE_STRING, PUNICODE_STRING,
@ -992,9 +1003,11 @@ extern "C"
BOOLEAN NTAPI RtlCreateUnicodeStringFromAsciiz (PUNICODE_STRING, PCSTR);
NTSTATUS NTAPI RtlDowncaseUnicodeString (PUNICODE_STRING, PUNICODE_STRING,
BOOLEAN);
NTSTATUS NTAPI RtlEnterCriticalSection (PRTL_CRITICAL_SECTION);
BOOLEAN NTAPI RtlEqualUnicodeString (PUNICODE_STRING, PUNICODE_STRING,
BOOLEAN);
VOID NTAPI RtlFreeAnsiString (PANSI_STRING);
BOOLEAN NTAPI RtlFreeHeap (PVOID, ULONG, PVOID);
VOID NTAPI RtlFreeOemString (POEM_STRING);
VOID NTAPI RtlFreeUnicodeString (PUNICODE_STRING);
HANDLE NTAPI RtlGetCurrentTransaction ();
@ -1002,6 +1015,7 @@ extern "C"
VOID NTAPI RtlInitUnicodeString (PUNICODE_STRING, PCWSTR);
NTSTATUS NTAPI RtlIntegerToUnicodeString (ULONG, ULONG, PUNICODE_STRING);
ULONG NTAPI RtlIsDosDeviceName_U (PCWSTR);
NTSTATUS NTAPI RtlLeaveCriticalSection (PRTL_CRITICAL_SECTION);
ULONG NTAPI RtlNtStatusToDosError (NTSTATUS);
NTSTATUS NTAPI RtlOemStringToUnicodeString (PUNICODE_STRING, POEM_STRING,
BOOLEAN);

View File

@ -3353,10 +3353,282 @@ cygwin_split_path (const char *path, char *dir, char *file)
/*****************************************************************************/
static inline PRTL_USER_PROCESS_PARAMETERS
get_user_proc_parms ()
/* The find_fast_cwd_pointers function and parts of the
cwdstuff::override_win32_cwd method are based on code using the
following license:
Copyright 2010 John Carey. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
THIS SOFTWARE IS PROVIDED BY JOHN CAREY ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL JOHN CAREY OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE. */
/* This structure is used to store the CWD starting with Windows Vista.
The CWD storage in the RTL_USER_PROCESS_PARAMETERS block is only
an afterthought now. The actual CWD storage is a FAST_CWD structure
which is allocated on the process heap. The new method only requires
minimal locking and it's much more multi-thread friendly. Presumably
it minimizes contention when accessing the CWD. */
typedef struct _FAST_CWD {
LONG ReferenceCount; /* Only release when this is 0. */
HANDLE DirectoryHandle;
ULONG OldDismountCount; /* Reflects the system DismountCount
at the time the CWD has been set. */
UNICODE_STRING Path; /* Path's Buffer member always refers
to the following Buffer array. */
WCHAR Buffer[MAX_PATH];
} FAST_CWD, *PFAST_CWD;
/* fast_cwd_ptr is a pointer to the global pointer in ntdll.dll pointing
to the FAST_CWD structure which constitutes the CWD.
We put the pointer into the common shared DLL segment. This allows to
restrict the call to find_fast_cwd_pointers() to once per Cygwin session
per user session. This works, because ASLR randomizes the load address
of DLLs only once at boot time. */
static PFAST_CWD *fast_cwd_ptr
__attribute__((section (".cygwin_dll_common"), shared)) = (PFAST_CWD *) -1;
/* This is the mapping of the KUSER_SHARED_DATA structure into the 32 bit
user address space. We need it here to access the current DismountCount. */
static KUSER_SHARED_DATA &SharedUserData
= *(volatile PKUSER_SHARED_DATA) 0x7ffe0000;
#define peek32(x) (*(uint32_t *)(x))
/* This function scans the code in ntdll.dll to find the address of the
global variable used to access the CWD starting with Vista. While the
pointer is global, it's not exported from the DLL, unfortunately.
Therefore we have to use some knowledge to figure out the address.
This code has been tested on Vista 32/64 bit, Server 2008 32/64 bit,
Windows 7 32/64 bit, and Server 2008 R2 (which is only 64 bit anyway).
There's some hope that this will still work for Windows 8... */
static void
find_fast_cwd_pointers ()
{
return NtCurrentTeb ()->Peb->ProcessParameters;
/* Note that we have been called. */
fast_cwd_ptr = NULL;
/* Fetch entry points of relevant functions in ntdll.dll. */
HMODULE ntdll = GetModuleHandle ("ntdll.dll");
if (!ntdll)
return;
const uint8_t *get_dir = (const uint8_t *)
GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
const uint8_t *ent_crit = (const uint8_t *)
GetProcAddress (ntdll, "RtlEnterCriticalSection");
if (!get_dir || !ent_crit)
return;
/* Search first relative call instruction in RtlGetCurrentDirectory_U. */
const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 32);
if (!rcall)
return;
/* Fetch offset from instruction and compute address of called function.
This function actually fetches the current FAST_CWD instance and
performs some other actions, not important to us. */
ptrdiff_t offset = (ptrdiff_t) peek32 (rcall + 1);
const uint8_t *use_cwd = rcall + 5 + offset;
/* Find first "push edi" instruction. */
const uint8_t *pushedi = (const uint8_t *) memchr (use_cwd, 0x57, 32);
/* ...which should be followed by "mov edi, crit-sect-addr" then
"push edi". */
const uint8_t *movedi = pushedi + 1;
if (movedi[0] != 0xbf || movedi[5] != 0x57)
return;
/* Compare the address used for the critical section with the known
PEB lock as stored in the PEB. */
if ((PRTL_CRITICAL_SECTION) peek32 (movedi + 1)
!= NtCurrentTeb ()->Peb->FastPebLock)
return;
/* To check we are seeing the right code, we check our expectation that
the next instruction is a relative call into RtlEnterCriticalSection. */
rcall = movedi + 6;
if (rcall[0] != 0xe8)
return;
/* Check that this is a relative call to RtlEnterCriticalSection. */
offset = (ptrdiff_t) peek32 (rcall + 1);
if (rcall + 5 + offset != ent_crit)
return;
/* After locking the critical section, the code should read the global
PFAST_CWD * pointer that is guarded by that critical section. */
const uint8_t *movesi = rcall + 5;
if (movesi[0] != 0x8b)
return;
fast_cwd_ptr = (PFAST_CWD *) peek32 (movesi + 2);
#ifdef DEBUGGING
system_printf ("fast_cwd_ptr: %p", fast_cwd_ptr);
#endif
}
static inline void
copy_cwd_str (PUNICODE_STRING tgt, PUNICODE_STRING src)
{
RtlCopyUnicodeString (tgt, src);
if (tgt->Buffer[tgt->Length / sizeof (WCHAR) - 1] != L'\\')
{
tgt->Buffer[tgt->Length / sizeof (WCHAR)] = L'\\';
tgt->Length += sizeof (WCHAR);
}
}
void
cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count)
{
NTSTATUS status;
HANDLE h = NULL;
PEB &peb = *NtCurrentTeb ()->Peb;
UNICODE_STRING &upp_cwd_str = peb.ProcessParameters->CurrentDirectoryName;
HANDLE &upp_cwd_hdl = peb.ProcessParameters->CurrentDirectoryHandle;
if (wincap.has_fast_cwd ())
{
if (fast_cwd_ptr == (PFAST_CWD *) -1)
{
find_fast_cwd_pointers ();
if (!fast_cwd_ptr)
system_printf ("WARNING: Couldn't compute FAST_CWD pointer. "
"Please report this problem to\nthe public mailing "
"list cygwin@cygwin.com");
}
if (fast_cwd_ptr)
{
/* Default method starting with Vista. If we got a valid value for
fast_cwd_ptr, we can simply replace the RtlSetCurrentDirectory_U
function entirely, just as on pre-Vista. */
PVOID heap = peb.ProcessHeap;
/* First allocate a new FAST_CWD strcuture on the heap. */
PFAST_CWD f_cwd = (PFAST_CWD)
RtlAllocateHeap (heap, 0, sizeof (FAST_CWD));
if (!f_cwd)
{
debug_printf ("RtlAllocateHeap failed");
return;
}
/* Fill in the values. */
f_cwd->ReferenceCount = 1;
f_cwd->DirectoryHandle = dir;
f_cwd->OldDismountCount = old_dismount_count;
RtlInitEmptyUnicodeString (&f_cwd->Path, f_cwd->Buffer,
MAX_PATH * sizeof (WCHAR));
copy_cwd_str (&f_cwd->Path, error ? &ro_u_pipedir : &win32);
/* Use PEB lock when switching fast_cwd_ptr to the new FAST_CWD
structure and writing the CWD to the user process parameter
block. This is equivalent to calling RtlAcquirePebLock/
RtlReleasePebLock, but without having to go through the FS
selector again. */
RtlEnterCriticalSection (peb.FastPebLock);
PFAST_CWD old_cwd = *fast_cwd_ptr;
*fast_cwd_ptr = f_cwd;
upp_cwd_str = f_cwd->Path;
upp_cwd_hdl = dir;
RtlLeaveCriticalSection (peb.FastPebLock);
/* Decrement the reference count. If it's down to 0, free structure
from heap. */
if (old_cwd && InterlockedDecrement (&old_cwd->ReferenceCount) == 0)
{
/* In contrast to pre-Vista, the handle on init is always a fresh
one and not the handle inherited from the parent process. So
we always have to close it here. However, the handle could
be NULL, if we cd'ed into a virtual dir. */
if (old_cwd->DirectoryHandle)
NtClose (old_cwd->DirectoryHandle);
RtlFreeHeap (heap, 0, old_cwd);
}
}
else
{
/* This is more a hack, and it's only used on Vista and later if we
failed to find the fast_cwd_ptr value. What we do here is to call
RtlSetCurrentDirectory_U and let it set up a new FAST_CWD
structure. Afterwards, compute the address of that structure
utilizing the fact that the buffer address in the user process
parameter block is actually pointing to the buffer in that
FAST_CWD structure. Then replace the directory handle in that
structure with our own handle and close the original one.
Note that the call to RtlSetCurrentDirectory_U also closes our
old dir handle, so there won't be any handle left open.
This method is prone to two race conditions:
- Due to the way RtlSetCurrentDirectory_U opens the directory
handle, the directory is locked against deletion or renaming
between the RtlSetCurrentDirectory_U and the subsequent NtClose
call.
- When another thread calls SetCurrentDirectory at exactly the
same time, a crash might occur, or worse, unrelated data could
be overwritten or NtClose could be called on an unrelated handle.
Therefore, use this *only* as a fallback. */
if (!init)
{
status = RtlSetCurrentDirectory_U (error ? &ro_u_pipedir
: &win32);
if (!NT_SUCCESS (status))
{
debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %p",
error ? &ro_u_pipedir : &win32, status);
return;
}
}
RtlEnterCriticalSection (peb.FastPebLock);
PFAST_CWD f_cwd = (PFAST_CWD)
((PBYTE) upp_cwd_str.Buffer
- __builtin_offsetof (struct _FAST_CWD, Buffer));
h = upp_cwd_hdl;
f_cwd->DirectoryHandle = upp_cwd_hdl = dir;
RtlLeaveCriticalSection (peb.FastPebLock);
/* In contrast to pre-Vista, the handle on init is always a fresh one
and not the handle inherited from the parent process. So we always
have to close it here. */
NtClose (h);
}
}
else
{
/* This method is used for all pre-Vista OSes. We simply set the values
for the CWD in the user process parameter block entirely by ourselves
under PEB lock condition. This is how RtlSetCurrentDirectory_U worked
in these older OSes, so we're safe.
Note that we can't just RtlEnterCriticalSection (peb.FastPebLock)
on pre-Vista. RtlAcquirePebLock was way more complicated back then. */
RtlAcquirePebLock ();
if (!init)
copy_cwd_str (&upp_cwd_str, error ? &ro_u_pipedir : &win32);
h = upp_cwd_hdl;
upp_cwd_hdl = dir;
RtlReleasePebLock ();
/* Only on init, the handle is potentially a native handle. However,
if it's identical to dir, it's the inherited handle from a Cygwin
parent process and must not be closed. */
if (h && h != dir)
NtClose (h);
}
}
/* Initialize cygcwd 'muto' for serializing access to cwd info. */
@ -3366,12 +3638,13 @@ cwdstuff::init ()
cwd_lock.init ("cwd_lock");
/* Cygwin processes inherit the cwd from their parent. If the win32 path
buffer is not NULL, the cwd struct is already set up. */
buffer is not NULL, the cwd struct is already set up, and we only
have to override the Win32 CWD with ours. */
if (win32.Buffer)
return;
/* Initially re-open the cwd to allow POSIX semantics. */
set (NULL, NULL);
override_win32_cwd (true, SharedUserData.DismountCount);
else
/* Initially re-open the cwd to allow POSIX semantics. */
set (NULL, NULL);
}
/* Chdir and fill out the elements of a cwdstuff struct. */
@ -3380,6 +3653,7 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
{
NTSTATUS status;
UNICODE_STRING upath;
PEB &peb = *NtCurrentTeb ()->Peb;
bool virtual_path = false;
bool unc_path = false;
bool inaccessible_path = false;
@ -3430,6 +3704,13 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
virtual_path = true;
}
/* Memorize old DismountCount before opening the dir. This value is
stored in the FAST_CWD structure on Vista and later. It would be
simpler to fetch the old DismountCount in override_win32_cwd, but
Windows also fetches it before opening the directory handle. It's
not quite clear if that's really required, but since we don't know
the side effects of this action, we better follow Windows' lead. */
ULONG old_dismount_count = SharedUserData.DismountCount;
/* Open a directory handle with FILE_OPEN_FOR_BACKUP_INTENT and with all
sharing flags set. The handle is right now used in exceptions.cc only,
but that might change in future. */
@ -3447,7 +3728,7 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
RtlInitUnicodeString (&upath, L"");
InitializeObjectAttributes (&attr, &upath,
OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
get_user_proc_parms ()->CurrentDirectoryHandle, NULL);
peb.ProcessParameters->CurrentDirectoryHandle, NULL);
}
else
InitializeObjectAttributes (&attr, &upath,
@ -3477,9 +3758,10 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
return -1;
}
}
/* Set new handle. It's only used when creating stackdumps so far. */
if (dir)
NtClose (dir);
/* Set new handle. Note that we simply overwrite the old handle here
without closing it. The handle is also used as Win32 CWD handle in
the user parameter block, and it will be closed in override_win32_cwd,
if required. */
dir = h;
if (!nat_cwd)
@ -3487,7 +3769,7 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
/* On init, just fetch the Win32 dir from the PEB. We can access
the PEB without lock, because no other thread can change the CWD
at that time. */
PUNICODE_STRING pdir = &get_user_proc_parms ()->CurrentDirectoryName;
PUNICODE_STRING pdir = &peb.ProcessParameters->CurrentDirectoryName;
RtlInitEmptyUnicodeString (&win32,
(PWCHAR) crealloc_abort (win32.Buffer,
pdir->Length
@ -3568,13 +3850,7 @@ cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
}
/* Keep the Win32 CWD in sync. Don't check for error, other than for
strace output. Try to keep overhead low. */
if (nat_cwd)
{
status = RtlSetCurrentDirectory_U (error ? &ro_u_pipedir : &win32);
if (!NT_SUCCESS (status))
debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %p",
error ? &ro_u_pipedir : &win32, status);
}
override_win32_cwd (!nat_cwd, old_dismount_count);
/* Eventually, create POSIX path if it's not set on entry. */
tmp_pathbuf tp;

View File

@ -60,6 +60,7 @@ wincaps wincap_nt4sp4 __attribute__((section (".cygwin_dll_common"), shared)) =
has_localenames:false,
has_mwmo_inputavailable:false,
has_buggy_thread_startup:false,
has_fast_cwd:false,
};
wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -99,6 +100,7 @@ wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_localenames:false,
has_mwmo_inputavailable:true,
has_buggy_thread_startup:false,
has_fast_cwd:false,
};
wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -138,6 +140,7 @@ wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) =
has_localenames:false,
has_mwmo_inputavailable:true,
has_buggy_thread_startup:false,
has_fast_cwd:false,
};
wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -177,6 +180,7 @@ wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = {
has_localenames:false,
has_mwmo_inputavailable:true,
has_buggy_thread_startup:false,
has_fast_cwd:false,
};
wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -216,6 +220,7 @@ wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_localenames:false,
has_mwmo_inputavailable:true,
has_buggy_thread_startup:false,
has_fast_cwd:false,
};
wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -255,6 +260,7 @@ wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_localenames:false,
has_mwmo_inputavailable:true,
has_buggy_thread_startup:false,
has_fast_cwd:false,
};
wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -294,6 +300,7 @@ wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_localenames:false,
has_mwmo_inputavailable:true,
has_buggy_thread_startup:false,
has_fast_cwd:false,
};
wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -333,6 +340,7 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
has_localenames:true,
has_mwmo_inputavailable:true,
has_buggy_thread_startup:true,
has_fast_cwd:true,
};
wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
@ -372,6 +380,7 @@ wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
has_localenames:true,
has_mwmo_inputavailable:true,
has_buggy_thread_startup:false,
has_fast_cwd:true,
};
wincapc wincap __attribute__((section (".cygwin_dll_common"), shared));

View File

@ -50,6 +50,7 @@ struct wincaps
unsigned has_localenames : 1;
unsigned has_mwmo_inputavailable : 1;
unsigned has_buggy_thread_startup : 1;
unsigned has_fast_cwd : 1;
};
class wincapc
@ -105,6 +106,7 @@ public:
bool IMPLEMENT (has_localenames)
bool IMPLEMENT (has_mwmo_inputavailable)
bool IMPLEMENT (has_buggy_thread_startup)
bool IMPLEMENT (has_fast_cwd)
#undef IMPLEMENT
};