summaryrefslogtreecommitdiff
path: root/winsup
diff options
context:
space:
mode:
Diffstat (limited to 'winsup')
-rw-r--r--winsup/cygwin/ChangeLog11
-rw-r--r--winsup/cygwin/child_info.h142
-rw-r--r--winsup/cygwin/fork.cc714
-rw-r--r--winsup/cygwin/hookapi.cc237
4 files changed, 1104 insertions, 0 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index f99f88bfdc2..45dfda92356 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,14 @@
+2006-07-06 Christopher Faylor <cgf@timesys.com>
+
+ * hookapi.cc: Add comment header
+ (putmem): Make static.
+ (get_export): Ditto.
+ (rvadelta): Ditto. Don't assume that a section which ends where the
+ import_rva begins is the import list.
+
+ * child_info.h: Update copyright.
+ * fork.cc: Ditto.
+
2006-07-06 Corinna Vinschen <corinna@vinschen.de>
* include/cygwin/in6.h (struct in6_addr): Fix typo.
diff --git a/winsup/cygwin/child_info.h b/winsup/cygwin/child_info.h
new file mode 100644
index 00000000000..9f11015ddcb
--- /dev/null
+++ b/winsup/cygwin/child_info.h
@@ -0,0 +1,142 @@
+/* child_info.h: shared child info for cygwin
+
+ Copyright 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 <setjmp.h>
+
+enum child_info_types
+{
+ _PROC_EXEC,
+ _PROC_SPAWN,
+ _PROC_FORK,
+ _PROC_WHOOPS
+};
+
+enum child_status
+{
+ _CI_STRACED = 0x01,
+ _CI_ISCYGWIN = 0x02,
+ _CI_SAW_CTRL_C = 0x04
+
+};
+
+#define OPROC_MAGIC_MASK 0xff00ff00
+#define OPROC_MAGIC_GENERIC 0xaf00f000
+
+#define PROC_MAGIC_GENERIC 0xaf00fa00
+
+#define PROC_EXEC (_PROC_EXEC)
+#define PROC_SPAWN (_PROC_SPAWN)
+#define PROC_FORK (_PROC_FORK)
+
+#define EXEC_MAGIC_SIZE sizeof(child_info)
+
+/* Change this value if you get a message indicating that it is out-of-sync. */
+#define CURR_CHILD_INFO_MAGIC 0x704d1f7eU
+
+/* NOTE: Do not make gratuitous changes to the names or organization of the
+ below class. The layout is checksummed to determine compatibility between
+ different cygwin versions. */
+class child_info
+{
+public:
+ DWORD zero[4]; // must be zeroed
+ DWORD cb; // size of this record
+ DWORD intro; // improbable string
+ unsigned long magic; // magic number unique to child_info
+ unsigned short type; // type of record, exec, spawn, fork
+ HANDLE subproc_ready; // used for synchronization with parent
+ HANDLE user_h;
+ HANDLE parent;
+ init_cygheap *cygheap;
+ void *cygheap_max;
+ DWORD cygheap_reserve_sz;
+ unsigned char flag;
+ unsigned fhandler_union_cb;
+ int retry; // number of times we've tried to start child process
+ DWORD exit_code; // process exit code
+ static int retry_count;// retry count;
+ child_info (unsigned, child_info_types, bool);
+ child_info (): subproc_ready (NULL), parent (NULL) {}
+ ~child_info ();
+ void ready (bool);
+ bool sync (int, HANDLE&, DWORD) __attribute__ ((regparm (3)));
+ DWORD proc_retry (HANDLE) __attribute__ ((regparm (2)));
+ bool isstraced () const {return !!(flag & _CI_STRACED);}
+ bool iscygwin () const {return !!(flag & _CI_ISCYGWIN);}
+ bool saw_ctrl_c () const {return !!(flag & _CI_SAW_CTRL_C);}
+ void set_saw_ctrl_c () {flag |= _CI_SAW_CTRL_C;}
+};
+
+class mount_info;
+class _pinfo;
+
+class child_info_fork: public child_info
+{
+public:
+ HANDLE forker_finished;// for synchronization with child
+ DWORD stacksize; // size of parent stack
+ jmp_buf jmp; // where child will jump to
+ void *stacktop; // location of top of parent stack
+ void *stackbottom; // location of bottom of parent stack
+ child_info_fork ();
+ void handle_fork () __attribute__ ((regparm (1)));;
+ bool handle_failure (DWORD) __attribute__ ((regparm (2)));
+ void alloc_stack ();
+ void alloc_stack_hard_way (volatile char *);
+};
+
+class fhandler_base;
+
+class cygheap_exec_info
+{
+public:
+ char *old_title;
+ int argc;
+ char **argv;
+ int envc;
+ char **envp;
+ HANDLE myself_pinfo;
+};
+
+class child_info_spawn: public child_info
+{
+public:
+ cygheap_exec_info *moreinfo;
+
+ ~child_info_spawn ()
+ {
+ if (moreinfo)
+ {
+ if (moreinfo->old_title)
+ cfree (moreinfo->old_title);
+ if (moreinfo->envp)
+ {
+ for (char **e = moreinfo->envp; *e; e++)
+ cfree (*e);
+ cfree (moreinfo->envp);
+ }
+ CloseHandle (moreinfo->myself_pinfo);
+ cfree (moreinfo);
+ }
+ }
+ child_info_spawn (): moreinfo (NULL) {};
+ child_info_spawn (child_info_types, bool);
+ void *operator new (size_t, void *p) __attribute__ ((nothrow)) {return p;}
+ void set (child_info_types ci, bool b) { new (this) child_info_spawn (ci, b);}
+ void handle_spawn () __attribute__ ((regparm (1)));
+};
+
+void __stdcall init_child_info (DWORD, child_info *, HANDLE);
+
+extern "C" {
+extern child_info *child_proc_info;
+extern child_info_spawn *spawn_info asm ("_child_proc_info");
+extern child_info_fork *fork_info asm ("_child_proc_info");
+}
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
new file mode 100644
index 00000000000..20436291143
--- /dev/null
+++ b/winsup/cygwin/fork.cc
@@ -0,0 +1,714 @@
+/* fork.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygheap.h"
+#include "child_info.h"
+#include "cygtls.h"
+#include "perprocess.h"
+#include "dll_init.h"
+#include "sync.h"
+#include "shared_info.h"
+#include "cygmalloc.h"
+#include "cygthread.h"
+
+#define NPIDS_HELD 4
+
+/* Timeout to wait for child to start, parent to init child, etc. */
+/* FIXME: Once things stabilize, bump up to a few minutes. */
+#define FORK_WAIT_TIMEOUT (300 * 1000) /* 300 seconds */
+
+class frok
+{
+ dll *first_dll;
+ bool load_dlls;
+ child_info_fork ch;
+ const char *error;
+ int child_pid;
+ int this_errno;
+ int __stdcall parent (void *esp);
+ int __stdcall child (void *esp);
+ friend int fork ();
+};
+
+static void
+resume_child (HANDLE forker_finished)
+{
+ SetEvent (forker_finished);
+ debug_printf ("signalled child");
+ return;
+}
+
+/* Notify parent that it is time for the next step. */
+static void __stdcall
+sync_with_parent (const char *s, bool hang_self)
+{
+ debug_printf ("signalling parent: %s", s);
+ fork_info->ready (false);
+ if (hang_self)
+ {
+ HANDLE h = fork_info->forker_finished;
+ /* Wait for the parent to fill in our stack and heap.
+ Don't wait forever here. If our parent dies we don't want to clog
+ the system. If the wait fails, we really can't continue so exit. */
+ DWORD psync_rc = WaitForSingleObject (h, FORK_WAIT_TIMEOUT);
+ debug_printf ("awake");
+ switch (psync_rc)
+ {
+ case WAIT_TIMEOUT:
+ api_fatal ("WFSO timed out %s", s);
+ break;
+ case WAIT_FAILED:
+ if (GetLastError () == ERROR_INVALID_HANDLE &&
+ WaitForSingleObject (fork_info->forker_finished, 1) != WAIT_FAILED)
+ break;
+ api_fatal ("WFSO failed %s, fork_finished %p, %E", s,
+ fork_info->forker_finished);
+ break;
+ default:
+ debug_printf ("no problems");
+ break;
+ }
+ }
+}
+
+int __stdcall
+frok::child (void *)
+{
+ HANDLE& hParent = ch.parent;
+ extern void fixup_hooks_after_fork ();
+ extern void fixup_timers_after_fork ();
+ debug_printf ("child is running. pid %d, ppid %d, stack here %p",
+ myself->pid, myself->ppid, __builtin_frame_address (0));
+
+ sync_with_parent ("after longjmp", true);
+ sigproc_printf ("hParent %p, child 1 first_dll %p, load_dlls %d", hParent,
+ first_dll, load_dlls);
+
+ /* If we've played with the stack, stacksize != 0. That means that
+ fork() was invoked from other than the main thread. Make sure that
+ the threadinfo information is properly set up. */
+ if (fork_info->stacksize)
+ {
+ _main_tls = &_my_tls;
+ _main_tls->init_thread (NULL, NULL);
+ _main_tls->local_clib = *_impure_ptr;
+ _impure_ptr = &_main_tls->local_clib;
+ }
+
+ if (wincap.has_security ())
+ {
+ set_cygwin_privileges (hProcImpToken);
+ cygheap->user.reimpersonate ();
+ }
+
+#ifdef DEBUGGING
+ char c;
+ if (GetEnvironmentVariable ("FORKDEBUG", &c, 1))
+ try_to_debug ();
+ char buf[80];
+ /* This is useful for debugging fork problems. Use gdb to attach to
+ the pid reported here. */
+ if (GetEnvironmentVariable ("CYGWIN_FORK_SLEEP", buf, sizeof (buf)))
+ {
+ small_printf ("Sleeping %d after fork, pid %u\n", atoi (buf), GetCurrentProcessId ());
+ Sleep (atoi (buf));
+ }
+#endif
+
+ set_file_api_mode (current_codepage);
+
+ MALLOC_CHECK;
+
+#ifdef USE_SERVER
+ /* Incredible but true: If we use sockets and SYSV IPC shared memory,
+ there's a good chance that a duplicated socket in the child occupies
+ memory which is needed to duplicate shared memory from the parent
+ process, if the shared memory hasn't been duplicated already.
+ The same goes very likely for "normal" mmap shared memory, too, but
+ with SYSV IPC it was the first time observed. So, *never* fixup
+ fdtab before fixing up shared memory. */
+ if (fixup_shms_after_fork ())
+ api_fatal ("recreate_shm areas after fork failed");
+#endif
+
+ MALLOC_CHECK;
+
+ /* If we haven't dynamically loaded any dlls, just signal
+ the parent. Otherwise, load all the dlls, tell the parent
+ that we're done, and wait for the parent to fill in the.
+ loaded dlls' data/bss. */
+ if (!load_dlls)
+ {
+ cygheap->fdtab.fixup_after_fork (hParent);
+ sync_with_parent ("performed fork fixup", false);
+ }
+ else
+ {
+ dlls.load_after_fork (hParent, first_dll);
+ cygheap->fdtab.fixup_after_fork (hParent);
+ sync_with_parent ("loaded dlls", true);
+ }
+
+ init_console_handler (myself->ctty >= 0);
+ ForceCloseHandle1 (fork_info->forker_finished, forker_finished);
+
+ pthread::atforkchild ();
+ fixup_timers_after_fork ();
+ cygbench ("fork-child");
+ ld_preload ();
+ fixup_hooks_after_fork ();
+ _my_tls.fixup_after_fork ();
+ wait_for_sigthread (true);
+ cygwin_finished_initializing = true;
+ return 0;
+}
+
+#define NO_SLOW_PID_REUSE
+#ifndef NO_SLOW_PID_REUSE
+static void
+slow_pid_reuse (HANDLE h)
+{
+ static NO_COPY HANDLE last_fork_procs[NPIDS_HELD];
+ static NO_COPY unsigned nfork_procs;
+
+ if (nfork_procs >= (sizeof (last_fork_procs) / sizeof (last_fork_procs [0])))
+ nfork_procs = 0;
+ /* Keep a list of handles to child processes sitting around to prevent
+ Windows from reusing the same pid n times in a row. Having the same pids
+ close in succesion confuses bash. Keeping a handle open will stop
+ windows from reusing the same pid. */
+ if (last_fork_procs[nfork_procs])
+ ForceCloseHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
+ if (DuplicateHandle (hMainProc, h, hMainProc, &last_fork_procs[nfork_procs],
+ 0, FALSE, DUPLICATE_SAME_ACCESS))
+ ProtectHandle1 (last_fork_procs[nfork_procs], fork_stupidity);
+ else
+ {
+ last_fork_procs[nfork_procs] = NULL;
+ system_printf ("couldn't create last_fork_proc, %E");
+ }
+ nfork_procs++;
+}
+#endif
+
+int __stdcall
+frok::parent (void *stack_here)
+{
+ HANDLE forker_finished;
+ DWORD rc;
+ child_pid = -1;
+ error = NULL;
+ this_errno = 0;
+ bool fix_impersonation = false;
+ pinfo child;
+ static char errbuf[256];
+
+ pthread::atforkprepare ();
+
+ int c_flags = GetPriorityClass (hMainProc);
+ debug_printf ("priority class %d", c_flags);
+
+ /* If we don't have a console, then don't create a console for the
+ child either. */
+ HANDLE console_handle = CreateFile ("CONOUT$", GENERIC_WRITE,
+ FILE_SHARE_WRITE, &sec_none_nih,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (console_handle != INVALID_HANDLE_VALUE)
+ CloseHandle (console_handle);
+ else
+ c_flags |= DETACHED_PROCESS;
+
+ /* Some file types (currently only sockets) need extra effort in the
+ parent after CreateProcess and before copying the datastructures
+ to the child. So we have to start the child in suspend state,
+ unfortunately, to avoid a race condition. */
+ if (cygheap->fdtab.need_fixup_before ())
+ c_flags |= CREATE_SUSPENDED;
+
+ /* Remember the address of the first loaded dll and decide
+ if we need to load dlls. We do this here so that this
+ information will be available in the parent and, when
+ the stack is copied, in the child. */
+ first_dll = dlls.start.next;
+ load_dlls = dlls.reload_on_fork && dlls.loaded_dlls;
+
+ /* This will help some of the confusion. */
+ fflush (stdout);
+
+ forker_finished = CreateEvent (&sec_all, FALSE, FALSE, NULL);
+ if (forker_finished == NULL)
+ {
+ this_errno = geterrno_from_win_error ();
+ error = "unable to allocate forker_finished event";
+ return -1;
+ }
+
+ ProtectHandleINH (forker_finished);
+
+ ch.forker_finished = forker_finished;
+
+ ch.stackbottom = _tlsbase;
+ ch.stacktop = stack_here;
+ ch.stacksize = (char *) ch.stackbottom - (char *) stack_here;
+ debug_printf ("stack - bottom %p, top %p, size %d",
+ ch.stackbottom, ch.stacktop, ch.stacksize);
+
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+
+ memset (&si, 0, sizeof (si));
+ si.cb = sizeof (STARTUPINFO);
+ si.lpReserved2 = (LPBYTE) &ch;
+ si.cbReserved2 = sizeof (ch);
+
+ syscall_printf ("CreateProcess (%s, %s, 0, 0, 1, %p, 0, 0, %p, %p)",
+ myself->progname, myself->progname, c_flags, &si, &pi);
+ bool locked = __malloc_lock ();
+ time_t start_time = time (NULL);
+
+ /* Remove impersonation */
+ cygheap->user.deimpersonate ();
+ fix_impersonation = true;
+
+ while (1)
+ {
+ rc = CreateProcess (myself->progname, /* image to run */
+ myself->progname, /* what we send in arg0 */
+ &sec_none_nih,
+ &sec_none_nih,
+ TRUE, /* inherit handles from parent */
+ c_flags,
+ NULL, /* environment filled in later */
+ 0, /* use current drive/directory */
+ &si,
+ &pi);
+
+ if (!rc)
+ {
+ this_errno = geterrno_from_win_error ();
+ error = "CreateProcessA failed";
+ memset (&pi, 0, sizeof (pi));
+ goto cleanup;
+ }
+
+ /* Fixup the parent datastructure if needed and resume the child's
+ main thread. */
+ if (c_flags & CREATE_SUSPENDED)
+ {
+ cygheap->fdtab.fixup_before_fork (pi.dwProcessId);
+ ResumeThread (pi.hThread);
+ }
+
+ CloseHandle (pi.hThread);
+
+ /* Protect the handle but name it similarly to the way it will
+ be called in subproc handling. */
+ ProtectHandle1 (pi.hProcess, childhProc);
+
+ strace.write_childpid (ch, pi.dwProcessId);
+
+ /* Wait for subproc to initialize itself. */
+ if (!ch.sync (pi.dwProcessId, pi.hProcess, FORK_WAIT_TIMEOUT))
+ {
+ DWORD exit_code = ch.proc_retry (pi.hProcess);
+ if (!exit_code)
+ continue;
+ this_errno = EAGAIN;
+ /* Not thread safe, but do we care? */
+ __small_sprintf (errbuf, "died waiting for longjmp before initialization, "
+ "retry %d, exit code %p", ch.retry, exit_code);
+ error = errbuf;
+ goto cleanup;
+ }
+ break;
+ }
+
+ /* Restore impersonation */
+ cygheap->user.reimpersonate ();
+ fix_impersonation = false;
+
+ child_pid = cygwin_pid (pi.dwProcessId);
+ child.init (child_pid, 1, NULL);
+
+ if (!child)
+ {
+ this_errno = get_errno () == ENOMEM ? ENOMEM : EAGAIN;
+#ifdef DEBUGGING
+ error = "pinfo failed";
+#else
+ syscall_printf ("pinfo failed");
+#endif
+ goto cleanup;
+ }
+
+ child->start_time = start_time; /* Register child's starting time. */
+ child->nice = myself->nice;
+
+ /* Initialize things that are done later in dll_crt0_1 that aren't done
+ for the forkee. */
+ strcpy (child->progname, myself->progname);
+
+ /* Fill in fields in the child's process table entry. */
+ child->dwProcessId = pi.dwProcessId;
+ child.hProcess = pi.hProcess;
+
+ /* Hopefully, this will succeed. The alternative to doing things this
+ way is to reserve space prior to calling CreateProcess and then fill
+ it in afterwards. This requires more bookkeeping than I like, though,
+ so we'll just do it the easy way. So, terminate any child process if
+ we can't actually record the pid in the internal table. */
+ if (!child.remember (false))
+ {
+ TerminateProcess (pi.hProcess, 1);
+ this_errno = EAGAIN;
+#ifdef DEBUGGING0
+ error = "child.remember failed";
+#endif
+ goto cleanup;
+ }
+
+#ifndef NO_SLOW_PID_REUSE
+ slow_pid_reuse (pi.hProcess);
+#endif
+
+ /* CHILD IS STOPPED */
+ debug_printf ("child is alive (but stopped)");
+
+ /* Initialize, in order: stack, dll data, dll bss.
+ data, bss, heap were done earlier (in dcrt0.cc)
+ Note: variables marked as NO_COPY will not be copied since they are
+ placed in a protected segment. */
+
+ MALLOC_CHECK;
+ const void *impure_beg;
+ const void *impure_end;
+ const char *impure;
+ if (&_my_tls == _main_tls)
+ impure_beg = impure_end = impure = NULL;
+ else
+ {
+ impure = "impure";
+ impure_beg = _impure_ptr;
+ impure_end = _impure_ptr + 1;
+ }
+ rc = child_copy (pi.hProcess, true,
+ "stack", stack_here, ch.stackbottom,
+ impure, impure_beg, impure_end,
+ NULL);
+
+ __malloc_unlock ();
+ locked = false;
+ MALLOC_CHECK;
+ if (!rc)
+ {
+ this_errno = get_errno ();
+ DWORD exit_code;
+ if (!GetExitCodeProcess (pi.hProcess, &exit_code))
+ exit_code = 0xdeadbeef;
+ __small_sprintf (errbuf, "pid %u, exitval %p", pi.dwProcessId, exit_code);
+ error = errbuf;
+ goto cleanup;
+ }
+
+ /* Now fill data/bss of any DLLs that were linked into the program. */
+ for (dll *d = dlls.istart (DLL_LINK); d; d = dlls.inext ())
+ {
+ debug_printf ("copying data/bss of a linked dll");
+ if (!child_copy (pi.hProcess, true,
+ "linked dll data", d->p.data_start, d->p.data_end,
+ "linked dll bss", d->p.bss_start, d->p.bss_end,
+ NULL))
+ {
+ this_errno = get_errno ();
+#ifdef DEBUGGING
+ DWORD exit_code;
+ if (!GetExitCodeProcess (pi.hProcess, &exit_code))
+ exit_code = 0xdeadbeef;
+ __small_sprintf (errbuf, "pid %u, exitval %p", pi.dwProcessId, exit_code);
+ error = errbuf;
+#endif
+ goto cleanup;
+ }
+ }
+
+ /* Start thread, and then wait for it to reload dlls. */
+ resume_child (forker_finished);
+ if (!ch.sync (child->pid, pi.hProcess, FORK_WAIT_TIMEOUT))
+ {
+ this_errno = EAGAIN;
+ error = "died waiting for dll loading";
+ goto cleanup;
+ }
+
+ /* If DLLs were loaded in the parent, then the child has reloaded all
+ of them and is now waiting to have all of the individual data and
+ bss sections filled in. */
+ if (load_dlls)
+ {
+ /* CHILD IS STOPPED */
+ /* write memory of reloaded dlls */
+ for (dll *d = dlls.istart (DLL_LOAD); d; d = dlls.inext ())
+ {
+ debug_printf ("copying data/bss for a loaded dll");
+ if (!child_copy (pi.hProcess, true,
+ "loaded dll data", d->p.data_start, d->p.data_end,
+ "loaded dll bss", d->p.bss_start, d->p.bss_end,
+ NULL))
+ {
+ this_errno = get_errno ();
+#ifdef DEBUGGING
+ error = "copying data/bss for a loaded dll";
+#endif
+ goto cleanup;
+ }
+ }
+ /* Start the child up again. */
+ resume_child (forker_finished);
+ }
+
+ ForceCloseHandle (forker_finished);
+ forker_finished = NULL;
+ pthread::atforkparent ();
+
+ return child_pid;
+
+/* Common cleanup code for failure cases */
+cleanup:
+ if (fix_impersonation)
+ cygheap->user.reimpersonate ();
+ if (locked)
+ __malloc_unlock ();
+
+ /* Remember to de-allocate the fd table. */
+ if (pi.hProcess && !child.hProcess)
+ ForceCloseHandle1 (pi.hProcess, childhProc);
+ if (forker_finished)
+ ForceCloseHandle (forker_finished);
+ debug_printf ("returning -1");
+ return -1;
+}
+
+extern "C" int
+fork ()
+{
+ frok grouped;
+ MALLOC_CHECK;
+
+ debug_printf ("entering");
+ grouped.first_dll = NULL;
+ grouped.load_dlls = 0;
+
+ int res;
+ int ischild;
+
+ myself->set_has_pgid_children ();
+
+ if (grouped.ch.parent == NULL)
+ return -1;
+ if (grouped.ch.subproc_ready == NULL)
+ {
+ system_printf ("unable to allocate subproc_ready event, %E");
+ return -1;
+ }
+
+ if (sig_send (NULL, __SIGHOLD))
+ {
+ if (exit_state)
+ Sleep (INFINITE);
+ set_errno (EAGAIN);
+ return -1;
+ }
+
+ ischild = setjmp (grouped.ch.jmp);
+
+ void *esp;
+ __asm__ volatile ("movl %%esp,%0": "=r" (esp));
+
+ if (ischild)
+ res = grouped.child (esp);
+ else
+ {
+ res = grouped.parent (esp);
+ sig_send (NULL, __SIGNOHOLD);
+ }
+
+ MALLOC_CHECK;
+ if (ischild || res > 0)
+ /* everything is ok */;
+ else
+ {
+ if (!grouped.error)
+ syscall_printf ("fork failed - child pid %d, errno %d", grouped.child_pid, grouped.this_errno);
+ else
+ {
+ char buf[strlen (grouped.error) + sizeof ("child %d - , errno 4294967295 ")];
+ strcpy (buf, "child %d - ");
+ strcat (buf, grouped.error);
+ strcat (buf, ", errno %d");
+ system_printf (buf, grouped.child_pid, grouped.this_errno);
+ }
+
+ set_errno (grouped.this_errno);
+ }
+ syscall_printf ("%d = fork()", res);
+ return res;
+}
+#ifdef DEBUGGING
+void
+fork_init ()
+{
+}
+#endif /*DEBUGGING*/
+
+#ifdef NEWVFORK
+/* Dummy function to force second assignment below to actually be
+ carried out */
+static vfork_save *
+get_vfork_val ()
+{
+ return vfork_storage.val ();
+}
+#endif
+
+extern "C" int
+vfork ()
+{
+#ifndef NEWVFORK
+ debug_printf ("stub called");
+ return fork ();
+#else
+ vfork_save *vf = get_vfork_val ();
+ char **esp, **pp;
+
+ if (vf == NULL)
+ vf = vfork_storage.create ();
+ else if (vf->pid)
+ return fork ();
+
+ // FIXME the tls stuff could introduce a signal race if a child process
+ // exits quickly.
+ if (!setjmp (vf->j))
+ {
+ vf->pid = -1;
+ __asm__ volatile ("movl %%esp,%0": "=r" (vf->vfork_esp):);
+ __asm__ volatile ("movl %%ebp,%0": "=r" (vf->vfork_ebp):);
+ for (pp = (char **) vf->frame, esp = vf->vfork_esp;
+ esp <= vf->vfork_ebp + 2; pp++, esp++)
+ *pp = *esp;
+ vf->ctty = myself->ctty;
+ vf->sid = myself->sid;
+ vf->pgid = myself->pgid;
+ cygheap->ctty_on_hold = cygheap->ctty;
+ vf->console_count = cygheap->console_count;
+ debug_printf ("cygheap->ctty_on_hold %p, cygheap->console_count %d", cygheap->ctty_on_hold, cygheap->console_count);
+ int res = cygheap->fdtab.vfork_child_dup () ? 0 : -1;
+ debug_printf ("%d = vfork()", res);
+ _my_tls.call_signal_handler (); // FIXME: racy
+ vf->tls = _my_tls;
+ return res;
+ }
+
+ vf = get_vfork_val ();
+
+ for (pp = (char **) vf->frame, esp = vf->vfork_esp;
+ esp <= vf->vfork_ebp + 2; pp++, esp++)
+ *esp = *pp;
+
+ cygheap->fdtab.vfork_parent_restore ();
+
+ myself->ctty = vf->ctty;
+ myself->sid = vf->sid;
+ myself->pgid = vf->pgid;
+ termios_printf ("cygheap->ctty %p, cygheap->ctty_on_hold %p", cygheap->ctty, cygheap->ctty_on_hold);
+ cygheap->console_count = vf->console_count;
+
+ if (vf->pid < 0)
+ {
+ int exitval = vf->exitval;
+ vf->pid = 0;
+ if ((vf->pid = fork ()) == 0)
+ exit (exitval);
+ }
+
+ int pid = vf->pid;
+ vf->pid = 0;
+ debug_printf ("exiting vfork, pid %d", pid);
+ sig_dispatch_pending ();
+
+ _my_tls.call_signal_handler (); // FIXME: racy
+ _my_tls = vf->tls;
+ return pid;
+#endif
+}
+
+/* Copy memory from one process to another. */
+
+bool
+child_copy (HANDLE hp, bool write, ...)
+{
+ va_list args;
+ va_start (args, write);
+ static const char *huh[] = {"read", "write"};
+
+ char *what;
+ while ((what = va_arg (args, char *)))
+ {
+ char *low = va_arg (args, char *);
+ char *high = va_arg (args, char *);
+ DWORD todo = wincap.chunksize () ?: high - low;
+ char *here;
+
+ for (here = low; here < high; here += todo)
+ {
+ DWORD done = 0;
+ if (here + todo > high)
+ todo = high - here;
+ int res;
+ if (write)
+ res = WriteProcessMemory (hp, here, here, todo, &done);
+ else
+ res = ReadProcessMemory (hp, here, here, todo, &done);
+ debug_printf ("%s - hp %p low %p, high %p, res %d", what, hp, low, high, res);
+ if (!res || todo != done)
+ {
+ if (!res)
+ __seterrno ();
+ /* If this happens then there is a bug in our fork
+ implementation somewhere. */
+ system_printf ("%s %s copy failed, %p..%p, done %d, windows pid %u, %E",
+ what, huh[write], low, high, done);
+ goto err;
+ }
+ }
+ }
+
+ debug_printf ("done");
+ return true;
+
+ err:
+ TerminateProcess (hp, 1);
+ set_errno (EAGAIN);
+ return false;
+}
diff --git a/winsup/cygwin/hookapi.cc b/winsup/cygwin/hookapi.cc
new file mode 100644
index 00000000000..39ba6de8849
--- /dev/null
+++ b/winsup/cygwin/hookapi.cc
@@ -0,0 +1,237 @@
+/* hookapi.cc
+
+ Copyright 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 "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include <stdlib.h>
+#include <imagehlp.h>
+#include <alloca.h>
+
+#define rva(coerce, base, addr) (coerce) ((char *) (base) + (addr))
+#define rvacyg(coerce, addr) rva (coerce, cygwin_hmodule, addr)
+
+struct function_hook
+{
+ const char *name; // Function name, e.g. "DirectDrawCreateEx".
+ const void *hookfn; // Address of your function.
+ void *origfn; // Stored by HookAPICalls, the address of the original function.
+};
+
+/* Given an HMODULE, returns a pointer to the PE header */
+static PIMAGE_NT_HEADERS
+PEHeaderFromHModule (HMODULE hModule)
+{
+ PIMAGE_NT_HEADERS pNTHeader;
+
+ if (PIMAGE_DOS_HEADER(hModule) ->e_magic != IMAGE_DOS_SIGNATURE)
+ pNTHeader = NULL;
+ else
+ {
+ pNTHeader = PIMAGE_NT_HEADERS (PBYTE (hModule)
+ + PIMAGE_DOS_HEADER (hModule) ->e_lfanew);
+ if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
+ pNTHeader = NULL;
+ }
+
+ return pNTHeader;
+}
+
+static long
+rvadelta (PIMAGE_NT_HEADERS pnt, DWORD import_rva)
+{
+ PIMAGE_SECTION_HEADER section = (PIMAGE_SECTION_HEADER) (pnt + 1);
+ for (int i = 0; i < pnt->FileHeader.NumberOfSections; i++)
+ if (section[i].VirtualAddress <= import_rva
+ && (section[i].VirtualAddress + section[i].Misc.VirtualSize) > import_rva)
+ // if (strncasematch ((char *) section[i].Name, ".idata", IMAGE_SIZEOF_SHORT_NAME))
+ return section[i].VirtualAddress - section[i].PointerToRawData;
+ return -1;
+}
+
+static void *
+putmem (PIMAGE_THUNK_DATA pi, const void *hookfn)
+{
+ DWORD ofl;
+ if (!VirtualProtect (pi, sizeof (PVOID), PAGE_READWRITE, &ofl) )
+ return NULL;
+
+ void *origfn = (void *) pi->u1.Function;
+ pi->u1.Function = (DWORD) hookfn;
+
+ VirtualProtect (pi, sizeof (PVOID), ofl, &ofl);
+ return origfn;
+}
+
+/* Builds stubs for and redirects the IAT for one DLL (pImportDesc) */
+
+static bool
+RedirectIAT (function_hook& fh, PIMAGE_IMPORT_DESCRIPTOR pImportDesc,
+ HMODULE hm)
+{
+ // If no import names table, we can't redirect this, so bail
+ if (pImportDesc->OriginalFirstThunk == 0)
+ return false;
+
+ /* import address table */
+ PIMAGE_THUNK_DATA pt = rva (PIMAGE_THUNK_DATA, hm, pImportDesc->FirstThunk);
+ /* import names table */
+ PIMAGE_THUNK_DATA pn = rva (PIMAGE_THUNK_DATA, hm, pImportDesc->OriginalFirstThunk);
+
+ /* Scan through the IAT, completing the stubs and redirecting the IAT
+ entries to point to the stubs. */
+ for (PIMAGE_THUNK_DATA pi = pt; pn->u1.Ordinal; pi++, pn++)
+ {
+ if (IMAGE_SNAP_BY_ORDINAL (pn->u1.Ordinal) )
+ continue;
+
+ /* import by name */
+ PIMAGE_IMPORT_BY_NAME pimp = rva (PIMAGE_IMPORT_BY_NAME, hm, pn->u1.AddressOfData);
+
+ if (strcmp (fh.name, (char *) pimp->Name) == 0)
+ {
+ fh.origfn = putmem (pi, fh.hookfn);
+ if (!fh.origfn)
+ return false;
+ hook_chain *hc;
+ for (hc = &cygheap->hooks; hc->next; hc = hc->next)
+ continue;
+ hc->next = (hook_chain *) cmalloc (HEAP_1_HOOK, sizeof (hook_chain));
+ hc->next->loc = (void **) pi;
+ hc->next->func = fh.hookfn;
+ hc->next->next = NULL;
+ break;
+ }
+ }
+
+ return true;
+}
+
+static void
+get_export (function_hook& fh)
+{
+ PIMAGE_DOS_HEADER pdh = (PIMAGE_DOS_HEADER) cygwin_hmodule;
+ if (pdh->e_magic != IMAGE_DOS_SIGNATURE)
+ return;
+ PIMAGE_NT_HEADERS pnt = (PIMAGE_NT_HEADERS) ((char *) pdh + pdh->e_lfanew);
+ if (pnt->Signature != IMAGE_NT_SIGNATURE || pnt->FileHeader.SizeOfOptionalHeader == 0)
+ return;
+ PIMAGE_EXPORT_DIRECTORY pexp =
+ rvacyg (PIMAGE_EXPORT_DIRECTORY,
+ pnt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
+ if (!pexp)
+ return;
+
+ PDWORD pfuncs = rvacyg (PDWORD, pexp->AddressOfFunctions);
+ PDWORD pnames = rvacyg (PDWORD, pexp->AddressOfNames);
+ for (DWORD i = 0; i < pexp->NumberOfNames; i++)
+ if (strcmp (fh.name, rvacyg (char *, pnames[i])) == 0)
+ {
+ fh.origfn = rvacyg (void *, pfuncs[i]);
+ break;
+ }
+}
+
+static const char *
+makename (const char *name, char *&buf, int& i, int inc)
+{
+ i += inc;
+ static const char *testers[] = {"NOTUSED", "64", "32"};
+ if (i < 0 || i >= (int) (sizeof (testers) / sizeof (testers[0])))
+ return NULL;
+ if (i)
+ {
+ __small_sprintf (buf, "_%s%s", name, testers[i]);
+ name = buf;
+ }
+ return name;
+}
+
+// Top level routine to find the EXE's imports, and redirect them
+void *
+hook_or_detect_cygwin (const char *name, const void *fn, WORD& subsys)
+{
+ HMODULE hm = fn ? GetModuleHandle (NULL) : (HMODULE) name;
+ PIMAGE_NT_HEADERS pExeNTHdr = PEHeaderFromHModule (hm);
+
+ if (!pExeNTHdr)
+ return false;
+
+ subsys = pExeNTHdr->OptionalHeader.Subsystem;
+
+ DWORD importRVA = pExeNTHdr->OptionalHeader.DataDirectory
+ [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
+ if (!importRVA)
+ return false;
+
+ long delta = fn ? 0 : rvadelta (pExeNTHdr, importRVA);
+ if (delta < 0)
+ return false;
+
+ // Convert imports RVA to a usable pointer
+ PIMAGE_IMPORT_DESCRIPTOR pdfirst = rva (PIMAGE_IMPORT_DESCRIPTOR, hm, importRVA - delta);
+
+ function_hook fh;
+ fh.origfn = NULL;
+ fh.hookfn = fn;
+ char *buf = (char *) alloca (strlen (name) + sizeof ("_64"));
+ int i;
+ // Iterate through each import descriptor, and redirect if appropriate
+ for (PIMAGE_IMPORT_DESCRIPTOR pd = pdfirst; pd->FirstThunk; pd++)
+ {
+ if (!strcasematch (rva (PSTR, hm, pd->Name - delta), "cygwin1.dll"))
+ continue;
+ if (!fn)
+ return (void *) "found it"; // just checking if executable used cygwin1.dll
+ i = -1;
+ while (!fh.origfn && (fh.name = makename (name, buf, i, 1)))
+ RedirectIAT (fh, pd, hm);
+ if (fh.origfn)
+ break;
+ }
+
+ while (!fh.origfn && (fh.name = makename (name, buf, i, -1)))
+ get_export (fh);
+
+ return fh.origfn;
+}
+
+void
+ld_preload ()
+{
+ char *p = getenv ("LD_PRELOAD");
+ if (!p)
+ return;
+ char *s = (char *) alloca (strlen (p) + 1);
+ strcpy (s, p);
+ char *here = NULL;
+ for (p = strtok_r (s, ":\t\n", &here); p; p = strtok_r (NULL, ":\t\n", &here))
+ {
+ path_conv lib (p);
+ if (!LoadLibrary (lib))
+ {
+ __seterrno ();
+ api_fatal ("error while loading shared libraries: %s: "
+ "cannot open shared object file: %s", p,
+ strerror (get_errno ()));
+ }
+ }
+}
+
+void
+fixup_hooks_after_fork ()
+{
+ for (hook_chain *hc = &cygheap->hooks; (hc = hc->next); )
+ putmem ((PIMAGE_THUNK_DATA) hc->loc, hc->func);
+}