summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <vinschen@redhat.com>2006-07-13 08:34:54 +0000
committerCorinna Vinschen <vinschen@redhat.com>2006-07-13 08:34:54 +0000
commitdf457dc39eb25d1645bb867e7709b7bfbae468e5 (patch)
tree99f5aa2426e66d2c0e8dc7b95d05b26e581f7c8d
parent36c31208450a61ffc6e2f93908f74042c00fb43c (diff)
downloadgdb-df457dc39eb25d1645bb867e7709b7bfbae468e5.tar.gz
* Merge HEAD into cv-branch.
-rw-r--r--winsup/cygwin/ChangeLog16
-rw-r--r--winsup/cygwin/exceptions.cc1393
-rw-r--r--winsup/cygwin/mmap.cc2037
-rw-r--r--winsup/cygwin/winsup.h362
4 files changed, 3808 insertions, 0 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index 30bfa928a8f..f63f1117ec8 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,19 @@
+2006-07-13 Corinna Vinschen <corinna@vinschen.de>
+
+ * exceptions.cc (_cygtls::handle_exceptions): Call new
+ mmap_is_attached_or_noreserve_page function in case of access violation
+ and allow application to retry access on noreserve pages.
+ * mmap.cc (mmap_is_attached_or_noreserve_page): Changed from
+ mmap_is_attached_page. Handle also noreserve pages now. Change
+ comment accordingly.
+ * winsup.h (mmap_is_attached_or_noreserve_page): Declare instead of
+ mmap_is_attached_page.
+
+2006-07-12 Corinna Vinschen <corinna@vinschen.de>
+
+ * mmap.cc (mmap_record::alloc_page_map): Don't call VirtualProtect
+ on maps created with MAP_NORESERVE.
+
2006-07-12 Corinna Vinschen <corinna@vinschen.de>
* include/netdb.h: Declare rcmd, rcmd_af, rexec, rresvport,
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
new file mode 100644
index 00000000000..12c6e0687b5
--- /dev/null
+++ b/winsup/cygwin/exceptions.cc
@@ -0,0 +1,1393 @@
+/* exceptions.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006 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 <wingdi.h>
+#include <winuser.h>
+#include <imagehlp.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <assert.h>
+#include <syslog.h>
+
+#include "exceptions.h"
+#include "sync.h"
+#include "pinfo.h"
+#include "cygtls.h"
+#include "sigproc.h"
+#include "cygerrno.h"
+#include "shared_info.h"
+#include "perprocess.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "child_info.h"
+
+#define CALL_HANDLER_RETRY 20
+
+char debugger_command[2 * CYG_MAX_PATH + 20];
+
+extern "C" {
+extern void sigdelayed ();
+};
+
+extern child_info_spawn *chExeced;
+int NO_COPY sigExeced;
+
+static BOOL WINAPI ctrl_c_handler (DWORD);
+char windows_system_directory[1024];
+static size_t windows_system_directory_length;
+
+/* This is set to indicate that we have already exited. */
+
+static NO_COPY int exit_already = 0;
+static muto NO_COPY mask_sync;
+
+NO_COPY static struct
+{
+ unsigned int code;
+ const char *name;
+} status_info[] =
+{
+#define X(s) s, #s
+ { X (STATUS_ABANDONED_WAIT_0) },
+ { X (STATUS_ACCESS_VIOLATION) },
+ { X (STATUS_ARRAY_BOUNDS_EXCEEDED) },
+ { X (STATUS_BREAKPOINT) },
+ { X (STATUS_CONTROL_C_EXIT) },
+ { X (STATUS_DATATYPE_MISALIGNMENT) },
+ { X (STATUS_FLOAT_DENORMAL_OPERAND) },
+ { X (STATUS_FLOAT_DIVIDE_BY_ZERO) },
+ { X (STATUS_FLOAT_INEXACT_RESULT) },
+ { X (STATUS_FLOAT_INVALID_OPERATION) },
+ { X (STATUS_FLOAT_OVERFLOW) },
+ { X (STATUS_FLOAT_STACK_CHECK) },
+ { X (STATUS_FLOAT_UNDERFLOW) },
+ { X (STATUS_GUARD_PAGE_VIOLATION) },
+ { X (STATUS_ILLEGAL_INSTRUCTION) },
+ { X (STATUS_INTEGER_DIVIDE_BY_ZERO) },
+ { X (STATUS_INTEGER_OVERFLOW) },
+ { X (STATUS_INVALID_DISPOSITION) },
+ { X (STATUS_IN_PAGE_ERROR) },
+ { X (STATUS_NONCONTINUABLE_EXCEPTION) },
+ { X (STATUS_NO_MEMORY) },
+ { X (STATUS_PENDING) },
+ { X (STATUS_PRIVILEGED_INSTRUCTION) },
+ { X (STATUS_SINGLE_STEP) },
+ { X (STATUS_STACK_OVERFLOW) },
+ { X (STATUS_TIMEOUT) },
+ { X (STATUS_USER_APC) },
+ { X (STATUS_WAIT_0) },
+ { 0, 0 }
+#undef X
+};
+
+/* Initialization code. */
+
+BOOL WINAPI
+dummy_ctrl_c_handler (DWORD)
+{
+ return TRUE;
+}
+
+void
+init_console_handler (bool install_handler)
+{
+ BOOL res;
+
+ SetConsoleCtrlHandler (ctrl_c_handler, FALSE);
+ if (wincap.has_null_console_handler_routine ())
+ SetConsoleCtrlHandler (NULL, FALSE);
+ if (install_handler)
+ res = SetConsoleCtrlHandler (ctrl_c_handler, TRUE);
+ else if (wincap.has_null_console_handler_routine ())
+ res = SetConsoleCtrlHandler (NULL, TRUE);
+ else
+ res = SetConsoleCtrlHandler (dummy_ctrl_c_handler, TRUE);
+ if (!res)
+ system_printf ("SetConsoleCtrlHandler failed, %E");
+}
+
+extern "C" void
+error_start_init (const char *buf)
+{
+ if (!buf || !*buf)
+ {
+ debugger_command[0] = '\0';
+ return;
+ }
+
+ char pgm[CYG_MAX_PATH];
+ if (!GetModuleFileName (NULL, pgm, CYG_MAX_PATH))
+ strcpy (pgm, "cygwin1.dll");
+ for (char *p = strchr (pgm, '\\'); p; p = strchr (p, '\\'))
+ *p = '/';
+
+ __small_sprintf (debugger_command, "%s \"%s\"", buf, pgm);
+}
+
+static void
+open_stackdumpfile ()
+{
+ if (myself->progname[0])
+ {
+ const char *p;
+ /* write to progname.stackdump if possible */
+ if (!myself->progname[0])
+ p = "unknown";
+ else if ((p = strrchr (myself->progname, '\\')))
+ p++;
+ else
+ p = myself->progname;
+ char corefile[strlen (p) + sizeof (".stackdump")];
+ __small_sprintf (corefile, "%s.stackdump", p);
+ HANDLE h = CreateFile (corefile, GENERIC_WRITE, 0, &sec_none_nih,
+ CREATE_ALWAYS, 0, 0);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ if (!myself->cygstarted)
+ system_printf ("Dumping stack trace to %s", corefile);
+ else
+ debug_printf ("Dumping stack trace to %s", corefile);
+ SetStdHandle (STD_ERROR_HANDLE, h);
+ }
+ }
+}
+
+/* Utilities for dumping the stack, etc. */
+
+static void
+exception (EXCEPTION_RECORD *e, CONTEXT *in)
+{
+ const char *exception_name = NULL;
+
+ if (e)
+ {
+ for (int i = 0; status_info[i].name; i++)
+ {
+ if (status_info[i].code == e->ExceptionCode)
+ {
+ exception_name = status_info[i].name;
+ break;
+ }
+ }
+ }
+
+ if (exception_name)
+ small_printf ("Exception: %s at eip=%08x\r\n", exception_name, in->Eip);
+ else
+ small_printf ("Signal %d at eip=%08x\r\n", e->ExceptionCode, in->Eip);
+ small_printf ("eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\r\n",
+ in->Eax, in->Ebx, in->Ecx, in->Edx, in->Esi, in->Edi);
+ small_printf ("ebp=%08x esp=%08x program=%s, pid %u, thread %s\r\n",
+ in->Ebp, in->Esp, myself->progname, myself->pid, cygthread::name ());
+ small_printf ("cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\r\n",
+ in->SegCs, in->SegDs, in->SegEs, in->SegFs, in->SegGs, in->SegSs);
+}
+
+/* A class for manipulating the stack. */
+class stack_info
+{
+ int walk (); /* Uses the "old" method */
+ char *next_offset () {return *((char **) sf.AddrFrame.Offset);}
+ bool needargs;
+ DWORD dummy_frame;
+public:
+ STACKFRAME sf; /* For storing the stack information */
+ void init (DWORD, bool, bool); /* Called the first time that stack info is needed */
+
+ /* Postfix ++ iterates over the stack, returning zero when nothing is left. */
+ int operator ++(int) { return walk (); }
+};
+
+/* The number of parameters used in STACKFRAME */
+#define NPARAMS (sizeof (thestack.sf.Params) / sizeof (thestack.sf.Params[0]))
+
+/* This is the main stack frame info for this process. */
+static NO_COPY stack_info thestack;
+
+/* Initialize everything needed to start iterating. */
+void
+stack_info::init (DWORD ebp, bool wantargs, bool goodframe)
+{
+# define debp ((DWORD *) ebp)
+ memset (&sf, 0, sizeof (sf));
+ if (!goodframe)
+ sf.AddrFrame.Offset = ebp;
+ else
+ {
+ dummy_frame = ebp;
+ sf.AddrFrame.Offset = (DWORD) &dummy_frame;
+ }
+ sf.AddrReturn.Offset = debp[1];
+ sf.AddrFrame.Mode = AddrModeFlat;
+ needargs = wantargs;
+# undef debp
+}
+
+/* Walk the stack by looking at successive stored 'bp' frames.
+ This is not foolproof. */
+int
+stack_info::walk ()
+{
+ char **ebp;
+ if ((ebp = (char **) next_offset ()) == NULL)
+ return 0;
+
+ sf.AddrFrame.Offset = (DWORD) ebp;
+ sf.AddrPC.Offset = sf.AddrReturn.Offset;
+
+ if (!sf.AddrPC.Offset)
+ return 0; /* stack frames are exhausted */
+
+ /* The return address always follows the stack pointer */
+ sf.AddrReturn.Offset = (DWORD) *++ebp;
+
+ if (needargs)
+ /* The arguments follow the return address */
+ for (unsigned i = 0; i < NPARAMS; i++)
+ sf.Params[i] = (DWORD) *++ebp;
+
+ return 1;
+}
+
+static void
+stackdump (DWORD ebp, int open_file, bool isexception)
+{
+ extern unsigned long rlim_core;
+ static bool already_dumped;
+
+ if (rlim_core == 0UL || (open_file && already_dumped))
+ return;
+
+ if (open_file)
+ open_stackdumpfile ();
+
+ already_dumped = true;
+
+ int i;
+
+ thestack.init (ebp, 1, !isexception); /* Initialize from the input CONTEXT */
+ small_printf ("Stack trace:\r\nFrame Function Args\r\n");
+ for (i = 0; i < 16 && thestack++; i++)
+ {
+ small_printf ("%08x %08x ", thestack.sf.AddrFrame.Offset,
+ thestack.sf.AddrPC.Offset);
+ for (unsigned j = 0; j < NPARAMS; j++)
+ small_printf ("%s%08x", j == 0 ? " (" : ", ", thestack.sf.Params[j]);
+ small_printf (")\r\n");
+ }
+ small_printf ("End of stack trace%s\n",
+ i == 16 ? " (more stack frames may be present)" : "");
+}
+
+static bool
+inside_kernel (CONTEXT *cx)
+{
+ int res;
+ MEMORY_BASIC_INFORMATION m;
+
+ if (in_dllentry)
+ return true;
+
+ memset (&m, 0, sizeof m);
+ if (!VirtualQuery ((LPCVOID) cx->Eip, &m, sizeof m))
+ sigproc_printf ("couldn't get memory info, pc %p, %E", cx->Eip);
+
+ char *checkdir = (char *) alloca (windows_system_directory_length + 4);
+ memset (checkdir, 0, sizeof (checkdir));
+
+# define h ((HMODULE) m.AllocationBase)
+ /* Apparently Windows 95 can sometimes return bogus addresses from
+ GetThreadContext. These resolve to a strange allocation base.
+ These should *never* be treated as interruptible. */
+ if (!h || m.State != MEM_COMMIT)
+ res = true;
+ else if (h == user_data->hmodule)
+ res = false;
+ else if (!GetModuleFileName (h, checkdir, windows_system_directory_length + 2))
+ res = false;
+ else
+ res = strncasematch (windows_system_directory, checkdir,
+ windows_system_directory_length);
+ sigproc_printf ("pc %p, h %p, inside_kernel %d", cx->Eip, h, res);
+# undef h
+ return res;
+}
+
+/* Temporary (?) function for external callers to get a stack dump */
+extern "C" void
+cygwin_stackdump ()
+{
+ CONTEXT c;
+ c.ContextFlags = CONTEXT_FULL;
+ GetThreadContext (GetCurrentThread (), &c);
+ stackdump (c.Ebp, 0, 0);
+}
+
+#define TIME_TO_WAIT_FOR_DEBUGGER 10000
+
+extern "C" int
+try_to_debug (bool waitloop)
+{
+ debug_printf ("debugger_command '%s'", debugger_command);
+ if (*debugger_command == '\0')
+ return 0;
+ if (being_debugged ())
+ {
+ extern void break_here ();
+ break_here ();
+ return 0;
+ }
+
+ __small_sprintf (strchr (debugger_command, '\0'), " %u", GetCurrentProcessId ());
+
+ LONG prio = GetThreadPriority (GetCurrentThread ());
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
+ PROCESS_INFORMATION pi = {NULL, 0, 0, 0};
+
+ STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
+ si.lpReserved = NULL;
+ si.lpDesktop = NULL;
+ si.dwFlags = 0;
+ si.cb = sizeof (si);
+
+ /* FIXME: need to know handles of all running threads to
+ suspend_all_threads_except (current_thread_id);
+ */
+
+ /* if any of these mutexes is owned, we will fail to start any cygwin app
+ until trapped app exits */
+
+ lock_ttys::release ();
+
+ /* prevent recursive exception handling */
+ char* rawenv = GetEnvironmentStrings () ;
+ for (char* p = rawenv; *p != '\0'; p = strchr (p, '\0') + 1)
+ {
+ if (strncmp (p, "CYGWIN=", strlen ("CYGWIN=")) == 0)
+ {
+ char* q = strstr (p, "error_start") ;
+ /* replace 'error_start=...' with '_rror_start=...' */
+ if (q)
+ {
+ *q = '_' ;
+ SetEnvironmentVariable ("CYGWIN", p + strlen ("CYGWIN=")) ;
+ }
+ break ;
+ }
+ }
+
+ console_printf ("*** starting debugger for pid %u, tid %u\n",
+ cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ());
+ BOOL dbg;
+ dbg = CreateProcess (NULL,
+ debugger_command,
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
+ NULL,
+ NULL,
+ &si,
+ &pi);
+
+ if (!dbg)
+ system_printf ("Failed to start debugger, %E");
+ else
+ {
+ if (!waitloop)
+ return dbg;
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE);
+ while (!being_debugged ())
+ low_priority_sleep (0);
+ Sleep (2000);
+ }
+
+ console_printf ("*** continuing pid %u from debugger call (%d)\n",
+ cygwin_pid (GetCurrentProcessId ()), dbg);
+
+ SetThreadPriority (GetCurrentThread (), prio);
+ return dbg;
+}
+
+extern "C" DWORD __stdcall RtlUnwind (void *, void *, void *, DWORD);
+static void __stdcall rtl_unwind (exception_list *, PEXCEPTION_RECORD) __attribute__ ((noinline, regparm (3)));
+void __stdcall
+rtl_unwind (exception_list *frame, PEXCEPTION_RECORD e)
+{
+ __asm__ ("\n\
+ pushl %%ebx \n\
+ pushl %%edi \n\
+ pushl %%esi \n\
+ pushl $0 \n\
+ pushl %1 \n\
+ pushl $1f \n\
+ pushl %0 \n\
+ call _RtlUnwind@16 \n\
+1: \n\
+ popl %%esi \n\
+ popl %%edi \n\
+ popl %%ebx \n\
+": : "r" (frame), "r" (e));
+}
+
+/* Main exception handler. */
+
+extern "C" char *__progname;
+int
+_cygtls::handle_exceptions (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, void *)
+{
+ static bool NO_COPY debugging;
+ static int NO_COPY recursed;
+ _cygtls& me = _my_tls;
+
+ if (debugging && ++debugging < 500000)
+ {
+ SetThreadPriority (hMainThread, THREAD_PRIORITY_NORMAL);
+ return 0;
+ }
+
+ /* If we've already exited, don't do anything here. Returning 1
+ tells Windows to keep looking for an exception handler. */
+ if (exit_already || e->ExceptionFlags)
+ return 1;
+
+ siginfo_t si = {0};
+ si.si_code = SI_KERNEL;
+ /* Coerce win32 value to posix value. */
+ switch (e->ExceptionCode)
+ {
+ case STATUS_FLOAT_DENORMAL_OPERAND:
+ case STATUS_FLOAT_DIVIDE_BY_ZERO:
+ case STATUS_FLOAT_INVALID_OPERATION:
+ case STATUS_FLOAT_STACK_CHECK:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_FLTSUB;
+ break;
+ case STATUS_FLOAT_INEXACT_RESULT:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_FLTRES;
+ break;
+ case STATUS_FLOAT_OVERFLOW:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_FLTOVF;
+ break;
+ case STATUS_FLOAT_UNDERFLOW:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_FLTUND;
+ break;
+ case STATUS_INTEGER_DIVIDE_BY_ZERO:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_INTDIV;
+ break;
+ case STATUS_INTEGER_OVERFLOW:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_INTOVF;
+ break;
+
+ case STATUS_ILLEGAL_INSTRUCTION:
+ si.si_signo = SIGILL;
+ si.si_code = ILL_ILLOPC;
+ break;
+
+ case STATUS_PRIVILEGED_INSTRUCTION:
+ si.si_signo = SIGILL;
+ si.si_code = ILL_PRVOPC;
+ break;
+
+ case STATUS_NONCONTINUABLE_EXCEPTION:
+ si.si_signo = SIGILL;
+ si.si_code = ILL_ILLADR;
+ break;
+
+ case STATUS_TIMEOUT:
+ si.si_signo = SIGALRM;
+ break;
+
+ case STATUS_GUARD_PAGE_VIOLATION:
+ si.si_signo = SIGBUS;
+ si.si_code = BUS_OBJERR;
+ break;
+
+ case STATUS_DATATYPE_MISALIGNMENT:
+ si.si_signo = SIGBUS;
+ si.si_code = BUS_ADRALN;
+ break;
+
+ case STATUS_ACCESS_VIOLATION:
+ switch (mmap_is_attached_or_noreserve_page (e->ExceptionInformation[1]))
+ {
+ case 2: /* MAP_NORESERVE page, now commited. */
+ return 0;
+ case 1: /* MAP_NORESERVE page, commit failed, or
+ access to mmap page beyond EOF. */
+ si.si_signo = SIGBUS;
+ si.si_code = BUS_OBJERR;
+ break;
+ default:
+ MEMORY_BASIC_INFORMATION m;
+ VirtualQuery ((PVOID) e->ExceptionInformation[1], &m, sizeof m);
+ si.si_signo = SIGSEGV;
+ si.si_code = m.State == MEM_FREE ? SEGV_MAPERR : SEGV_ACCERR;
+ break;
+ }
+ break;
+
+ case STATUS_ARRAY_BOUNDS_EXCEEDED:
+ case STATUS_IN_PAGE_ERROR:
+ case STATUS_NO_MEMORY:
+ case STATUS_INVALID_DISPOSITION:
+ case STATUS_STACK_OVERFLOW:
+ si.si_signo = SIGSEGV;
+ si.si_code = SEGV_MAPERR;
+ break;
+
+ case STATUS_CONTROL_C_EXIT:
+ si.si_signo = SIGINT;
+ break;
+
+ case STATUS_INVALID_HANDLE:
+ /* CloseHandle will throw this exception if it is given an
+ invalid handle. We don't care about the exception; we just
+ want CloseHandle to return an error. This can be revisited
+ if gcc ever supports Windows style structured exception
+ handling. */
+ return 0;
+
+ default:
+ /* If we don't recognize the exception, we have to assume that
+ we are doing structured exception handling, and we let
+ something else handle it. */
+ return 1;
+ }
+
+ rtl_unwind (frame, e);
+
+ debug_printf ("In cygwin_except_handler exc %p at %p sp %p", e->ExceptionCode, in->Eip, in->Esp);
+ debug_printf ("In cygwin_except_handler sig %d at %p", si.si_signo, in->Eip);
+
+ if (global_sigs[si.si_signo].sa_mask & SIGTOMASK (si.si_signo))
+ syscall_printf ("signal %d, masked %p", si.si_signo,
+ global_sigs[si.si_signo].sa_mask);
+
+ debug_printf ("In cygwin_except_handler calling %p",
+ global_sigs[si.si_signo].sa_handler);
+
+ DWORD *ebp = (DWORD *) in->Esp;
+ for (DWORD *bpend = (DWORD *) __builtin_frame_address (0); ebp > bpend; ebp--)
+ if (*ebp == in->SegCs && ebp[-1] == in->Eip)
+ {
+ ebp -= 2;
+ break;
+ }
+
+ if (me.fault_guarded ())
+ me.return_from_fault ();
+
+ me.copy_context (in);
+ if (!cygwin_finished_initializing
+ || &me == _sig_tls
+ || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_DFL
+ || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_IGN
+ || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_ERR)
+ {
+ /* Print the exception to the console */
+ if (!myself->cygstarted)
+ for (int i = 0; status_info[i].name; i++)
+ if (status_info[i].code == e->ExceptionCode)
+ {
+ system_printf ("Exception: %s", status_info[i].name);
+ break;
+ }
+
+ /* Another exception could happen while tracing or while exiting.
+ Only do this once. */
+ if (recursed++)
+ system_printf ("Error while dumping state (probably corrupted stack)");
+ else
+ {
+ if (try_to_debug (0))
+ {
+ debugging = true;
+ return 0;
+ }
+
+ open_stackdumpfile ();
+ exception (e, in);
+ stackdump ((DWORD) ebp, 0, 1);
+ }
+
+ if (e->ExceptionCode == STATUS_ACCESS_VIOLATION)
+ {
+ int error_code = 0;
+ if (si.si_code == SEGV_ACCERR) /* Address present */
+ error_code |= 1;
+ if (e->ExceptionInformation[0]) /* Write access */
+ error_code |= 2;
+ if (!inside_kernel (in)) /* User space */
+ error_code |= 4;
+ klog (LOG_INFO, "%s[%d]: segfault at %08x rip %08x rsp %08x error %d",
+ __progname, myself->pid,
+ e->ExceptionInformation[1], in->Eip, in->Esp,
+ ((in->Eip >= 0x61000000 && in->Eip < 0x61200000)
+ ? 0 : 4) | (e->ExceptionInformation[0] << 1));
+ }
+
+ me.signal_exit (0x80 | si.si_signo); // Flag signal + core dump
+ }
+
+ si.si_addr = (void *) in->Eip;
+ si.si_errno = si.si_pid = si.si_uid = 0;
+ me.incyg++;
+ sig_send (NULL, si, &me); // Signal myself
+ me.incyg--;
+ e->ExceptionFlags = 0;
+ return 0;
+}
+
+/* Utilities to call a user supplied exception handler. */
+
+#define SIG_NONMASKABLE (SIGTOMASK (SIGKILL) | SIGTOMASK (SIGSTOP))
+
+/* Non-raceable sigsuspend
+ * Note: This implementation is based on the Single UNIX Specification
+ * man page. This indicates that sigsuspend always returns -1 and that
+ * attempts to block unblockable signals will be silently ignored.
+ * This is counter to what appears to be documented in some UNIX
+ * man pages, e.g. Linux.
+ */
+int __stdcall
+handle_sigsuspend (sigset_t tempmask)
+{
+ if (&_my_tls != _main_tls)
+ {
+ cancelable_wait (signal_arrived, INFINITE, cw_cancel_self);
+ return -1;
+ }
+
+ sigset_t oldmask = myself->getsigmask (); // Remember for restoration
+
+ set_signal_mask (tempmask, myself->getsigmask ());
+ sigproc_printf ("oldmask %p, newmask %p", oldmask, tempmask);
+
+ pthread_testcancel ();
+ cancelable_wait (signal_arrived, INFINITE);
+
+ set_sig_errno (EINTR); // Per POSIX
+
+ /* A signal dispatch function will have been added to our stack and will
+ be hit eventually. Set the old mask to be restored when the signal
+ handler returns and indicate its presence by modifying deltamask. */
+
+ _my_tls.deltamask |= SIG_NONMASKABLE;
+ _my_tls.oldmask = oldmask; // Will be restored by signal handler
+ return -1;
+}
+
+extern DWORD exec_exit; // Possible exit value for exec
+
+extern "C" {
+static void
+sig_handle_tty_stop (int sig)
+{
+ _my_tls.incyg = 1;
+ /* Silently ignore attempts to suspend if there is no accommodating
+ cygwin parent to deal with this behavior. */
+ if (!myself->cygstarted)
+ {
+ myself->process_state &= ~PID_STOPPED;
+ return;
+ }
+
+ myself->stopsig = sig;
+ myself->alert_parent (sig);
+ sigproc_printf ("process %d stopped by signal %d", myself->pid, sig);
+ HANDLE w4[2];
+ w4[0] = sigCONT;
+ w4[1] = signal_arrived;
+ switch (WaitForMultipleObjects (2, w4, TRUE, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ case WAIT_OBJECT_0 + 1:
+ reset_signal_arrived ();
+ myself->alert_parent (SIGCONT);
+ break;
+ default:
+ api_fatal ("WaitSingleObject failed, %E");
+ break;
+ }
+ _my_tls.incyg = 0;
+}
+}
+
+bool
+_cygtls::interrupt_now (CONTEXT *cx, int sig, void *handler,
+ struct sigaction& siga)
+{
+ bool interrupted;
+
+ if (incyg || spinning || locked () || inside_kernel (cx))
+ interrupted = false;
+ else
+ {
+ push ((__stack_t) cx->Eip);
+ interrupt_setup (sig, handler, siga);
+ cx->Eip = pop ();
+ SetThreadContext (*this, cx); /* Restart the thread in a new location */
+ interrupted = true;
+ }
+ return interrupted;
+}
+
+void __stdcall
+_cygtls::interrupt_setup (int sig, void *handler, struct sigaction& siga)
+{
+ push ((__stack_t) sigdelayed);
+ deltamask = siga.sa_mask & ~SIG_NONMASKABLE;
+ sa_flags = siga.sa_flags;
+ func = (void (*) (int)) handler;
+ if (siga.sa_flags & SA_RESETHAND)
+ siga.sa_handler = SIG_DFL;
+ saved_errno = -1; // Flag: no errno to save
+ if (handler == sig_handle_tty_stop)
+ {
+ myself->stopsig = 0;
+ myself->process_state |= PID_STOPPED;
+ }
+
+ this->sig = sig; // Should always be last thing set to avoid a race
+
+ if (!event)
+ threadkill = false;
+ else
+ {
+ HANDLE h = event;
+ event = NULL;
+ SetEvent (h);
+ }
+
+ /* Clear any waiting threads prior to dispatching to handler function */
+ int res = SetEvent (signal_arrived); // For an EINTR case
+ proc_subproc (PROC_CLEARWAIT, 1);
+ sigproc_printf ("armed signal_arrived %p, sig %d, res %d", signal_arrived,
+ sig, res);
+}
+
+extern "C" void __stdcall
+set_sig_errno (int e)
+{
+ *_my_tls.errno_addr = e;
+ _my_tls.saved_errno = e;
+ // sigproc_printf ("errno %d", e);
+}
+
+static int setup_handler (int, void *, struct sigaction&, _cygtls *tls)
+ __attribute__((regparm(3)));
+static int
+setup_handler (int sig, void *handler, struct sigaction& siga, _cygtls *tls)
+{
+ CONTEXT cx;
+ bool interrupted = false;
+
+ if (tls->sig)
+ {
+ sigproc_printf ("trying to send sig %d but signal %d already armed",
+ sig, tls->sig);
+ goto out;
+ }
+
+ for (int i = 0; i < CALL_HANDLER_RETRY; i++)
+ {
+ tls->lock ();
+ if (tls->incyg)
+ {
+ sigproc_printf ("controlled interrupt. stackptr %p, stack %p, stackptr[-1] %p",
+ tls->stackptr, tls->stack, tls->stackptr[-1]);
+ tls->interrupt_setup (sig, handler, siga);
+ interrupted = true;
+ tls->unlock ();
+ break;
+ }
+
+ tls->unlock ();
+ DWORD res;
+ HANDLE hth = (HANDLE) *tls;
+
+ /* Suspend the thread which will receive the signal.
+ For Windows 95, we also have to ensure that the addresses returned by
+ GetThreadContext are valid.
+ If one of these conditions is not true we loop for a fixed number of times
+ since we don't want to stall the signal handler. FIXME: Will this result in
+ noticeable delays?
+ If the thread is already suspended (which can occur when a program has called
+ SuspendThread on itself) then just queue the signal. */
+
+#ifndef DEBUGGING
+ sigproc_printf ("suspending mainthread");
+#else
+ cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
+ if (!GetThreadContext (hth, &cx))
+ memset (&cx, 0, sizeof cx);
+ sigproc_printf ("suspending mainthread PC %p", cx.Eip);
+#endif
+ res = SuspendThread (hth);
+ /* Just set pending if thread is already suspended */
+ if (res)
+ {
+ ResumeThread (hth);
+ break;
+ }
+ cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
+ if (!GetThreadContext (hth, &cx))
+ system_printf ("couldn't get context of main thread, %E");
+ else
+ interrupted = tls->interrupt_now (&cx, sig, handler, siga);
+
+ res = ResumeThread (hth);
+ if (interrupted)
+ break;
+
+ sigproc_printf ("couldn't interrupt. trying again.");
+ low_priority_sleep (0);
+ }
+
+out:
+ sigproc_printf ("signal %d %sdelivered", sig, interrupted ? "" : "not ");
+ return interrupted;
+}
+
+static inline bool
+has_visible_window_station ()
+{
+ HWINSTA station_hdl;
+ USEROBJECTFLAGS uof;
+ DWORD len;
+
+ /* Check if the process is associated with a visible window station.
+ These are processes running on the local desktop as well as processes
+ running in terminal server sessions.
+ Processes running in a service session not explicitely associated
+ with the desktop (using the "Allow service to interact with desktop"
+ property) are running in an invisible window station. */
+ if ((station_hdl = GetProcessWindowStation ())
+ && GetUserObjectInformationA (station_hdl, UOI_FLAGS, &uof,
+ sizeof uof, &len)
+ && (uof.dwFlags & WSF_VISIBLE))
+ return true;
+ return false;
+}
+
+/* Keyboard interrupt handler. */
+static BOOL WINAPI
+ctrl_c_handler (DWORD type)
+{
+ static bool saw_close;
+
+ if (!cygwin_finished_initializing)
+ {
+ if (myself->cygstarted) /* Was this process created by a cygwin process? */
+ return TRUE; /* Yes. Let the parent eventually handle CTRL-C issues. */
+ debug_printf ("exiting with status %p", STATUS_CONTROL_C_EXIT);
+ ExitProcess (STATUS_CONTROL_C_EXIT);
+ }
+
+ _my_tls.remove (INFINITE);
+
+#if 0
+ if (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT)
+ proc_subproc (PROC_KILLFORKED, 0);
+#endif
+
+ /* Return FALSE to prevent an "End task" dialog box from appearing
+ for each Cygwin process window that's open when the computer
+ is shut down or console window is closed. */
+
+ if (type == CTRL_SHUTDOWN_EVENT)
+ {
+#if 0
+ /* Don't send a signal. Only NT service applications and their child
+ processes will receive this event and the services typically already
+ handle the shutdown action when getting the SERVICE_CONTROL_SHUTDOWN
+ control message. */
+ sig_send (NULL, SIGTERM);
+#endif
+ return FALSE;
+ }
+
+ if (myself->ctty != -1)
+ {
+ if (type == CTRL_CLOSE_EVENT)
+ {
+ sig_send (NULL, SIGHUP);
+ saw_close = true;
+ return FALSE;
+ }
+ if (!saw_close && type == CTRL_LOGOFF_EVENT)
+ {
+ /* The CTRL_LOGOFF_EVENT is sent when *any* user logs off.
+ The below code sends a SIGHUP only if it is not performing the
+ default activity for SIGHUP. Note that it is possible for two
+ SIGHUP signals to arrive if a process group leader is exiting
+ too. Getting this 100% right is saved for a future cygwin mailing
+ list goad. */
+ if (global_sigs[SIGHUP].sa_handler != SIG_DFL)
+ {
+ sig_send (myself_nowait, SIGHUP);
+ return TRUE;
+ }
+ return FALSE;
+ }
+ }
+
+ if (chExeced)
+ {
+ chExeced->set_saw_ctrl_c ();
+ return TRUE;
+ }
+
+ /* We're only the process group leader when we have a valid pinfo structure.
+ If we don't have one, then the parent "stub" will handle the signal. */
+ if (!pinfo (cygwin_pid (GetCurrentProcessId ())))
+ return TRUE;
+
+ tty_min *t = cygwin_shared->tty.get_tty (myself->ctty);
+ /* Ignore this if we're not the process group leader since it should be handled
+ *by* the process group leader. */
+ if (myself->ctty != -1 && t->getpgid () == myself->pid &&
+ (GetTickCount () - t->last_ctrl_c) >= MIN_CTRL_C_SLOP)
+ /* Otherwise we just send a SIGINT to the process group and return TRUE (to indicate
+ that we have handled the signal). At this point, type should be
+ a CTRL_C_EVENT or CTRL_BREAK_EVENT. */
+ {
+ int sig = SIGINT;
+ /* If intr and quit are both mapped to ^C, send SIGQUIT on ^BREAK */
+ if (type == CTRL_BREAK_EVENT
+ && t->ti.c_cc[VINTR] == 3 && t->ti.c_cc[VQUIT] == 3)
+ sig = SIGQUIT;
+ t->last_ctrl_c = GetTickCount ();
+ killsys (-myself->pid, sig);
+ t->last_ctrl_c = GetTickCount ();
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+/* Function used by low level sig wrappers. */
+extern "C" void __stdcall
+set_process_mask (sigset_t newmask)
+{
+ set_signal_mask (newmask, myself->getsigmask ());
+sigproc_printf ("mask now %p\n", myself->getsigmask ());
+}
+
+extern "C" int
+sighold (int sig)
+{
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("signal %d out of range", sig);
+ return -1;
+ }
+ mask_sync.acquire (INFINITE);
+ sigset_t mask = myself->getsigmask ();
+ sigaddset (&mask, sig);
+ set_signal_mask (mask, myself->getsigmask ());
+ mask_sync.release ();
+ return 0;
+}
+
+extern "C" int
+sigrelse (int sig)
+{
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("signal %d out of range", sig);
+ return -1;
+ }
+ mask_sync.acquire (INFINITE);
+ sigset_t mask = myself->getsigmask ();
+ sigdelset (&mask, sig);
+ set_signal_mask (mask, myself->getsigmask ());
+ mask_sync.release ();
+ return 0;
+}
+
+extern "C" _sig_func_ptr
+sigset (int sig, _sig_func_ptr func)
+{
+ sig_dispatch_pending ();
+ _sig_func_ptr prev;
+
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG || sig == SIGKILL || sig == SIGSTOP)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("SIG_ERR = sigset (%d, %p)", sig, func);
+ return (_sig_func_ptr) SIG_ERR;
+ }
+
+ mask_sync.acquire (INFINITE);
+ sigset_t mask = myself->getsigmask ();
+ /* If sig was in the signal mask return SIG_HOLD, otherwise return the
+ previous disposition. */
+ if (sigismember (&mask, sig))
+ prev = SIG_HOLD;
+ else
+ prev = global_sigs[sig].sa_handler;
+ /* If func is SIG_HOLD, add sig to the signal mask, otherwise set the
+ disposition to func and remove sig from the signal mask. */
+ if (func == SIG_HOLD)
+ sigaddset (&mask, sig);
+ else
+ {
+ /* No error checking. The test which could return SIG_ERR has already
+ been made above. */
+ signal (sig, func);
+ sigdelset (&mask, sig);
+ }
+ set_signal_mask (mask, myself->getsigmask ());
+ mask_sync.release ();
+ return prev;
+}
+
+extern "C" int
+sigignore (int sig)
+{
+ return sigset (sig, SIG_IGN) == SIG_ERR ? -1 : 0;
+}
+
+/* Update the signal mask for this process and return the old mask.
+ Called from sigdelayed */
+extern "C" sigset_t
+set_process_mask_delta ()
+{
+ mask_sync.acquire (INFINITE);
+ sigset_t newmask, oldmask;
+
+ if (_my_tls.deltamask & SIG_NONMASKABLE)
+ oldmask = _my_tls.oldmask; /* from handle_sigsuspend */
+ else
+ oldmask = myself->getsigmask ();
+ newmask = (oldmask | _my_tls.deltamask) & ~SIG_NONMASKABLE;
+ sigproc_printf ("oldmask %p, newmask %p, deltamask %p", oldmask, newmask,
+ _my_tls.deltamask);
+ myself->setsigmask (newmask);
+ mask_sync.release ();
+ return oldmask;
+}
+
+/* Set the signal mask for this process.
+ Note that some signals are unmaskable, as in UNIX. */
+extern "C" void __stdcall
+set_signal_mask (sigset_t newmask, sigset_t& oldmask)
+{
+#ifdef CGF
+ if (&_my_tls == _sig_tls)
+ small_printf ("********* waiting in signal thread\n");
+#endif
+ mask_sync.acquire (INFINITE);
+ newmask &= ~SIG_NONMASKABLE;
+ sigset_t mask_bits = oldmask & ~newmask;
+ sigproc_printf ("oldmask %p, newmask %p, mask_bits %p", oldmask, newmask,
+ mask_bits);
+ oldmask = newmask;
+ if (mask_bits)
+ sig_dispatch_pending (true);
+ else
+ sigproc_printf ("not calling sig_dispatch_pending");
+ mask_sync.release ();
+}
+
+int __stdcall
+sigpacket::process ()
+{
+ DWORD continue_now;
+ if (si.si_signo != SIGCONT)
+ continue_now = false;
+ else
+ {
+ continue_now = myself->process_state & PID_STOPPED;
+ myself->stopsig = 0;
+ myself->process_state &= ~PID_STOPPED;
+ /* Clear pending stop signals */
+ sig_clear (SIGSTOP);
+ sig_clear (SIGTSTP);
+ sig_clear (SIGTTIN);
+ sig_clear (SIGTTOU);
+ }
+
+ int rc = 1;
+
+ sigproc_printf ("signal %d processing", si.si_signo);
+ struct sigaction& thissig = global_sigs[si.si_signo];
+
+ myself->rusage_self.ru_nsignals++;
+
+ bool masked;
+ void *handler;
+ if (!hExeced || (void *) thissig.sa_handler == (void *) SIG_IGN)
+ handler = (void *) thissig.sa_handler;
+ else if (tls)
+ return 1;
+ else
+ handler = NULL;
+
+ if (si.si_signo == SIGKILL)
+ goto exit_sig;
+ if (si.si_signo == SIGSTOP)
+ {
+ sig_clear (SIGCONT);
+ if (!tls)
+ tls = _main_tls;
+ goto stop;
+ }
+
+ bool insigwait_mask;
+ if ((masked = ISSTATE (myself, PID_STOPPED)))
+ insigwait_mask = false;
+ else if (!tls)
+ insigwait_mask = !handler && (tls = _cygtls::find_tls (si.si_signo));
+ else
+ insigwait_mask = sigismember (&tls->sigwait_mask, si.si_signo);
+
+ if (insigwait_mask)
+ goto thread_specific;
+
+ if (masked)
+ /* nothing to do */;
+ else if (sigismember (mask, si.si_signo))
+ masked = true;
+ else if (tls)
+ masked = sigismember (&tls->sigmask, si.si_signo);
+
+ if (!tls)
+ tls = _main_tls;
+
+ if (masked)
+ {
+ sigproc_printf ("signal %d blocked", si.si_signo);
+ rc = -1;
+ goto done;
+ }
+
+ /* Clear pending SIGCONT on stop signals */
+ if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU)
+ sig_clear (SIGCONT);
+
+#ifdef CGF
+ if (being_debugged ())
+ {
+ char sigmsg[sizeof (_CYGWIN_SIGNAL_STRING " 0xffffffff")];
+ __small_sprintf (sigmsg, _CYGWIN_SIGNAL_STRING " %p", si.si_signo);
+ OutputDebugString (sigmsg);
+ }
+#endif
+
+ if (handler == (void *) SIG_DFL)
+ {
+ if (insigwait_mask)
+ goto thread_specific;
+ if (si.si_signo == SIGCHLD || si.si_signo == SIGIO || si.si_signo == SIGCONT || si.si_signo == SIGWINCH
+ || si.si_signo == SIGURG)
+ {
+ sigproc_printf ("default signal %d ignored", si.si_signo);
+ if (continue_now)
+ SetEvent (signal_arrived);
+ goto done;
+ }
+
+ if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU)
+ goto stop;
+
+ goto exit_sig;
+ }
+
+ if (handler == (void *) SIG_IGN)
+ {
+ sigproc_printf ("signal %d ignored", si.si_signo);
+ goto done;
+ }
+
+ if (handler == (void *) SIG_ERR)
+ goto exit_sig;
+
+ tls->set_siginfo (this);
+ goto dosig;
+
+stop:
+ /* Eat multiple attempts to STOP */
+ if (ISSTATE (myself, PID_STOPPED))
+ goto done;
+ handler = (void *) sig_handle_tty_stop;
+ struct sigaction dummy = global_sigs[SIGSTOP];
+ thissig = dummy;
+
+dosig:
+ /* Dispatch to the appropriate function. */
+ sigproc_printf ("signal %d, about to call %p", si.si_signo, handler);
+ rc = setup_handler (si.si_signo, handler, thissig, tls);
+
+done:
+ if (continue_now)
+ SetEvent (sigCONT);
+ sigproc_printf ("returning %d", rc);
+ return rc;
+
+thread_specific:
+ tls->sig = si.si_signo;
+ tls->set_siginfo (this);
+ sigproc_printf ("releasing sigwait for thread");
+ SetEvent (tls->event);
+ goto done;
+
+exit_sig:
+ if (si.si_signo == SIGQUIT || si.si_signo == SIGABRT)
+ {
+ CONTEXT c;
+ c.ContextFlags = CONTEXT_FULL;
+ GetThreadContext (hMainThread, &c);
+ tls->copy_context (&c);
+ si.si_signo |= 0x80;
+ }
+ sigproc_printf ("signal %d, about to call do_exit", si.si_signo);
+ tls->signal_exit (si.si_signo); /* never returns */
+}
+
+/* Cover function to `do_exit' to handle exiting even in presence of more
+ exceptions. We used to call exit, but a SIGSEGV shouldn't cause atexit
+ routines to run. */
+void
+_cygtls::signal_exit (int rc)
+{
+ if (hExeced)
+ {
+ sigproc_printf ("terminating captive process");
+ TerminateProcess (hExeced, sigExeced = rc);
+ }
+
+ signal_debugger (rc & 0x7f);
+ if ((rc & 0x80) && !try_to_debug ())
+ stackdump (thread_context.ebp, 1, 1);
+
+ lock_process until_exit (true);
+ if (hExeced || exit_state)
+ myself.exit (rc);
+
+ /* Starve other threads in a vain attempt to stop them from doing something
+ stupid. */
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
+
+ user_data->resourcelocks->Delete ();
+ user_data->resourcelocks->Init ();
+
+ sigproc_printf ("about to call do_exit (%x)", rc);
+ SetEvent (signal_arrived);
+ do_exit (rc);
+}
+
+void
+events_init ()
+{
+ mask_sync.init ("mask_sync");
+ windows_system_directory[0] = '\0';
+ GetSystemDirectory (windows_system_directory, sizeof (windows_system_directory) - 2);
+ char *end = strchr (windows_system_directory, '\0');
+ if (end == windows_system_directory)
+ api_fatal ("can't find windows system directory");
+ if (end[-1] != '\\')
+ {
+ *end++ = '\\';
+ *end = '\0';
+ }
+ windows_system_directory_length = end - windows_system_directory;
+ debug_printf ("windows_system_directory '%s', windows_system_directory_length %d",
+ windows_system_directory, windows_system_directory_length);
+}
+
+void
+events_terminate ()
+{
+ exit_already = 1;
+}
+
+int
+_cygtls::call_signal_handler ()
+{
+ int this_sa_flags = 0;
+ /* Call signal handler. */
+ while (sig)
+ {
+ lock ();
+ this_sa_flags = sa_flags;
+ int thissig = sig;
+
+ pop ();
+ reset_signal_arrived ();
+ sigset_t this_oldmask = set_process_mask_delta ();
+ int this_errno = saved_errno;
+ sig = 0;
+ unlock (); // make sure synchronized
+ incyg = 0;
+ if (!(this_sa_flags & SA_SIGINFO))
+ {
+ void (*sigfunc) (int) = func;
+ sigfunc (thissig);
+ }
+ else
+ {
+ siginfo_t thissi = infodata;
+ void (*sigact) (int, siginfo_t *, void *) = (void (*) (int, siginfo_t *, void *)) func;
+ /* no ucontext_t information provided yet */
+ sigact (thissig, &thissi, NULL);
+ }
+ incyg = 1;
+ set_signal_mask (this_oldmask, myself->getsigmask ());
+ if (this_errno >= 0)
+ set_errno (this_errno);
+ }
+
+ return this_sa_flags & SA_RESTART;
+}
+
+extern "C" void __stdcall
+reset_signal_arrived ()
+{
+ // NEEDED? WaitForSingleObject (signal_arrived, 10);
+ ResetEvent (signal_arrived);
+ sigproc_printf ("reset signal_arrived");
+ if (_my_tls.stackptr > _my_tls.stack)
+ debug_printf ("stackptr[-1] %p", _my_tls.stackptr[-1]);
+}
+
+void
+_cygtls::copy_context (CONTEXT *c)
+{
+ memcpy (&thread_context, c, (&thread_context._internal - (unsigned char *) &thread_context));
+}
+
+void
+_cygtls::signal_debugger (int sig)
+{
+ if (isinitialized () && being_debugged ())
+ {
+ char sigmsg[2 * sizeof (_CYGWIN_SIGNAL_STRING " ffffffff ffffffff")];
+ __small_sprintf (sigmsg, _CYGWIN_SIGNAL_STRING " %d %p %p", sig, thread_id, &thread_context);
+ OutputDebugString (sigmsg);
+ }
+}
diff --git a/winsup/cygwin/mmap.cc b/winsup/cygwin/mmap.cc
new file mode 100644
index 00000000000..193ed7c0f22
--- /dev/null
+++ b/winsup/cygwin/mmap.cc
@@ -0,0 +1,2037 @@
+/* mmap.cc
+
+ Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005,
+ 2006 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 <unistd.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "pinfo.h"
+#include "sys/cygwin.h"
+#include "ntdll.h"
+
+/* __PROT_ATTACH indicates an anonymous mapping which is supposed to be
+ attached to a file mapping for pages beyond the file's EOF. The idea
+ is to support mappings longer than the file, without the file growing
+ to mapping length (POSIX semantics). */
+#define __PROT_ATTACH 0x8000000
+/* Filler pages are the pages from the last file backed page to the next
+ 64K boundary. These pages are created as anonymous pages, but with
+ the same page protection as the file's pages, since POSIX applications
+ expect to be able to access this part the same way as the file pages. */
+#define __PROT_FILLER 0x4000000
+
+#define PAGE_CNT(bytes) howmany((bytes),getpagesize())
+
+#define PGBITS (sizeof (DWORD)*8)
+#define MAPSIZE(pages) howmany ((pages), PGBITS)
+
+#define MAP_SET(n) (page_map[(n)/PGBITS] |= (1L << ((n) % PGBITS)))
+#define MAP_CLR(n) (page_map[(n)/PGBITS] &= ~(1L << ((n) % PGBITS)))
+#define MAP_ISSET(n) (page_map[(n)/PGBITS] & (1L << ((n) % PGBITS)))
+
+/* Used for anonymous mappings. */
+static fhandler_dev_zero fh_anonymous;
+/* Used for reopening a disk file when necessary. */
+static fhandler_disk_file fh_disk_file;
+
+/* Small helpers to avoid having lots of flag bit tests in the code. */
+static inline bool
+priv (int flags)
+{
+ return (flags & MAP_PRIVATE) == MAP_PRIVATE;
+}
+
+static inline bool
+fixed (int flags)
+{
+ return (flags & MAP_FIXED) == MAP_FIXED;
+}
+
+static inline bool
+anonymous (int flags)
+{
+ return (flags & MAP_ANONYMOUS) == MAP_ANONYMOUS;
+}
+
+static inline bool
+noreserve (int flags)
+{
+ return (flags & MAP_NORESERVE) == MAP_NORESERVE;
+}
+
+static inline bool
+autogrow (int flags)
+{
+ return (flags & MAP_AUTOGROW) == MAP_AUTOGROW;
+}
+
+static inline bool
+attached (int prot)
+{
+ return (prot & __PROT_ATTACH) == __PROT_ATTACH;
+}
+
+static inline bool
+filler (int prot)
+{
+ return (prot & __PROT_FILLER) == __PROT_FILLER;
+}
+
+static inline DWORD
+gen_create_protect (DWORD openflags, int flags)
+{
+ DWORD ret = PAGE_READONLY;
+
+ if (priv (flags))
+ ret = PAGE_WRITECOPY;
+ else if (openflags & GENERIC_WRITE)
+ ret = PAGE_READWRITE;
+
+ /* Ignore EXECUTE permission on 9x. */
+ if ((openflags & GENERIC_EXECUTE)
+ && wincap.virtual_protect_works_on_shared_pages ())
+ ret <<= 4;
+
+ return ret;
+}
+
+/* Generate Windows protection flags from mmap prot and flag values. */
+static inline DWORD
+gen_protect (int prot, int flags)
+{
+ DWORD ret = PAGE_NOACCESS;
+
+ /* Attached pages are only reserved, but the protection must be a
+ valid value, so we just return PAGE_READWRITE. */
+ if (attached (prot))
+ return PAGE_EXECUTE_READWRITE;
+
+ if (prot & PROT_WRITE)
+ ret = (priv (flags) && (!anonymous (flags) || filler (prot)))
+ ? PAGE_WRITECOPY : PAGE_READWRITE;
+ else if (prot & PROT_READ)
+ ret = PAGE_READONLY;
+
+ /* Ignore EXECUTE permission on 9x. */
+ if ((prot & PROT_EXEC)
+ && wincap.virtual_protect_works_on_shared_pages ())
+ ret <<= 4;
+
+ return ret;
+}
+
+/* Generate Windows access flags from mmap prot and flag values.
+ Only used on 9x. PROT_EXEC not supported here since it's not
+ necessary. */
+static inline DWORD
+gen_access (DWORD openflags, int flags)
+{
+ DWORD ret = FILE_MAP_READ;
+ if (priv (flags))
+ ret = FILE_MAP_COPY;
+ else if (openflags & GENERIC_WRITE)
+ ret = priv (flags) ? FILE_MAP_COPY : FILE_MAP_WRITE;
+ return ret;
+}
+
+/* OS specific wrapper functions for map/section functions. */
+static BOOL
+VirtualProt9x (PVOID addr, SIZE_T len, DWORD prot, PDWORD oldprot)
+{
+ if (addr >= (caddr_t)0x80000000 && addr <= (caddr_t)0xBFFFFFFF)
+ return TRUE; /* FAKEALARM! */
+ return VirtualProtect (addr, len, prot, oldprot);
+}
+
+static BOOL
+VirtualProtNT (PVOID addr, SIZE_T len, DWORD prot, PDWORD oldprot)
+{
+ return VirtualProtect (addr, len, prot, oldprot);
+}
+
+static BOOL
+VirtualProtEx9x (HANDLE parent, PVOID addr, SIZE_T len, DWORD prot,
+ PDWORD oldprot)
+{
+ if (addr >= (caddr_t)0x80000000 && addr <= (caddr_t)0xBFFFFFFF)
+ return TRUE; /* FAKEALARM! */
+ return VirtualProtectEx (parent, addr, len, prot, oldprot);
+}
+static BOOL
+VirtualProtExNT (HANDLE parent, PVOID addr, SIZE_T len, DWORD prot,
+ PDWORD oldprot)
+{
+ return VirtualProtectEx (parent, addr, len, prot, oldprot);
+}
+
+/* This allows to stay lazy about VirtualProtect usage in subsequent code. */
+#define VirtualProtect(a,l,p,o) (mmap_func->VirtualProt((a),(l),(p),(o)))
+#define VirtualProtectEx(h,a,l,p,o) (mmap_func->VirtualProtEx((h),(a),(l),(p),(o)))
+
+static HANDLE
+CreateMapping9x (HANDLE fhdl, size_t len, _off64_t off, DWORD openflags,
+ int prot, int flags, const char *name)
+{
+ HANDLE h;
+ DWORD high, low;
+
+ DWORD protect = gen_create_protect (openflags, flags);
+
+ /* copy-on-write doesn't work properly on 9x with real files. While the
+ changes are not propagated to the file, they are visible to other
+ processes sharing the same file mapping object. Workaround: Don't
+ use named file mapping. That should work since sharing file
+ mappings only works reliable using named file mapping on 9x.
+
+ On 9x/ME try first to open the mapping by name when opening a
+ shared file object. This is needed since 9x/ME only shares objects
+ between processes by name. What a mess... */
+
+ if (fhdl != INVALID_HANDLE_VALUE && !priv (flags))
+ {
+ /* Grrr, the whole stuff is just needed to try to get a reliable
+ mapping of the same file. Even that uprising isn't bullet
+ proof but it does it's best... */
+ char namebuf[CYG_MAX_PATH];
+ cygwin_conv_to_full_posix_path (name, namebuf);
+ for (int i = strlen (namebuf) - 1; i >= 0; --i)
+ namebuf[i] = cyg_tolower (namebuf [i]);
+
+ debug_printf ("named sharing");
+ DWORD access = gen_access (openflags, flags);
+ /* Different access modes result in incompatible mappings. So we
+ create different maps per access mode by using different names. */
+ switch (access)
+ {
+ case FILE_MAP_READ:
+ namebuf[0] = 'R';
+ break;
+ case FILE_MAP_WRITE:
+ namebuf[0] = 'W';
+ break;
+ case FILE_MAP_COPY:
+ namebuf[0] = 'C';
+ break;
+ }
+ if (!(h = OpenFileMapping (access, TRUE, namebuf)))
+ h = CreateFileMapping (fhdl, &sec_none, protect, 0, 0, namebuf);
+ }
+ else if (fhdl == INVALID_HANDLE_VALUE)
+ {
+ /* Standard anonymous mapping needs non-zero len. */
+ h = CreateFileMapping (fhdl, &sec_none, protect, 0, len, NULL);
+ }
+ else if (autogrow (flags))
+ {
+ high = (off + len) >> 32;
+ low = (off + len) & UINT32_MAX;
+ /* Auto-grow only works if the protection is PAGE_READWRITE. So,
+ first we call CreateFileMapping with PAGE_READWRITE, then, if the
+ requested protection is different, we close the mapping and
+ reopen it again with the correct protection, if auto-grow worked. */
+ h = CreateFileMapping (fhdl, &sec_none, PAGE_READWRITE,
+ high, low, NULL);
+ if (h && protect != PAGE_READWRITE)
+ {
+ CloseHandle (h);
+ h = CreateFileMapping (fhdl, &sec_none, protect,
+ high, low, NULL);
+ }
+ }
+ else
+ {
+ /* Zero len creates mapping for whole file. */
+ h = CreateFileMapping (fhdl, &sec_none, protect, 0, 0, NULL);
+ }
+ return h;
+}
+
+static HANDLE
+CreateMappingNT (HANDLE fhdl, size_t len, _off64_t off, DWORD openflags,
+ int prot, int flags, const char *)
+{
+ HANDLE h;
+ NTSTATUS ret;
+
+ LARGE_INTEGER sectionsize = { QuadPart: len };
+ ULONG protect = gen_create_protect (openflags, flags);
+ ULONG attributes = attached (prot) ? SEC_RESERVE : SEC_COMMIT;
+
+ OBJECT_ATTRIBUTES oa;
+ InitializeObjectAttributes (&oa, NULL, OBJ_INHERIT, NULL,
+ sec_none.lpSecurityDescriptor);
+
+ if (fhdl == INVALID_HANDLE_VALUE)
+ {
+ /* Standard anonymous mapping needs non-zero len. */
+ ret = NtCreateSection (&h, SECTION_ALL_ACCESS, &oa,
+ &sectionsize, protect, attributes, NULL);
+ }
+ else if (autogrow (flags))
+ {
+ /* Auto-grow only works if the protection is PAGE_READWRITE. So,
+ first we call NtCreateSection with PAGE_READWRITE, then, if the
+ requested protection is different, we close the mapping and
+ reopen it again with the correct protection, if auto-grow worked. */
+ sectionsize.QuadPart += off;
+ ret = NtCreateSection (&h, SECTION_ALL_ACCESS, &oa,
+ &sectionsize, PAGE_READWRITE, attributes, fhdl);
+ if (NT_SUCCESS (ret) && protect != PAGE_READWRITE)
+ {
+ CloseHandle (h);
+ ret = NtCreateSection (&h, SECTION_ALL_ACCESS, &oa,
+ &sectionsize, protect, attributes, fhdl);
+ }
+ }
+ else
+ {
+ /* Zero len creates mapping for whole file and allows
+ AT_EXTENDABLE_FILE mapping, if we ever use it... */
+ sectionsize.QuadPart = 0;
+ ret = NtCreateSection (&h, SECTION_ALL_ACCESS, &oa,
+ &sectionsize, protect, attributes, fhdl);
+ }
+ if (!NT_SUCCESS (ret))
+ {
+ h = NULL;
+ SetLastError (RtlNtStatusToDosError (ret));
+ }
+ return h;
+}
+
+void *
+MapView9x (HANDLE h, void *addr, size_t len, DWORD openflags,
+ int prot, int flags, _off64_t off)
+{
+ DWORD high = off >> 32;
+ DWORD low = off & UINT32_MAX;
+ DWORD access = gen_access (openflags, flags);
+ void *base;
+
+ /* Try mapping using the given address first, even if it's NULL.
+ If it failed, and addr was not NULL and flags is not MAP_FIXED,
+ try again with NULL address. */
+ if (!addr)
+ base = MapViewOfFile (h, access, high, low, len);
+ else
+ {
+ base = MapViewOfFileEx (h, access, high, low, len, addr);
+ if (!base && !fixed (flags))
+ base = MapViewOfFile (h, access, high, low, len);
+ }
+ debug_printf ("%x = MapViewOfFileEx (h:%x, access:%x, 0, off:%D, "
+ "len:%u, addr:%x)", base, h, access, off, len, addr);
+ return base;
+}
+
+void *
+MapViewNT (HANDLE h, void *addr, size_t len, DWORD openflags,
+ int prot, int flags, _off64_t off)
+{
+ NTSTATUS ret;
+ LARGE_INTEGER offset = { QuadPart:off };
+ DWORD protect = gen_create_protect (openflags, flags);
+ void *base = addr;
+ ULONG commitsize = attached (prot) ? 0 : len;
+ ULONG viewsize = len;
+ ULONG alloc_type = base && !wincap.is_wow64 () ? AT_ROUND_TO_PAGE : 0;
+
+ /* Try mapping using the given address first, even if it's NULL.
+ If it failed, and addr was not NULL and flags is not MAP_FIXED,
+ try again with NULL address. */
+ ret = NtMapViewOfSection (h, GetCurrentProcess (), &base, 0, commitsize,
+ &offset, &viewsize, ViewShare, alloc_type, protect);
+ if (!NT_SUCCESS (ret) && addr && !fixed (flags))
+ {
+ base = NULL;
+ ret = NtMapViewOfSection (h, GetCurrentProcess (), &base, 0, commitsize,
+ &offset, &viewsize, ViewShare, 0, protect);
+ }
+ if (!NT_SUCCESS (ret))
+ {
+ base = NULL;
+ SetLastError (RtlNtStatusToDosError (ret));
+ }
+ debug_printf ("%x = NtMapViewOfSection (h:%x, addr:%x, len:%u, off:%D, "
+ "protect:%x, type:%x)", base, h, addr, len, off, protect, 0);
+ return base;
+}
+
+struct mmap_func_t
+{
+ HANDLE (*CreateMapping)(HANDLE, size_t, _off64_t, DWORD, int, int,
+ const char *);
+ void * (*MapView)(HANDLE, void *, size_t, DWORD, int, int, _off64_t);
+ BOOL (*VirtualProt)(PVOID, SIZE_T, DWORD, PDWORD);
+ BOOL (*VirtualProtEx)(HANDLE, PVOID, SIZE_T, DWORD, PDWORD);
+};
+
+mmap_func_t mmap_funcs_9x =
+{
+ CreateMapping9x,
+ MapView9x,
+ VirtualProt9x,
+ VirtualProtEx9x
+};
+
+mmap_func_t mmap_funcs_nt =
+{
+ CreateMappingNT,
+ MapViewNT,
+ VirtualProtNT,
+ VirtualProtExNT
+};
+
+mmap_func_t *mmap_func;
+
+void
+mmap_init ()
+{
+ mmap_func = wincap.is_winnt () ? &mmap_funcs_nt : &mmap_funcs_9x;
+}
+
+/* Class structure used to keep a record of all current mmap areas
+ in a process. Needed for bookkeeping all mmaps in a process and
+ for duplicating all mmaps after fork() since mmaps are not propagated
+ to child processes by Windows. All information must be duplicated
+ by hand, see fixup_mmaps_after_fork().
+
+ The class structure:
+
+ One member of class map per process, global variable mmapped_areas.
+ Contains a dynamic class list array. Each list entry represents all
+ mapping to a file, keyed by file descriptor and file name hash.
+ Each list entry contains a dynamic class mmap_record array. Each
+ mmap_record represents exactly one mapping. For each mapping, there's
+ an additional so called `page_map'. It's an array of bits, one bit
+ per mapped memory page. The bit is set if the page is accessible,
+ unset otherwise. */
+
+class mmap_record
+{
+ private:
+ int fd;
+ HANDLE mapping_hdl;
+ DWORD openflags;
+ int prot;
+ int flags;
+ _off64_t offset;
+ DWORD len;
+ caddr_t base_address;
+ DWORD *page_map;
+ device dev;
+
+ public:
+ mmap_record (int nfd, HANDLE h, DWORD of, int p, int f, _off64_t o, DWORD l,
+ caddr_t b) :
+ fd (nfd),
+ mapping_hdl (h),
+ openflags (of),
+ prot (p),
+ flags (f),
+ offset (o),
+ len (l),
+ base_address (b),
+ page_map (NULL)
+ {
+ dev.devn = 0;
+ if (fd >= 0 && !cygheap->fdtab.not_open (fd))
+ dev = cygheap->fdtab[fd]->dev ();
+ else if (fd == -1)
+ dev.parse (FH_ZERO);
+ }
+
+ int get_fd () const { return fd; }
+ HANDLE get_handle () const { return mapping_hdl; }
+ device& get_device () { return dev; }
+ int get_prot () const { return prot; }
+ int get_openflags () const { return openflags; }
+ int get_flags () const { return flags; }
+ bool priv () const { return ::priv (flags); }
+ bool fixed () const { return ::fixed (flags); }
+ bool anonymous () const { return ::anonymous (flags); }
+ bool noreserve () const { return ::noreserve (flags); }
+ bool autogrow () const { return ::autogrow (flags); }
+ bool attached () const { return ::attached (prot); }
+ bool filler () const { return ::filler (prot); }
+ _off64_t get_offset () const { return offset; }
+ DWORD get_len () const { return len; }
+ caddr_t get_address () const { return base_address; }
+
+ bool alloc_page_map ();
+ void free_page_map () { if (page_map) cfree (page_map); }
+
+ DWORD find_unused_pages (DWORD pages) const;
+ _off64_t map_pages (_off64_t off, DWORD len);
+ bool map_pages (caddr_t addr, DWORD len);
+ bool unmap_pages (caddr_t addr, DWORD len);
+ int access (caddr_t address);
+
+ fhandler_base *alloc_fh ();
+ void free_fh (fhandler_base *fh);
+
+ DWORD gen_create_protect () const
+ { return ::gen_create_protect (get_openflags (), get_flags ()); }
+ DWORD gen_protect () const
+ { return ::gen_protect (get_prot (), get_flags ()); }
+ DWORD gen_access () const
+ { return ::gen_access (get_openflags (), get_flags ()); }
+ bool compatible_flags (int fl) const;
+};
+
+class list
+{
+ private:
+ mmap_record *recs;
+ int nrecs, maxrecs;
+ int fd;
+ DWORD hash;
+
+ public:
+ int get_fd () const { return fd; }
+ DWORD get_hash () const { return hash; }
+ mmap_record *get_record (int i) { return i >= nrecs ? NULL : recs + i; }
+
+ bool anonymous () const { return fd == -1; }
+ void set (int nfd);
+ mmap_record *add_record (mmap_record r);
+ bool del_record (int i);
+ void free_recs () { if (recs) cfree (recs); }
+ mmap_record *search_record (_off64_t off, DWORD len);
+ long search_record (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len,
+ long start);
+ caddr_t try_map (void *addr, size_t len, int flags, _off64_t off);
+};
+
+class map
+{
+ private:
+ list *lists;
+ unsigned nlists, maxlists;
+
+ public:
+ list *get_list (unsigned i) { return i >= nlists ? NULL : lists + i; }
+ list *get_list_by_fd (int fd);
+ list *add_list (int fd);
+ void del_list (unsigned i);
+};
+
+/* This is the global map structure pointer. */
+static map mmapped_areas;
+
+bool
+mmap_record::compatible_flags (int fl) const
+{
+#define MAP_COMPATMASK (MAP_TYPE | MAP_NORESERVE)
+ return (get_flags () & MAP_COMPATMASK) == (fl & MAP_COMPATMASK);
+}
+
+DWORD
+mmap_record::find_unused_pages (DWORD pages) const
+{
+ DWORD mapped_pages = PAGE_CNT (get_len ());
+ DWORD start;
+
+ if (pages > mapped_pages)
+ return (DWORD)-1;
+ for (start = 0; start <= mapped_pages - pages; ++start)
+ if (!MAP_ISSET (start))
+ {
+ DWORD cnt;
+ for (cnt = 0; cnt < pages; ++cnt)
+ if (MAP_ISSET (start + cnt))
+ break;
+ if (cnt >= pages)
+ return start;
+ }
+ return (DWORD)-1;
+}
+
+bool
+mmap_record::alloc_page_map ()
+{
+ /* Allocate one bit per page */
+ if (!(page_map = (DWORD *) ccalloc (HEAP_MMAP,
+ MAPSIZE (PAGE_CNT (get_len ())),
+ sizeof (DWORD))))
+ return false;
+
+ DWORD start_protect = gen_create_protect ();
+ DWORD real_protect = gen_protect ();
+ if (real_protect != start_protect && !noreserve ()
+ && !VirtualProtect (get_address (), get_len (),
+ real_protect, &start_protect))
+ system_printf ("Warning: VirtualProtect (addr: %p, len: 0x%x, "
+ "new_prot: 0x%x, old_prot: 0x%x), %E",
+ get_address (), get_len (),
+ real_protect, start_protect);
+ DWORD len = PAGE_CNT (get_len ());
+ while (len-- > 0)
+ MAP_SET (len);
+ return true;
+}
+
+_off64_t
+mmap_record::map_pages (_off64_t off, DWORD len)
+{
+ /* Used ONLY if this mapping matches into the chunk of another already
+ performed mapping in a special case of MAP_ANON|MAP_PRIVATE.
+
+ Otherwise it's job is now done by alloc_page_map(). */
+ DWORD old_prot;
+ debug_printf ("map_pages (fd=%d, off=%D, len=%u)", get_fd (), off, len);
+ len = PAGE_CNT (len);
+
+ if ((off = find_unused_pages (len)) == (DWORD)-1)
+ return 0L;
+ if (!noreserve ()
+ && !VirtualProtect (get_address () + off * getpagesize (),
+ len * getpagesize (), gen_protect (), &old_prot))
+ {
+ __seterrno ();
+ return (_off64_t)-1;
+ }
+
+ while (len-- > 0)
+ MAP_SET (off + len);
+ return off * getpagesize ();
+}
+
+bool
+mmap_record::map_pages (caddr_t addr, DWORD len)
+{
+ debug_printf ("map_pages (addr=%x, len=%u)", addr, len);
+ DWORD old_prot;
+ DWORD off = addr - get_address ();
+ off /= getpagesize ();
+ len = PAGE_CNT (len);
+ /* First check if the area is unused right now. */
+ for (DWORD l = 0; l < len; ++l)
+ if (MAP_ISSET (off + l))
+ {
+ set_errno (EINVAL);
+ return false;
+ }
+ if (!noreserve ()
+ && !VirtualProtect (get_address () + off * getpagesize (),
+ len * getpagesize (), gen_protect (), &old_prot))
+ {
+ __seterrno ();
+ return false;
+ }
+ for (; len-- > 0; ++off)
+ MAP_SET (off);
+ return true;
+}
+
+bool
+mmap_record::unmap_pages (caddr_t addr, DWORD len)
+{
+ DWORD old_prot;
+ DWORD off = addr - get_address ();
+ off /= getpagesize ();
+ len = PAGE_CNT (len);
+ if (anonymous () && priv () && noreserve ()
+ && !VirtualFree (get_address () + off * getpagesize (),
+ len * getpagesize (), MEM_DECOMMIT))
+ debug_printf ("VirtualFree in unmap_pages () failed, %E");
+ else if (!VirtualProtect (get_address () + off * getpagesize (),
+ len * getpagesize (), PAGE_NOACCESS, &old_prot))
+ debug_printf ("VirtualProtect in unmap_pages () failed, %E");
+
+ for (; len-- > 0; ++off)
+ MAP_CLR (off);
+ /* Return TRUE if all pages are free'd which may result in unmapping
+ the whole chunk. */
+ for (len = MAPSIZE (PAGE_CNT (get_len ())); len > 0; )
+ if (page_map[--len])
+ return false;
+ return true;
+}
+
+int
+mmap_record::access (caddr_t address)
+{
+ if (address < get_address () || address >= get_address () + get_len ())
+ return 0;
+ DWORD off = (address - get_address ()) / getpagesize ();
+ return MAP_ISSET (off);
+}
+
+fhandler_base *
+mmap_record::alloc_fh ()
+{
+ if (anonymous ())
+ {
+ fh_anonymous.set_io_handle (INVALID_HANDLE_VALUE);
+ fh_anonymous.set_access (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
+ return &fh_anonymous;
+ }
+
+ /* The file descriptor could have been closed or, even
+ worse, could have been reused for another file before
+ the call to fork(). This requires creating a fhandler
+ of the correct type to be sure to call the method of the
+ correct class. */
+ fhandler_base *fh = build_fh_dev (get_device ());
+ fh->set_access (get_openflags ());
+ return fh;
+}
+
+void
+mmap_record::free_fh (fhandler_base *fh)
+{
+ if (!anonymous ())
+ cfree (fh);
+}
+
+mmap_record *
+list::add_record (mmap_record r)
+{
+ if (nrecs == maxrecs)
+ {
+ mmap_record *new_recs;
+ if (maxrecs == 0)
+ new_recs = (mmap_record *)
+ cmalloc (HEAP_MMAP, 5 * sizeof (mmap_record));
+ else
+ new_recs = (mmap_record *)
+ crealloc (recs, (maxrecs + 5) * sizeof (mmap_record));
+ if (!new_recs)
+ return NULL;
+ maxrecs += 5;
+ recs = new_recs;
+ }
+ recs[nrecs] = r;
+ if (!recs[nrecs].alloc_page_map ())
+ return NULL;
+ return recs + nrecs++;
+}
+
+/* Used in mmap() */
+mmap_record *
+list::search_record (_off64_t off, DWORD len)
+{
+ if (anonymous () && !off)
+ {
+ len = PAGE_CNT (len);
+ for (int i = 0; i < nrecs; ++i)
+ if (recs[i].find_unused_pages (len) != (DWORD)-1)
+ return recs + i;
+ }
+ else
+ {
+ for (int i = 0; i < nrecs; ++i)
+ if (off >= recs[i].get_offset ()
+ && off + len <= recs[i].get_offset ()
+ + (PAGE_CNT (recs[i].get_len ()) * getpagesize ()))
+ return recs + i;
+ }
+ return NULL;
+}
+
+/* Used in munmap() */
+long
+list::search_record (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len,
+ long start)
+{
+ caddr_t low, high;
+
+ for (long i = start + 1; i < nrecs; ++i)
+ {
+ low = (addr >= recs[i].get_address ()) ? addr : recs[i].get_address ();
+ high = recs[i].get_address ();
+ if (recs[i].filler ())
+ high += recs[i].get_len ();
+ else
+ high += (PAGE_CNT (recs[i].get_len ()) * getpagesize ());
+ high = (addr + len < high) ? addr + len : high;
+ if (low < high)
+ {
+ m_addr = low;
+ m_len = high - low;
+ return i;
+ }
+ }
+ return -1;
+}
+
+void
+list::set (int nfd)
+{
+ fd = nfd;
+ if (!anonymous ())
+ {
+ /* The fd isn't sufficient since it could already be the fd of another
+ file. So we use the inode number as evaluated by fstat to identify
+ the file. */
+ struct stat st;
+ fstat (nfd, &st);
+ hash = st.st_ino;
+ }
+ nrecs = maxrecs = 0;
+ recs = NULL;
+}
+
+bool
+list::del_record (int i)
+{
+ if (i < nrecs)
+ {
+ recs[i].free_page_map ();
+ for (; i < nrecs - 1; i++)
+ recs[i] = recs[i + 1];
+ nrecs--;
+ }
+ /* Return true if the list is empty which allows the caller to remove
+ this list from the list array. */
+ return !nrecs;
+}
+
+caddr_t
+list::try_map (void *addr, size_t len, int flags, _off64_t off)
+{
+ mmap_record *rec;
+
+ if (off == 0 && !fixed (flags))
+ {
+ /* If MAP_FIXED isn't given, check if this mapping matches into the
+ chunk of another already performed mapping. */
+ if ((rec = search_record (off, len)) != NULL
+ && rec->compatible_flags (flags))
+ {
+ if ((off = rec->map_pages (off, len)) == (_off64_t)-1)
+ return (caddr_t) MAP_FAILED;
+ return (caddr_t) rec->get_address () + off;
+ }
+ }
+ else if (fixed (flags))
+ {
+ /* If MAP_FIXED is given, test if the requested area is in an
+ unmapped part of an still active mapping. This can happen
+ if a memory region is unmapped and remapped with MAP_FIXED. */
+ caddr_t u_addr;
+ DWORD u_len;
+ long record_idx = -1;
+ if ((record_idx = search_record ((caddr_t) addr, len, u_addr, u_len,
+ record_idx)) >= 0)
+ {
+ rec = get_record (record_idx);
+ if (u_addr > (caddr_t) addr || u_addr + len < (caddr_t) addr + len
+ || !rec->compatible_flags (flags))
+ {
+ /* Partial match only, or access mode doesn't match. */
+ /* FIXME: Handle partial mappings gracefully if adjacent
+ memory is available. */
+ set_errno (EINVAL);
+ return (caddr_t) MAP_FAILED;
+ }
+ if (!rec->map_pages ((caddr_t) addr, len))
+ return (caddr_t) MAP_FAILED;
+ return (caddr_t) addr;
+ }
+ }
+ return NULL;
+}
+
+list *
+map::get_list_by_fd (int fd)
+{
+ unsigned i;
+ for (i = 0; i < nlists; i++)
+ {
+ if (fd == -1 && lists[i].anonymous ())
+ return lists + i;
+ /* The fd isn't sufficient since it could already be the fd of another
+ file. So we use the inode number as evaluated by fstat to identify
+ the file. */
+ struct stat st;
+ if (fd != -1 && !fstat (fd, &st) && lists[i].get_hash () == st.st_ino)
+ return lists + i;
+ }
+ return 0;
+}
+
+list *
+map::add_list (int fd)
+{
+ if (nlists == maxlists)
+ {
+ list *new_lists;
+ if (maxlists == 0)
+ new_lists = (list *) cmalloc (HEAP_MMAP, 5 * sizeof (list));
+ else
+ new_lists = (list *) crealloc (lists, (maxlists + 5) * sizeof (list));
+ if (!new_lists)
+ return NULL;
+ maxlists += 5;
+ lists = new_lists;
+ }
+ lists[nlists].set (fd);
+ return lists + nlists++;
+}
+
+void
+map::del_list (unsigned i)
+{
+ if (i < nlists)
+ {
+ lists[i].free_recs ();
+ for (; i < nlists - 1; i++)
+ lists[i] = lists[i + 1];
+ nlists--;
+ }
+}
+
+/* This function is called from exception_handler when a segmentation
+ violation has happened. We have two cases to check here.
+
+ First, is it an address within "attached" mmap pages (indicated by
+ the __PROT_ATTACH protection, see there)? In this case the function
+ returns 1 and the exception_handler raises SIGBUS, as demanded by the
+ memory protection extension described in SUSv3 (see the mmap man
+ page).
+
+ Second, check if the address is within "noreserve" mmap pages
+ (indicated by MAP_NORESERVE flag). If so, the function calls
+ VirtualAlloc to commit the page and returns 2. The exception handler
+ then just returns with 0 and the affected application retries the
+ failing memory access. If VirtualAlloc fails, the function returns
+ 1, so that the exception handler raises a SIGBUS, as described in the
+ MAP_NORESERVE man pages for Linux and Solaris.
+
+ In any other case 0 is returned and a normal SIGSEGV is raised. */
+int
+mmap_is_attached_or_noreserve_page (ULONG_PTR addr)
+{
+ list *map_list;
+ long record_idx;
+ caddr_t u_addr;
+ DWORD u_len;
+ DWORD pagesize = getsystempagesize ();
+
+ addr = rounddown (addr, pagesize);
+ if (!(map_list = mmapped_areas.get_list_by_fd (-1)))
+ return 0;
+ if ((record_idx = map_list->search_record ((caddr_t)addr, pagesize,
+ u_addr, u_len, -1)) < 0)
+ return 0;
+ if (map_list->get_record (record_idx)->attached ())
+ return 1;
+ if (!map_list->get_record (record_idx)->noreserve ())
+ return 0;
+ DWORD new_prot = map_list->get_record (record_idx)->gen_protect ();
+ return VirtualAlloc ((void *)addr, pagesize, MEM_COMMIT, new_prot) ? 2 : 1;
+}
+
+static caddr_t
+mmap_worker (fhandler_base *fh, caddr_t base, size_t len, int prot, int flags,
+ int fd, _off64_t off)
+{
+ list *map_list;
+ HANDLE h = fh->mmap (&base, len, prot, flags, off);
+ if (h == INVALID_HANDLE_VALUE)
+ return NULL;
+ if (!(map_list = mmapped_areas.get_list_by_fd (fd))
+ && !(map_list = mmapped_areas.add_list (fd)))
+ {
+ fh->munmap (h, base, len);
+ return NULL;
+ }
+ mmap_record mmap_rec (fd, h, fh->get_access (), prot, flags, off, len, base);
+ mmap_record *rec = map_list->add_record (mmap_rec);
+ if (!rec)
+ {
+ fh->munmap (h, base, len);
+ return NULL;
+ }
+ return base;
+}
+
+extern "C" void *
+mmap64 (void *addr, size_t len, int prot, int flags, int fd, _off64_t off)
+{
+ syscall_printf ("addr %x, len %u, prot %x, flags %x, fd %d, off %D",
+ addr, len, prot, flags, fd, off);
+
+ caddr_t ret = (caddr_t) MAP_FAILED;
+ fhandler_base *fh = NULL;
+ list *map_list = NULL;
+ size_t orig_len = 0;
+ caddr_t base = NULL;
+
+ DWORD pagesize = getpagesize ();
+
+ fh_anonymous.set_io_handle (INVALID_HANDLE_VALUE);
+ fh_anonymous.set_access (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
+ fh_disk_file.set_io_handle (NULL);
+
+ SetResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
+
+ /* EINVAL error conditions. Note that the addr%pagesize test is deferred
+ to workaround a serious alignment problem in Windows 98. */
+ if (off % pagesize
+ || ((prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)))
+ || ((flags & MAP_TYPE) != MAP_SHARED
+ && (flags & MAP_TYPE) != MAP_PRIVATE)
+#if 0
+ || (fixed (flags) && ((uintptr_t) addr % pagesize))
+#endif
+ || !len)
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+
+ /* There's a serious alignment problem in Windows 98. MapViewOfFile
+ sometimes returns addresses which are page aligned instead of
+ granularity aligned. OTOH, it's not possible to force such an
+ address using MapViewOfFileEx. So what we do here to let it work
+ at least most of the time is, allow 4K aligned addresses in 98,
+ to enable remapping of formerly mapped pages. If no matching
+ free pages exist, check addr again, this time for the real alignment. */
+ DWORD checkpagesize = wincap.has_mmap_alignment_bug () ?
+ getsystempagesize () : pagesize;
+ if (fixed (flags) && ((uintptr_t) addr % checkpagesize))
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+
+ if (!anonymous (flags) && fd != -1)
+ {
+ /* Ensure that fd is open */
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto out;
+
+ fh = cfd;
+
+ /* mmap /dev/zero is like MAP_ANONYMOUS. */
+ if (fh->get_device () == FH_ZERO)
+ flags |= MAP_ANONYMOUS;
+ }
+ if (anonymous (flags) || fd == -1)
+ {
+ fh = &fh_anonymous;
+ fd = -1;
+ flags |= MAP_ANONYMOUS;
+ /* Anonymous mappings are always forced to pagesize length with
+ no offset. */
+ len = roundup2 (len, pagesize);
+ off = 0;
+ }
+ else if (fh->get_device () == FH_FS)
+ {
+ /* EACCES error conditions according to SUSv3. File must be opened
+ for reading, regardless of the requested protection, and file must
+ be opened for writing when PROT_WRITE together with MAP_SHARED
+ is requested. */
+ if (!(fh->get_access () & GENERIC_READ)
+ || (!(fh->get_access () & GENERIC_WRITE)
+ && (prot & PROT_WRITE) && !priv (flags)))
+ {
+ set_errno (EACCES);
+ goto out;
+ }
+
+ /* On 9x you can't create mappings with PAGE_WRITECOPY protection if
+ the file isn't explicitely opened with WRITE access. */
+ if (!wincap.is_winnt () && priv (flags)
+ && !(fh->get_access () & GENERIC_WRITE))
+ {
+ HANDLE h = CreateFile (fh->get_win32_name (),
+ fh->get_access () | GENERIC_WRITE,
+ wincap.shared (), &sec_none_nih,
+ OPEN_EXISTING, 0, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ set_errno (EACCES);
+ goto out;
+ }
+ fh_disk_file.set_io_handle (h);
+ fh_disk_file.set_access (fh->get_access () | GENERIC_WRITE);
+ path_conv pc;
+ pc.set_name (fh->get_win32_name (), "");
+ fh_disk_file.set_name (pc);
+ fh = &fh_disk_file;
+ }
+
+ /* On NT you can't create mappings with PAGE_EXECUTE protection if
+ the file isn't explicitely opened with EXECUTE access. */
+ if (wincap.is_winnt ())
+ {
+ HANDLE h = CreateFile (fh->get_win32_name (),
+ fh->get_access () | GENERIC_EXECUTE,
+ wincap.shared (), &sec_none_nih,
+ OPEN_EXISTING, 0, NULL);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ fh_disk_file.set_io_handle (h);
+ fh_disk_file.set_access (fh->get_access () | GENERIC_EXECUTE);
+ fh = &fh_disk_file;
+ }
+ else if (prot & PROT_EXEC)
+ {
+ /* TODO: To be or not to be... I'm opting for refusing this
+ mmap request rather than faking it, but that might break
+ some non-portable code. */
+ set_errno (EACCES);
+ goto out;
+ }
+ }
+
+ DWORD high;
+ DWORD low = GetFileSize (fh->get_handle (), &high);
+ _off64_t fsiz = ((_off64_t)high << 32) + low;
+
+ /* Don't allow file mappings beginning beyond EOF since Windows can't
+ handle that POSIX like, unless MAP_AUTOGROW flag is set, which
+ mimics Windows behaviour. */
+ if (off >= fsiz && !autogrow (flags))
+ {
+ /* Instead, it seems suitable to return an anonymous mapping of
+ the given size instead. Mapped addresses beyond EOF aren't
+ written back to the file anyway, so the handling is identical
+ to other pages beyond EOF. */
+ fh = &fh_anonymous;
+ len = roundup2 (len, pagesize);
+ prot = PROT_READ | PROT_WRITE | __PROT_ATTACH;
+ flags &= MAP_FIXED;
+ flags |= MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
+ fd = -1;
+ off = 0;
+ goto go_ahead;
+ }
+ fsiz -= off;
+ /* On NT systems we're creating the pages beyond EOF as reserved,
+ anonymous pages. That's not possible on 9x for two reasons.
+ It neither allows to create reserved pages in the shared memory
+ area, nor does it allow to create page aligend mappings (in
+ contrast to granularity aligned mappings).
+
+ Note that this isn't done in WOW64 environments since apparently
+ WOW64 does not support the AT_ROUND_TO_PAGE flag which is required
+ to get this right. Too bad. */
+ if (wincap.virtual_protect_works_on_shared_pages ()
+ && !wincap.is_wow64 ()
+ && ((len > fsiz && !autogrow (flags))
+ || len < pagesize))
+ orig_len = len;
+ if (len > fsiz)
+ {
+ if (autogrow (flags))
+ {
+ /* Allow mapping beyond EOF if MAP_AUTOGROW flag is set.
+ Check if file has been opened for writing, otherwise
+ MAP_AUTOGROW is invalid. */
+ if (!(fh->get_access () & GENERIC_WRITE))
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+ }
+ else
+ /* Otherwise, don't map beyond EOF, since Windows would change
+ the file to the new length, in contrast to POSIX. */
+ len = fsiz;
+ }
+
+ /* If the requested offset + len is <= file size, drop MAP_AUTOGROW.
+ This simplifes fhandler::mmap's job. */
+ if (autogrow (flags) && (off + len) <= fsiz)
+ flags &= ~MAP_AUTOGROW;
+ }
+
+go_ahead:
+
+ map_list = mmapped_areas.get_list_by_fd (fd);
+
+ /* Test if an existing anonymous mapping can be recycled. */
+ if (map_list && anonymous (flags))
+ {
+ caddr_t tried = map_list->try_map (addr, len, flags, off);
+ /* try_map returns NULL if no map matched, otherwise it returns
+ a valid address, of MAP_FAILED in case of a fatal error. */
+ if (tried)
+ {
+ ret = tried;
+ goto out;
+ }
+ }
+
+ /* Deferred alignment test, see above. */
+ if (wincap.has_mmap_alignment_bug ()
+ && fixed (flags) && ((uintptr_t) addr % pagesize))
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+
+ base = mmap_worker (fh, (caddr_t) addr, len, prot, flags, fd, off);
+ if (!base)
+ goto out;
+
+ if (orig_len)
+ {
+ /* If the requested length is bigger than the file size, the
+ remainder is created as anonymous mapping. Actually two
+ mappings are created, first the reminder from the file end to
+ the next 64K boundary as accessible pages with the same
+ protection as the file's pages, then as much pages as necessary
+ to accomodate the requested length, but as reserved pages which
+ raise a SIGBUS when trying to access them. AT_ROUND_TO_PAGE
+ and page protection on shared pages is only supported by 32 bit NT,
+ so don't even try on 9x and in WOW64. This is accomplished by not
+ setting orig_len on 9x and in WOW64 above. */
+ orig_len = roundup2 (orig_len, pagesize);
+ len = roundup2 (len, getsystempagesize ());
+ if (orig_len - len)
+ {
+ orig_len -= len;
+ size_t valid_page_len = orig_len % pagesize;
+ size_t sigbus_page_len = orig_len - valid_page_len;
+
+ caddr_t at_base = base + len;
+ if (valid_page_len)
+ {
+ prot |= __PROT_FILLER;
+ flags &= MAP_SHARED | MAP_PRIVATE;
+ flags |= MAP_ANONYMOUS | MAP_FIXED;
+ at_base = mmap_worker (&fh_anonymous, at_base, valid_page_len,
+ prot, flags, -1, 0);
+ if (!at_base)
+ {
+ fh->munmap (fh->get_handle (), base, len);
+ set_errno (ENOMEM);
+ goto out;
+ }
+ at_base += valid_page_len;
+ }
+ if (sigbus_page_len)
+ {
+ prot = PROT_READ | PROT_WRITE | __PROT_ATTACH;
+ flags = MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED;
+ at_base = mmap_worker (&fh_anonymous, at_base, sigbus_page_len,
+ prot, flags, -1, 0);
+ if (!at_base)
+ debug_printf ("Warning: Mapping beyond EOF failed, %E");
+ }
+ }
+ }
+
+ ret = base;
+
+out:
+
+ ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
+
+ if (fh_disk_file.get_handle ())
+ CloseHandle (fh_disk_file.get_handle ());
+
+ syscall_printf ("%p = mmap() ", ret);
+ return ret;
+}
+
+extern "C" void *
+mmap (void *addr, size_t len, int prot, int flags, int fd, _off_t off)
+{
+ return mmap64 (addr, len, prot, flags, fd, (_off64_t)off);
+}
+
+/* munmap () removes all mmapped pages between addr and addr+len. */
+
+extern "C" int
+munmap (void *addr, size_t len)
+{
+ syscall_printf ("munmap (addr %x, len %u)", addr, len);
+
+ /* Error conditions according to SUSv3 */
+ if (!addr || !len || check_invalid_virtual_addr (addr, len))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ /* See comment in mmap64 for a description. */
+ size_t pagesize = wincap.has_mmap_alignment_bug () ?
+ getsystempagesize () : getpagesize ();
+ if (((uintptr_t) addr % pagesize) || !len)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ len = roundup2 (len, pagesize);
+
+ SetResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap");
+
+ /* Iterate through the map, unmap pages between addr and addr+len
+ in all maps. */
+ list *map_list;
+ for (unsigned list_idx = 0;
+ (map_list = mmapped_areas.get_list (list_idx));
+ ++list_idx)
+ {
+ long record_idx = -1;
+ caddr_t u_addr;
+ DWORD u_len;
+
+ while ((record_idx = map_list->search_record((caddr_t)addr, len, u_addr,
+ u_len, record_idx)) >= 0)
+ {
+ mmap_record *rec = map_list->get_record (record_idx);
+ if (rec->unmap_pages (u_addr, u_len))
+ {
+ /* The whole record has been unmapped, so we now actually
+ unmap it from the system in full length... */
+ fhandler_base *fh = rec->alloc_fh ();
+ fh->munmap (rec->get_handle (),
+ rec->get_address (),
+ rec->get_len ());
+ rec->free_fh (fh);
+
+ /* ...and delete the record. */
+ if (map_list->del_record (record_idx--))
+ {
+ /* Yay, the last record has been removed from the list,
+ we can remove the list now, too. */
+ mmapped_areas.del_list (list_idx--);
+ break;
+ }
+ }
+ }
+ }
+
+ ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap");
+ syscall_printf ("0 = munmap(): %x", addr);
+ return 0;
+}
+
+/* Sync file with memory. Ignore flags for now. */
+
+extern "C" int
+msync (void *addr, size_t len, int flags)
+{
+ int ret = -1;
+ list *map_list;
+
+ syscall_printf ("msync (addr: %p, len %u, flags %x)", addr, len, flags);
+
+ SetResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync");
+
+ /* See comment in mmap64 for a description. */
+ size_t pagesize = wincap.has_mmap_alignment_bug () ?
+ getsystempagesize () : getpagesize ();
+ if (((uintptr_t) addr % pagesize)
+ || (flags & ~(MS_ASYNC | MS_SYNC | MS_INVALIDATE))
+ || (flags & (MS_ASYNC | MS_SYNC) == (MS_ASYNC | MS_SYNC)))
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+ len = roundup2 (len, pagesize);
+
+ /* Iterate through the map, looking for the mmapped area.
+ Error if not found. */
+ for (unsigned list_idx = 0;
+ (map_list = mmapped_areas.get_list (list_idx));
+ ++list_idx)
+ {
+ mmap_record *rec;
+ for (int record_idx = 0;
+ (rec = map_list->get_record (record_idx));
+ ++record_idx)
+ {
+ if (rec->access ((caddr_t)addr))
+ {
+ /* Check whole area given by len. */
+ for (DWORD i = getpagesize (); i < len; ++i)
+ if (!rec->access ((caddr_t)addr + i))
+ {
+ set_errno (ENOMEM);
+ goto out;
+ }
+ fhandler_base *fh = rec->alloc_fh ();
+ ret = fh->msync (rec->get_handle (), (caddr_t)addr, len, flags);
+ rec->free_fh (fh);
+ goto out;
+ }
+ }
+ }
+
+ /* No matching mapping exists. */
+ set_errno (ENOMEM);
+
+out:
+ syscall_printf ("%d = msync()", ret);
+ ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync");
+ return ret;
+}
+
+/* Set memory protection */
+
+extern "C" int
+mprotect (void *addr, size_t len, int prot)
+{
+ bool in_mapped = false;
+ bool ret = false;
+ DWORD old_prot;
+ DWORD new_prot = 0;
+
+ syscall_printf ("mprotect (addr: %p, len %u, prot %x)", addr, len, prot);
+
+ /* See comment in mmap64 for a description. */
+ size_t pagesize = wincap.has_mmap_alignment_bug () ?
+ getsystempagesize () : getpagesize ();
+ if ((uintptr_t) addr % pagesize)
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+ len = roundup2 (len, pagesize);
+
+ SetResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "mprotect");
+
+ /* Iterate through the map, protect pages between addr and addr+len
+ in all maps. */
+ list *map_list;
+ for (unsigned list_idx = 0;
+ (map_list = mmapped_areas.get_list (list_idx));
+ ++list_idx)
+ {
+ long record_idx = -1;
+ caddr_t u_addr;
+ DWORD u_len;
+
+ while ((record_idx = map_list->search_record((caddr_t)addr, len,
+ u_addr, u_len,
+ record_idx)) >= 0)
+ {
+ mmap_record *rec = map_list->get_record (record_idx);
+ in_mapped = true;
+ if (rec->attached ())
+ continue;
+ new_prot = gen_protect (prot, rec->get_flags ());
+ if (rec->anonymous () && rec->priv () && rec->noreserve ())
+ {
+ if (new_prot == PAGE_NOACCESS)
+ ret = VirtualFree (u_addr, u_len, MEM_DECOMMIT);
+ else
+ ret = !!VirtualAlloc (u_addr, u_len, MEM_COMMIT, new_prot);
+ }
+ else
+ ret = VirtualProtect (u_addr, u_len, new_prot, &old_prot);
+ if (!ret)
+ {
+ __seterrno ();
+ break;
+ }
+ }
+ }
+
+ ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "mprotect");
+
+ if (!in_mapped)
+ {
+ int flags = 0;
+ MEMORY_BASIC_INFORMATION mbi;
+
+ ret = VirtualQuery (addr, &mbi, sizeof mbi);
+ if (ret)
+ {
+ /* If write protection is requested, check if the page was
+ originally protected writecopy. In this case call VirtualProtect
+ requesting PAGE_WRITECOPY, otherwise the VirtualProtect will fail
+ on NT version >= 5.0 */
+ if (prot & PROT_WRITE)
+ {
+ if (mbi.AllocationProtect == PAGE_WRITECOPY
+ || mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY)
+ flags = MAP_PRIVATE;
+ }
+ new_prot = gen_protect (prot, flags);
+ if (new_prot != PAGE_NOACCESS && mbi.State == MEM_RESERVE)
+ ret = VirtualAlloc (addr, len, MEM_COMMIT, new_prot);
+ else
+ ret = VirtualProtect (addr, len, new_prot, &old_prot);
+ }
+ if (!ret)
+ __seterrno ();
+ }
+
+out:
+
+ syscall_printf ("%d = mprotect ()", ret ? 0 : -1);
+ return ret ? 0 : -1;
+}
+
+extern "C" int
+mlock (const void *addr, size_t len)
+{
+ if (!wincap.has_working_virtual_lock ())
+ return 0;
+
+ int ret = -1;
+
+ /* Instead of using VirtualLock, which does not guarantee that the pages
+ aren't swapped out when the process is inactive, we're using
+ ZwLockVirtualMemory with the LOCK_VM_IN_RAM flag to do what mlock on
+ POSIX systems does. On NT, this requires SeLockMemoryPrivilege,
+ which is given only to SYSTEM by default. */
+
+ push_thread_privilege (SE_LOCK_MEMORY_PRIV, true);
+
+ /* Align address and length values to page size. */
+ size_t pagesize = getpagesize ();
+ PVOID base = (PVOID) rounddown((uintptr_t) addr, pagesize);
+ ULONG size = roundup2 (((uintptr_t) addr - (uintptr_t) base) + len, pagesize);
+ NTSTATUS status = 0;
+ do
+ {
+ status = NtLockVirtualMemory (hMainProc, &base, &size, LOCK_VM_IN_RAM);
+ if (status == STATUS_WORKING_SET_QUOTA)
+ {
+ /* The working set is too small, try to increase it so that the
+ requested locking region fits in. Unfortunately I don't know
+ any function which would return the currently locked pages of
+ a process (no go with NtQueryVirtualMemory).
+
+ So, except for the border cases, what we do here is something
+ really embarrassing. We raise the working set by 64K at a time
+ and retry, until either we fail to raise the working set size
+ further, or until NtLockVirtualMemory returns successfully (or
+ with another error). */
+ ULONG min, max;
+ if (!GetProcessWorkingSetSize (hMainProc, &min, &max))
+ {
+ set_errno (ENOMEM);
+ break;
+ }
+ if (min < size)
+ min = size + pagesize;
+ else if (size < pagesize)
+ min += size;
+ else
+ min += pagesize;
+ if (max < min)
+ max = min;
+ if (!SetProcessWorkingSetSize (hMainProc, min, max))
+ {
+ set_errno (ENOMEM);
+ break;
+ }
+ }
+ else if (!NT_SUCCESS (status))
+ __seterrno_from_nt_status (status);
+ else
+ ret = 0;
+ }
+ while (status == STATUS_WORKING_SET_QUOTA);
+
+ pop_thread_privilege ();
+
+ return ret;
+}
+
+extern "C" int
+munlock (const void *addr, size_t len)
+{
+ if (!wincap.has_working_virtual_lock ())
+ return 0;
+
+ int ret = -1;
+
+ push_thread_privilege (SE_LOCK_MEMORY_PRIV, true);
+
+ /* Align address and length values to page size. */
+ size_t pagesize = getpagesize ();
+ PVOID base = (PVOID) rounddown((uintptr_t) addr, pagesize);
+ ULONG size = roundup2 (((uintptr_t) addr - (uintptr_t) base) + len, pagesize);
+ NTSTATUS status = NtUnlockVirtualMemory (hMainProc, &base, &size,
+ LOCK_VM_IN_RAM);
+ if (!NT_SUCCESS (status))
+ __seterrno_from_nt_status (status);
+ else
+ ret = 0;
+
+ pop_thread_privilege ();
+
+ return ret;
+}
+
+/*
+ * Base implementation:
+ *
+ * `mmap' returns ENODEV as documented in SUSv2.
+ * In contrast to the global function implementation, the member function
+ * `mmap' has to return the mapped base address in `addr' and the handle to
+ * the mapping object as return value. In case of failure, the fhandler
+ * mmap has to close that handle by itself and return INVALID_HANDLE_VALUE.
+ *
+ * `munmap' and `msync' get the handle to the mapping object as first parameter
+ * additionally.
+*/
+HANDLE
+fhandler_base::mmap (caddr_t *addr, size_t len, int prot,
+ int flags, _off64_t off)
+{
+ set_errno (ENODEV);
+ return INVALID_HANDLE_VALUE;
+}
+
+int
+fhandler_base::munmap (HANDLE h, caddr_t addr, size_t len)
+{
+ set_errno (ENODEV);
+ return -1;
+}
+
+int
+fhandler_base::msync (HANDLE h, caddr_t addr, size_t len, int flags)
+{
+ set_errno (ENODEV);
+ return -1;
+}
+
+bool
+fhandler_base::fixup_mmap_after_fork (HANDLE h, int prot, int flags,
+ _off64_t offset, DWORD size,
+ void *address)
+{
+ set_errno (ENODEV);
+ return -1;
+}
+
+/* Implementation for anonymous maps. Using fhandler_dev_zero looks
+ quite the natural way. */
+HANDLE
+fhandler_dev_zero::mmap (caddr_t *addr, size_t len, int prot,
+ int flags, _off64_t off)
+{
+ HANDLE h;
+ void *base;
+
+ if (priv (flags) && !filler (prot))
+ {
+ /* Private anonymous maps are now implemented using VirtualAlloc.
+ This has two advantages:
+
+ - VirtualAlloc has a smaller footprint than a copy-on-write
+ anonymous map.
+
+ - It supports decommitting using VirtualFree, in contrast to
+ section maps. This allows minimum footprint private maps,
+ when using the (non-POSIX, yay-Linux) MAP_NORESERVE flag.
+ */
+ DWORD protect = gen_protect (prot, flags);
+ DWORD alloc_type = MEM_RESERVE | (noreserve (flags) ? 0 : MEM_COMMIT);
+ base = VirtualAlloc (*addr, len, alloc_type, protect);
+ if (!base && addr && !fixed (flags))
+ base = VirtualAlloc (NULL, len, alloc_type, protect);
+ if (!base || (fixed (flags) && base != *addr))
+ {
+ if (!base)
+ __seterrno ();
+ else
+ {
+ VirtualFree (base, 0, MEM_RELEASE);
+ set_errno (EINVAL);
+ debug_printf ("VirtualAlloc: address shift with MAP_FIXED given");
+ }
+ return INVALID_HANDLE_VALUE;
+ }
+ h = (HANDLE) 1; /* Fake handle to indicate success. */
+ }
+ else
+ {
+ h = mmap_func->CreateMapping (get_handle (), len, off, get_access (),
+ prot, flags, get_win32_name ());
+ if (!h)
+ {
+ __seterrno ();
+ debug_printf ("CreateMapping failed with %E");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ base = mmap_func->MapView (h, *addr, len, get_access(), prot, flags, off);
+ if (!base || (fixed (flags) && base != *addr))
+ {
+ if (!base)
+ __seterrno ();
+ else
+ {
+ UnmapViewOfFile (base);
+ set_errno (EINVAL);
+ debug_printf ("MapView: address shift with MAP_FIXED given");
+ }
+ CloseHandle (h);
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+ *addr = (caddr_t) base;
+ return h;
+}
+
+int
+fhandler_dev_zero::munmap (HANDLE h, caddr_t addr, size_t len)
+{
+ if (h == (HANDLE) 1) /* See fhandler_dev_zero::mmap. */
+ VirtualFree (addr, 0, MEM_RELEASE);
+ else
+ {
+ UnmapViewOfFile (addr);
+ CloseHandle (h);
+ }
+ return 0;
+}
+
+int
+fhandler_dev_zero::msync (HANDLE h, caddr_t addr, size_t len, int flags)
+{
+ return 0;
+}
+
+bool
+fhandler_dev_zero::fixup_mmap_after_fork (HANDLE h, int prot, int flags,
+ _off64_t offset, DWORD size,
+ void *address)
+{
+ /* Re-create the map */
+ void *base;
+ if (priv (flags) && !filler (prot))
+ {
+ DWORD alloc_type = MEM_RESERVE | (noreserve (flags) ? 0 : MEM_COMMIT);
+ /* Always allocate R/W so that ReadProcessMemory doesn't fail
+ due to a non-writable target address. The protection is
+ set to the correct one anyway in the fixup loop. */
+ base = VirtualAlloc (address, size, alloc_type, PAGE_READWRITE);
+ }
+ else
+ base = mmap_func->MapView (h, address, size, get_access (),
+ prot, flags, offset);
+ if (base != address)
+ {
+ MEMORY_BASIC_INFORMATION m;
+ VirtualQuery (address, &m, sizeof (m));
+ system_printf ("requested %p != %p mem alloc base %p, state %p, "
+ "size %d, %E", address, base, m.AllocationBase, m.State,
+ m.RegionSize);
+ }
+ return base == address;
+}
+
+/* Implementation for disk files and anonymous mappings. */
+HANDLE
+fhandler_disk_file::mmap (caddr_t *addr, size_t len, int prot,
+ int flags, _off64_t off)
+{
+ HANDLE h = mmap_func->CreateMapping (get_handle (), len, off, get_access (),
+ prot, flags, get_win32_name ());
+ if (!h)
+ {
+ __seterrno ();
+ debug_printf ("CreateMapping failed with %E");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ void *base = mmap_func->MapView (h, *addr, len, get_access (),
+ prot, flags, off);
+ if (!base || (fixed (flags) && base != *addr))
+ {
+ if (!base)
+ __seterrno ();
+ else
+ {
+ UnmapViewOfFile (base);
+ set_errno (EINVAL);
+ debug_printf ("MapView: address shift with MAP_FIXED given");
+ }
+ CloseHandle (h);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ *addr = (caddr_t) base;
+ return h;
+}
+
+int
+fhandler_disk_file::munmap (HANDLE h, caddr_t addr, size_t len)
+{
+ UnmapViewOfFile (addr);
+ CloseHandle (h);
+ return 0;
+}
+
+int
+fhandler_disk_file::msync (HANDLE h, caddr_t addr, size_t len, int flags)
+{
+ if (FlushViewOfFile (addr, len) == 0)
+ {
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+}
+
+bool
+fhandler_disk_file::fixup_mmap_after_fork (HANDLE h, int prot, int flags,
+ _off64_t offset, DWORD size,
+ void *address)
+{
+ /* Re-create the map */
+ void *base = mmap_func->MapView (h, address, size, get_access (),
+ prot, flags, offset);
+ if (base != address)
+ {
+ MEMORY_BASIC_INFORMATION m;
+ VirtualQuery (address, &m, sizeof (m));
+ system_printf ("requested %p != %p mem alloc base %p, state %p, "
+ "size %d, %E", address, base, m.AllocationBase, m.State,
+ m.RegionSize);
+ }
+ return base == address;
+}
+
+HANDLE
+fhandler_dev_mem::mmap (caddr_t *addr, size_t len, int prot,
+ int flags, _off64_t off)
+{
+ if (off >= mem_size
+ || (DWORD) len >= mem_size
+ || off + len >= mem_size)
+ {
+ set_errno (EINVAL);
+ debug_printf ("-1 = mmap(): illegal parameter, set EINVAL");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ UNICODE_STRING memstr;
+ RtlInitUnicodeString (&memstr, L"\\device\\physicalmemory");
+
+ OBJECT_ATTRIBUTES attr;
+ InitializeObjectAttributes (&attr, &memstr,
+ OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
+ NULL, NULL);
+
+ /* Section access is bit-wise ored, while on the Win32 level access
+ is only one of the values. It's not quite clear if the section
+ access has to be defined this way, or if SECTION_ALL_ACCESS would
+ be sufficient but this worked fine so far, so why change? */
+ ACCESS_MASK section_access;
+ if (prot & PROT_WRITE)
+ section_access = SECTION_MAP_READ | SECTION_MAP_WRITE;
+ else
+ section_access = SECTION_MAP_READ;
+
+ HANDLE h;
+ NTSTATUS ret = NtOpenSection (&h, section_access, &attr);
+ if (!NT_SUCCESS (ret))
+ {
+ __seterrno_from_nt_status (ret);
+ debug_printf ("-1 = mmap(): NtOpenSection failed with %E");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ void *base = MapViewNT (h, *addr, len, get_access (),
+ prot, flags | MAP_ANONYMOUS, off);
+ if (!base || (fixed (flags) && base != *addr))
+ {
+ if (!base)
+ __seterrno ();
+ else
+ {
+ NtUnmapViewOfSection (GetCurrentProcess (), base);
+ set_errno (EINVAL);
+ debug_printf ("MapView: address shift with MAP_FIXED given");
+ }
+ CloseHandle (h);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ *addr = (caddr_t) base;
+ return h;
+}
+
+int
+fhandler_dev_mem::munmap (HANDLE h, caddr_t addr, size_t len)
+{
+ NTSTATUS ret;
+ if (!NT_SUCCESS (ret = NtUnmapViewOfSection (INVALID_HANDLE_VALUE, addr)))
+ {
+ __seterrno_from_nt_status (ret);
+ return -1;
+ }
+ CloseHandle (h);
+ return 0;
+}
+
+int
+fhandler_dev_mem::msync (HANDLE h, caddr_t addr, size_t len, int flags)
+{
+ return 0;
+}
+
+bool
+fhandler_dev_mem::fixup_mmap_after_fork (HANDLE h, int prot, int flags,
+ _off64_t offset, DWORD size,
+ void *address)
+{
+ void *base = MapViewNT (h, address, size, get_access (), prot,
+ flags | MAP_ANONYMOUS, offset);
+ if (base != address)
+ {
+ MEMORY_BASIC_INFORMATION m;
+ VirtualQuery (address, &m, sizeof (m));
+ system_printf ("requested %p != %p mem alloc base %p, state %p, "
+ "size %d, %E", address, base, m.AllocationBase, m.State,
+ m.RegionSize);
+ }
+ return base == address;
+}
+
+/* Call to re-create all the file mappings in a forked child. Called from
+ the child in initialization. At this point we are passed a valid
+ mmapped_areas map, and all the HANDLE's are valid for the child, but
+ none of the mapped areas are in our address space. We need to iterate
+ through the map, doing the MapViewOfFile calls. */
+
+int __stdcall
+fixup_mmaps_after_fork (HANDLE parent)
+{
+ /* Iterate through the map */
+ list *map_list;
+ for (unsigned list_idx = 0;
+ (map_list = mmapped_areas.get_list (list_idx));
+ ++list_idx)
+ {
+ mmap_record *rec;
+ for (int record_idx = 0;
+ (rec = map_list->get_record (record_idx));
+ ++record_idx)
+ {
+ debug_printf ("fd %d, h 0x%x, address %p, len 0x%x, prot: 0x%x, "
+ "flags: 0x%x, offset %X",
+ rec->get_fd (), rec->get_handle (), rec->get_address (),
+ rec->get_len (), rec->get_prot (), rec->get_flags (),
+ rec->get_offset ());
+
+ fhandler_base *fh = rec->alloc_fh ();
+ bool ret = fh->fixup_mmap_after_fork (rec->get_handle (),
+ rec->get_prot (),
+ rec->get_flags () | MAP_FIXED,
+ rec->get_offset (),
+ rec->get_len (),
+ rec->get_address ());
+ rec->free_fh (fh);
+
+ if (!ret)
+ {
+ if (rec->attached ())
+ {
+ system_printf ("Warning: Fixup mapping beyond EOF failed");
+ continue;
+ }
+ return -1;
+ }
+
+ MEMORY_BASIC_INFORMATION mbi;
+ DWORD old_prot;
+
+ for (char *address = rec->get_address ();
+ address < rec->get_address () + rec->get_len ();
+ address += mbi.RegionSize)
+ {
+ if (!VirtualQueryEx (parent, address, &mbi, sizeof mbi))
+ {
+ system_printf ("VirtualQueryEx failed for MAP_PRIVATE "
+ "address %p, %E", address);
+ return -1;
+ }
+ /* Just skip reserved pages. */
+ if (mbi.State == MEM_RESERVE)
+ continue;
+ /* Copy-on-write pages must be copied to the child to circumvent
+ a strange notion how copy-on-write is supposed to work. */
+ if (rec->priv ())
+ {
+ if (rec->anonymous () && rec->noreserve ()
+ && !VirtualAlloc (address, mbi.RegionSize,
+ MEM_COMMIT, PAGE_READWRITE))
+ {
+ system_printf ("VirtualAlloc failed for MAP_PRIVATE "
+ "address %p, %E", address);
+ return -1;
+ }
+ if (mbi.Protect == PAGE_NOACCESS
+ && !VirtualProtectEx (parent, address, mbi.RegionSize,
+ PAGE_READONLY, &old_prot))
+ {
+ system_printf ("VirtualProtectEx failed for MAP_PRIVATE "
+ "address %p, %E", address);
+ return -1;
+ }
+ else if ((mbi.AllocationProtect == PAGE_WRITECOPY
+ || mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY)
+ && (mbi.Protect == PAGE_READWRITE
+ || mbi.Protect == PAGE_EXECUTE_READWRITE))
+ /* A WRITECOPY page which has been written to is set to
+ READWRITE, but that's an incompatible protection to
+ set the page to. Convert the protection to WRITECOPY
+ so that the below VirtualProtect doesn't fail. */
+ mbi.Protect <<= 1;
+
+ if (!ReadProcessMemory (parent, address, address,
+ mbi.RegionSize, NULL))
+ {
+ system_printf ("ReadProcessMemory failed for MAP_PRIVATE "
+ "address %p, %E", address);
+ return -1;
+ }
+ if (mbi.Protect == PAGE_NOACCESS
+ && !VirtualProtectEx (parent, address, mbi.RegionSize,
+ PAGE_NOACCESS, &old_prot))
+ {
+ system_printf ("WARNING: VirtualProtectEx to return to "
+ "PAGE_NOACCESS state in parent failed for "
+ "MAP_PRIVATE address %p, %E", address);
+ return -1;
+ }
+ }
+ /* Set child page protection to parent protection */
+ if (!VirtualProtect (address, mbi.RegionSize,
+ mbi.Protect, &old_prot))
+ {
+ MEMORY_BASIC_INFORMATION m;
+ VirtualQuery (address, &m, sizeof m);
+ system_printf ("VirtualProtect failed for "
+ "address %p, "
+ "parentstate: 0x%x, "
+ "state: 0x%x, "
+ "parentprot: 0x%x, "
+ "prot: 0x%x, %E",
+ address, mbi.State, m.State,
+ mbi.Protect, m.Protect);
+ return -1;
+ }
+ }
+ }
+ }
+
+ debug_printf ("succeeded");
+ return 0;
+}
diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h
new file mode 100644
index 00000000000..727151406d8
--- /dev/null
+++ b/winsup/cygwin/winsup.h
@@ -0,0 +1,362 @@
+/* winsup.h: main Cygwin header file.
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006 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. */
+
+#ifdef DEBUGIT
+#define spf(a, b, c) small_printf (a, b, c)
+#else
+#define spf(a, b, c) do {} while (0)
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define __INSIDE_CYGWIN__
+
+#define strlen __builtin_strlen
+#define strcmp __builtin_strcmp
+#define strcpy __builtin_strcpy
+#define memcpy __builtin_memcpy
+#define memcmp __builtin_memcmp
+#ifdef HAVE_BUILTIN_MEMSET
+# define memset __builtin_memset
+#endif
+
+#define NO_COPY __attribute__((nocommon)) __attribute__((section(".data_cygwin_nocopy")))
+#define NO_COPY_INIT __attribute__((section(".data_cygwin_nocopy")))
+
+#define EXPORT_ALIAS(sym,symalias) extern "C" __typeof (sym) symalias __attribute__ ((alias(#sym)));
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+
+#include <sys/types.h>
+#include <sys/strace.h>
+
+/* Declarations for functions used in C and C++ code. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern __uid32_t getuid32 (void);
+extern __uid32_t geteuid32 (void);
+extern int seteuid32 (__uid32_t);
+extern __gid32_t getegid32 (void);
+extern struct passwd *getpwuid32 (__uid32_t);
+extern struct passwd *getpwnam (const char *);
+extern struct __sFILE64 *fopen64 (const char *, const char *);
+extern struct hostent *cygwin_gethostbyname (const char *name);
+extern unsigned long cygwin_inet_addr (const char *cp);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Note that MAX_PATH is defined in the windows headers */
+/* There is also PATH_MAX and MAXPATHLEN.
+ PATH_MAX is from Posix and does *not* include the trailing NUL.
+ MAXPATHLEN is from Unix.
+
+ Thou shalt use CYG_MAX_PATH throughout. It avoids the NUL vs no-NUL
+ issue and is neither of the Unixy ones [so we can punt on which
+ one is the right one to use].
+
+ Windows ANSI calls are limited to MAX_PATH in length. Cygwin calls that
+ thunk through to Windows Wide calls are limited to 32K. We define
+ CYG_MAX_PATH as a convenient, not to short, not too long 'happy medium'.
+
+ */
+
+#define CYG_MAX_PATH (MAX_PATH)
+
+#ifdef __cplusplus
+
+extern const char case_folded_lower[];
+#define cyg_tolower(c) (case_folded_lower[(unsigned char)(c)])
+extern const char case_folded_upper[];
+#define cyg_toupper(c) (case_folded_upper[(unsigned char)(c)])
+
+#ifndef MALLOC_DEBUG
+#define cfree newlib_cfree_dont_use
+#endif
+
+#define WIN32_LEAN_AND_MEAN 1
+#define _WINGDI_H
+#define _WINUSER_H
+#define _WINNLS_H
+#define _WINVER_H
+#define _WINNETWK_H
+#define _WINSVC_H
+#include <windows.h>
+#include <wincrypt.h>
+#include <lmcons.h>
+#undef _WINGDI_H
+#undef _WINUSER_H
+#undef _WINNLS_H
+#undef _WINVER_H
+#undef _WINNETWK_H
+#undef _WINSVC_H
+
+#include "wincap.h"
+
+/* The one function we use from winuser.h most of the time */
+extern "C" DWORD WINAPI GetLastError (void);
+
+enum codepage_type {ansi_cp, oem_cp};
+extern codepage_type current_codepage;
+
+UINT get_cp ();
+
+int __stdcall sys_wcstombs(char *, int, const WCHAR *, int = -1)
+ __attribute__ ((regparm(3)));
+
+int __stdcall sys_mbstowcs(WCHAR *, const char *, int)
+ __attribute__ ((regparm(3)));
+
+/* Used to check if Cygwin DLL is dynamically loaded. */
+extern int dynamically_loaded;
+
+extern int cygserver_running;
+
+#define _MT_SAFE // DELTEME someday
+
+#define TITLESIZE 1024
+
+#include "debug.h"
+
+/* Events/mutexes */
+extern HANDLE tty_mutex;
+
+/**************************** Convenience ******************************/
+
+/* Used to define status flag accessor methods */
+#define IMPLEMENT_STATUS_FLAG(type,flag) \
+ void flag (type val) { status.flag = (val); } \
+ type flag () const { return (type) status.flag; }
+
+/* Used when treating / and \ as equivalent. */
+#define isdirsep(ch) \
+ ({ \
+ char __c = (ch); \
+ ((__c) == '/' || (__c) == '\\'); \
+ })
+
+/* Convert a signal to a signal mask */
+#define SIGTOMASK(sig) (1 << ((sig) - signal_shift_subtract))
+extern unsigned int signal_shift_subtract;
+
+extern int __api_fatal_exit_val;
+#define set_api_fatal_return(n) do {extern int __api_fatal_exit_val; __api_fatal_exit_val = (n);} while (0)
+#define api_fatal(fmt, args...) __api_fatal ("%P: *** " fmt,## args)
+
+#undef issep
+#define issep(ch) (strchr (" \t\n\r", (ch)) != NULL)
+
+#define isabspath(p) \
+ (isdirsep (*(p)) || (isalpha (*(p)) && (p)[1] == ':' && (!(p)[2] || isdirsep ((p)[2]))))
+
+/******************** Initialization/Termination **********************/
+
+class per_process;
+/* cygwin .dll initialization */
+void dll_crt0 (per_process *) __asm__ ("_dll_crt0__FP11per_process");
+extern "C" void __stdcall _dll_crt0 ();
+extern void dll_crt0_1 (void *);
+extern void dll_dllcrt0_1 (void *);
+
+/* dynamically loaded dll initialization */
+extern "C" int dll_dllcrt0 (HMODULE, per_process *);
+
+/* dynamically loaded dll initialization for non-cygwin apps */
+extern "C" int dll_noncygwin_dllcrt0 (HMODULE, per_process *);
+
+/* exit the program */
+
+enum exit_states
+ {
+ ES_NOT_EXITING = 0,
+ ES_SET_MUTO,
+ ES_GLOBAL_DTORS,
+ ES_EVENTS_TERMINATE,
+ ES_THREADTERM,
+ ES_SIGNAL,
+ ES_CLOSEALL,
+ ES_HUP_PGRP,
+ ES_HUP_SID,
+ ES_EXEC_EXIT,
+ ES_TITLE,
+ ES_TTY_TERMINATE,
+ ES_FINAL
+ };
+
+extern exit_states exit_state;
+void __stdcall do_exit (int) __attribute__ ((regparm (1), noreturn));
+
+/* UID/GID */
+void uinfo_init ();
+
+#define ILLEGAL_UID16 ((__uid16_t)-1)
+#define ILLEGAL_UID ((__uid32_t)-1)
+#define ILLEGAL_GID16 ((__gid16_t)-1)
+#define ILLEGAL_GID ((__gid32_t)-1)
+#define ILLEGAL_SEEK ((_off64_t)-1)
+
+#define uid16touid32(u16) ((u16)==ILLEGAL_UID16?ILLEGAL_UID:(__uid32_t)(u16))
+#define gid16togid32(g16) ((g16)==ILLEGAL_GID16?ILLEGAL_GID:(__gid32_t)(g16))
+
+/* Convert LARGE_INTEGER into long long */
+#define get_ll(pl) (((long long) (pl).HighPart << 32) | (pl).LowPart)
+
+/* various events */
+void events_init ();
+void events_terminate ();
+
+void __stdcall close_all_files (bool = false);
+
+/* debug_on_trap support. see exceptions.cc:try_to_debug() */
+extern "C" void error_start_init (const char*);
+extern "C" int try_to_debug (bool waitloop = 1);
+
+extern void ld_preload ();
+
+void set_file_api_mode (codepage_type);
+
+extern bool cygwin_finished_initializing;
+
+/**************************** Miscellaneous ******************************/
+
+void __stdcall set_std_handle (int);
+int __stdcall stat_dev (DWORD, int, unsigned long, struct __stat64 *);
+
+__ino64_t __stdcall hash_path_name (__ino64_t hash, const char *name) __attribute__ ((regparm(2)));
+void __stdcall nofinalslash (const char *src, char *dst) __attribute__ ((regparm(2)));
+extern "C" char *__stdcall rootdir (const char *full_path, char *root_path) __attribute__ ((regparm(2)));
+
+/* String manipulation */
+extern "C" char *__stdcall strccpy (char *s1, const char **s2, char c);
+extern "C" int __stdcall strcasematch (const char *s1, const char *s2) __attribute__ ((regparm(2)));
+extern "C" int __stdcall strncasematch (const char *s1, const char *s2, size_t n) __attribute__ ((regparm(3)));
+extern "C" char *__stdcall strcasestr (const char *searchee, const char *lookfor) __attribute__ ((regparm(2)));
+
+void *hook_or_detect_cygwin (const char *, const void *, WORD&) __attribute__ ((regparm (3)));
+
+/* Time related */
+void __stdcall totimeval (struct timeval *, FILETIME *, int, int);
+long __stdcall to_time_t (FILETIME *);
+void __stdcall to_timestruc_t (FILETIME *, timestruc_t *);
+void __stdcall time_as_timestruc_t (timestruc_t *);
+void __stdcall timeval_to_filetime (const struct timeval *, FILETIME *);
+
+/* Console related */
+void __stdcall set_console_title (char *);
+void init_console_handler (bool);
+
+void init_global_security ();
+
+int __stdcall check_invalid_virtual_addr (const void *s, unsigned sz) __attribute__ ((regparm(2)));
+
+ssize_t check_iovec (const struct iovec *, int, bool) __attribute__ ((regparm(3)));
+#define check_iovec_for_read(a, b) check_iovec ((a), (b), false)
+#define check_iovec_for_write(a, b) check_iovec ((a), (b), true)
+
+#define set_winsock_errno() __set_winsock_errno (__FUNCTION__, __LINE__)
+void __set_winsock_errno (const char *fn, int ln) __attribute__ ((regparm(2)));
+
+extern bool wsock_started;
+
+/* Printf type functions */
+extern "C" void __api_fatal (const char *, ...) __attribute__ ((noreturn));
+extern "C" int __small_sprintf (char *dst, const char *fmt, ...) /*__attribute__ ((regparm (2)))*/;
+extern "C" int __small_vsprintf (char *dst, const char *fmt, va_list ap) /*__attribute__ ((regparm (3)))*/;
+extern void multiple_cygwin_problem (const char *, unsigned, unsigned);
+
+extern "C" void vklog (int priority, const char *message, va_list ap);
+extern "C" void klog (int priority, const char *message, ...);
+bool child_copy (HANDLE, bool, ...);
+
+int symlink_worker (const char *, const char *, bool, bool)
+ __attribute__ ((regparm (3)));
+
+class path_conv;
+
+int fcntl_worker (int fd, int cmd, void *arg);
+
+__ino64_t __stdcall readdir_get_ino (struct __DIR *dir, const char *path, bool dot_dot) __attribute__ ((regparm (3)));
+
+extern "C" int low_priority_sleep (DWORD) __attribute__ ((regparm (1)));
+#define SLEEP_0_STAY_LOW INFINITE
+
+/* Returns the real page size, not the allocation size. */
+size_t getsystempagesize ();
+
+/* mmap functions. */
+void mmap_init ();
+int mmap_is_attached_or_noreserve_page (ULONG_PTR addr);
+
+int winprio_to_nice (DWORD) __attribute__ ((regparm (1)));
+DWORD nice_to_winprio (int &) __attribute__ ((regparm (1)));
+
+bool __stdcall create_pipe (PHANDLE, PHANDLE, LPSECURITY_ATTRIBUTES, DWORD)
+ __attribute__ ((regparm (3)));
+#define CreatePipe create_pipe
+
+inline bool flush_file_buffers (HANDLE h)
+{
+ return (GetFileType (h) != FILE_TYPE_PIPE) ? FlushFileBuffers (h) : true;
+}
+#define FlushFileBuffers flush_file_buffers
+
+/**************************** Exports ******************************/
+
+extern "C" {
+int cygwin_select (int , fd_set *, fd_set *, fd_set *,
+ struct timeval *to);
+int cygwin_gethostname (char *__name, size_t __len);
+
+extern DWORD binmode;
+extern char _data_start__, _data_end__, _bss_start__, _bss_end__;
+extern void (*__CTOR_LIST__) (void);
+extern void (*__DTOR_LIST__) (void);
+extern SYSTEM_INFO system_info;
+};
+
+/*************************** Unsorted ******************************/
+
+#define WM_ASYNCIO 0x8000 // WM_APP
+
+
+#define STD_RBITS (S_IRUSR | S_IRGRP | S_IROTH)
+#define STD_WBITS (S_IWUSR)
+#define STD_XBITS (S_IXUSR | S_IXGRP | S_IXOTH)
+#define NO_W ~(S_IWUSR | S_IWGRP | S_IWOTH)
+#define NO_R ~(S_IRUSR | S_IRGRP | S_IROTH)
+#define NO_X ~(S_IXUSR | S_IXGRP | S_IXOTH)
+
+/* The title on program start. */
+extern char *old_title;
+extern bool display_title;
+extern bool transparent_exe;
+
+extern bool in_forkee;
+extern bool in_dllentry;
+
+extern HANDLE hMainThread;
+extern HANDLE hMainProc;
+extern HANDLE hProcToken;
+extern HANDLE hProcImpToken;
+extern HANDLE hExeced;
+extern HMODULE cygwin_hmodule;
+
+extern bool cygwin_testing;
+
+extern char almost_null[];
+
+#endif /* defined __cplusplus */