OSDN Git Service

* dcrt0.cc (_dll_crt0): Fix formatting.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / dcrt0.cc
index b539e4b..8b69939 100644 (file)
@@ -1,8 +1,7 @@
 /* dcrt0.cc -- essentially the main() for the Cygwin dll
 
    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
-   2007, 2008, 2009, 2010
-   Red Hat, Inc.
+   2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
 
 This file is part of Cygwin.
 
@@ -36,7 +35,9 @@ details. */
 #include "tls_pbuf.h"
 #include "exception.h"
 #include "cygxdr.h"
+#include "fenv.h"
 #include "ntdll.h"
+#include "wow64.h"
 
 #define MAX_AT_FILE_LEVEL 10
 
@@ -48,8 +49,6 @@ extern "C" void __sinit (_reent *);
 static int NO_COPY envc;
 static char NO_COPY **envp;
 
-static char title_buf[TITLESIZE + 1];
-
 bool NO_COPY jit_debug;
 
 static void
@@ -97,12 +96,12 @@ insert_file (char *name, char *&cmd)
   PWCHAR wname = tp.w_get ();
   sys_mbstowcs (wname, NT_MAX_PATH, name + 1);
   f = CreateFileW (wname,
-                  GENERIC_READ,         /* open for reading    */
-                  FILE_SHARE_READ,      /* share for reading   */
-                  &sec_none_nih,        /* default security    */
-                  OPEN_EXISTING,        /* existing file only  */
-                  FILE_ATTRIBUTE_NORMAL,/* normal file         */
-                  NULL);                /* no attr. template   */
+                  GENERIC_READ,                /* open for reading     */
+                  FILE_SHARE_VALID_FLAGS,      /* share for reading    */
+                  &sec_none_nih,               /* default security     */
+                  OPEN_EXISTING,               /* existing file only   */
+                  FILE_ATTRIBUTE_NORMAL,       /* normal file          */
+                  NULL);                       /* no attr. template    */
 
   if (f == INVALID_HANDLE_VALUE)
     {
@@ -239,7 +238,7 @@ globify (char *word, char **&argv, int &argc, int &argvlen)
            else
              {
                --s;
-               while (cnt-- > 0)
+               while (cnt-- > 0)
                  *p++ = *++s;
              }
          }
@@ -387,57 +386,48 @@ check_sanity_and_sync (per_process *p)
 
 child_info NO_COPY *child_proc_info = NULL;
 
-#define CYGWIN_GUARD (PAGE_EXECUTE_READWRITE | PAGE_GUARD)
+#define CYGWIN_GUARD (PAGE_READWRITE | PAGE_GUARD)
 
 void
 child_info_fork::alloc_stack_hard_way (volatile char *b)
 {
-  void *new_stack_pointer;
-  MEMORY_BASIC_INFORMATION m;
-  void *newbase;
-  int newlen;
-  bool guard;
-
-  if (!VirtualQuery ((LPCVOID) &b, &m, sizeof m))
-    api_fatal ("fork: couldn't get stack info, %E");
-
-  LPBYTE curbot = (LPBYTE) m.BaseAddress + m.RegionSize;
-
-  if (stacktop > (LPBYTE) m.AllocationBase && stacktop < curbot)
-    {
-      newbase = curbot;
-      newlen = (LPBYTE) stackbottom - (LPBYTE) curbot;
-      guard = false;
-    }
-  else
-    {
-      newbase = (LPBYTE) stacktop - (128 * 1024);
-      newlen = (LPBYTE) stackbottom - (LPBYTE) newbase;
-      guard = true;
-    }
+  void *stack_ptr;
+  DWORD stacksize;
+
+  /* First check if the requested stack area is part of the user heap
+     or part of a mmapped region.  If so, we have been started from a
+     pthread with an application-provided stack, and the stack has just
+     to be used as is. */
+  if ((stacktop >= cygheap->user_heap.base
+      && stackbottom <= cygheap->user_heap.max)
+      || is_mmapped_region ((caddr_t) stacktop, (caddr_t) stackbottom))
+    return;
 
-  if (!VirtualAlloc (newbase, newlen, MEM_RESERVE, PAGE_NOACCESS))
+  /* First, try to reserve the entire stack. */
+  stacksize = (char *) stackbottom - (char *) stackaddr;
+  if (!VirtualAlloc (stackaddr, stacksize, MEM_RESERVE, PAGE_NOACCESS))
     api_fatal ("fork: can't reserve memory for stack %p - %p, %E",
-               stacktop, stackbottom);
-  new_stack_pointer = (void *) ((LPBYTE) stackbottom - (stacksize += 8192));
-  if (!VirtualAlloc (new_stack_pointer, stacksize, MEM_COMMIT,
-                    PAGE_EXECUTE_READWRITE))
-    api_fatal ("fork: can't commit memory for stack %p(%d), %E",
-              new_stack_pointer, stacksize);
-  if (!VirtualQuery ((LPCVOID) new_stack_pointer, &m, sizeof m))
-    api_fatal ("fork: couldn't get new stack info, %E");
-
-  if (guard)
+              stackaddr, stackbottom);
+  stacksize = (char *) stackbottom - (char *) stacktop;
+  stack_ptr = VirtualAlloc (stacktop, stacksize, MEM_COMMIT, PAGE_READWRITE);
+  if (!stack_ptr)
+    abort ("can't commit memory for stack %p(%d), %E", stacktop, stacksize);
+  if (guardsize != (size_t) -1)
     {
-      m.BaseAddress = (LPBYTE) m.BaseAddress - 1;
-      if (!VirtualAlloc ((LPVOID) m.BaseAddress, 1, MEM_COMMIT,
-                        CYGWIN_GUARD))
-       api_fatal ("fork: couldn't allocate new stack guard page %p, %E",
-                  m.BaseAddress);
+      /* Allocate PAGE_GUARD page if it still fits. */
+      if (stack_ptr > stackaddr)
+       {
+         stack_ptr = (void *) ((LPBYTE) stack_ptr
+                                       - wincap.page_size ());
+         if (!VirtualAlloc (stack_ptr, wincap.page_size (), MEM_COMMIT,
+                            CYGWIN_GUARD))
+           api_fatal ("fork: couldn't allocate new stack guard page %p, %E",
+                      stack_ptr);
+       }
+      /* Allocate POSIX guard pages. */
+      if (guardsize > 0)
+       VirtualAlloc (stackaddr, guardsize, MEM_COMMIT, PAGE_NOACCESS);
     }
-  if (!VirtualQuery ((LPCVOID) m.BaseAddress, &m, sizeof m))
-    api_fatal ("fork: couldn't get new stack info, %E");
-  stacktop = m.BaseAddress;
   b[0] = '\0';
 }
 
@@ -457,14 +447,29 @@ child_info_fork::alloc_stack ()
 {
   volatile char * volatile esp;
   __asm__ volatile ("movl %%esp,%0": "=r" (esp));
-  if (_tlsbase != stackbottom)
+  /* Make sure not to try a hard allocation if we have been forked off from
+     the main thread of a Cygwin process which has been started from a 64 bit
+     parent.  In that case the _tlsbase of the forked child is not the same
+     as the _tlsbase of the parent (== stackbottom), but only because the
+     stack of the parent has been slightly rearranged.  See comment in
+     wow64_revert_to_original_stack for details. We check here if the
+     parent stack fits into the child stack. */
+  if (_tlsbase != stackbottom
+      && (!wincap.is_wow64 ()
+         || stacktop < (char *) NtCurrentTeb ()->DeallocationStack
+         || stackbottom > _tlsbase))
     alloc_stack_hard_way (esp);
   else
     {
       char *st = (char *) stacktop - 4096;
       while (_tlstop >= st)
        esp = getstack (esp);
-      stacksize = 0;
+      stackaddr = 0;
+      /* This only affects forked children of a process started from a native
+         64 bit process, but it doesn't hurt to do it unconditionally.  Fix
+        StackBase in the child to be the same as in the parent, so that the
+        computation of _my_tls is correct. */
+      _tlsbase = (char *) stackbottom;
     }
 }
 
@@ -484,17 +489,8 @@ initial_env ()
     _cygwin_testing = 1;
 
 #ifdef DEBUGGING
-  char buf[NT_MAX_PATH];
   DWORD len;
-
-  if (GetEnvironmentVariableA ("CYGWIN_SLEEP", buf, sizeof (buf) - 1))
-    {
-      DWORD ms = atoi (buf);
-      console_printf ("Sleeping %d, pid %u %P\n", ms, GetCurrentProcessId ());
-      Sleep (ms);
-      if (!strace.active () && !dynamically_loaded)
-       strace.hello ();
-    }
+  char buf[NT_MAX_PATH];
   if (GetEnvironmentVariableA ("CYGWIN_DEBUG", buf, sizeof (buf) - 1))
     {
       char buf1[NT_MAX_PATH];
@@ -516,7 +512,6 @@ initial_env ()
        }
     }
 #endif
-
 }
 
 child_info *
@@ -529,7 +524,10 @@ get_cygwin_startup_info ()
 
   if (si.cbReserved2 < EXEC_MAGIC_SIZE || !res
       || res->intro != PROC_MAGIC_GENERIC || res->magic != CHILD_INFO_MAGIC)
-    res = NULL;
+    {
+      strace.activate (false);
+      res = NULL;
+    }
   else
     {
       if ((res->intro & OPROC_MAGIC_MASK) == OPROC_MAGIC_GENERIC)
@@ -541,12 +539,12 @@ get_cygwin_startup_info ()
       unsigned should_be_cb = 0;
       switch (res->type)
        {
-         case _PROC_FORK:
+         case _CH_FORK:
            in_forkee = true;
            should_be_cb = sizeof (child_info_fork);
            /* fall through */;
-         case _PROC_SPAWN:
-         case _PROC_EXEC:
+         case _CH_SPAWN:
+         case _CH_EXEC:
            if (!should_be_cb)
              should_be_cb = sizeof (child_info_spawn);
            if (should_be_cb != res->cb)
@@ -555,16 +553,15 @@ get_cygwin_startup_info ()
              multiple_cygwin_problem ("fhandler size", res->fhandler_union_cb, sizeof (fhandler_union));
            if (res->isstraced ())
              {
-               res->ready (false);
-               for (unsigned i = 0; !being_debugged () && i < 10000; i++)
+               while (!being_debugged ())
                  yield ();
-               strace.hello ();
+               strace.activate (res->type == _CH_FORK);
              }
            break;
          default:
            system_printf ("unknown exec type %d", res->type);
            /* intentionally fall through */
-         case _PROC_WHOOPS:
+         case _CH_WHOOPS:
            res = NULL;
            break;
        }
@@ -634,6 +631,12 @@ child_info_spawn::handle_spawn ()
     cygheap->fdtab.move_fd (__stdout, 1);
   cygheap->user.groups.clear_supp ();
 
+  /* If we're execing we may have "inherited" a list of children forked by the
+     previous process executing under this pid.  Reattach them here so that we
+     can wait for them.  */
+  if (type == _CH_EXEC)
+    reattach_children ();
+
   ready (true);
 
   /* Need to do this after debug_fixup_after_fork_exec or DEBUGGING handling of
@@ -642,60 +645,37 @@ child_info_spawn::handle_spawn ()
   child_proc_info->parent = NULL;
 
   signal_fixup_after_exec ();
-  if (moreinfo->old_title)
-    {
-      old_title = strcpy (title_buf, moreinfo->old_title);
-      cfree (moreinfo->old_title);
-    }
   fixup_lockf_after_exec ();
 }
 
-#if 0
-/* Setting the TS-aware flag in the application's PE header is sufficient.
-   Just keep this in as a reminder. */
-
-static DEP_SYSTEM_POLICY_TYPE dep_system_policy = (DEP_SYSTEM_POLICY_TYPE) -1;
-
-static void
-disable_dep ()
-{
-  DWORD ppolicy;
-  BOOL perm;
-
-  if (dep_system_policy < 0)
-    {
-      dep_system_policy = GetSystemDEPPolicy ();
-      debug_printf ("DEP System Policy: %d", (int) dep_system_policy);
-    }
-  if (dep_system_policy < OptIn)
-    return;
-  if (!GetProcessDEPPolicy (GetCurrentProcess (), &ppolicy, &perm))
-    {
-      debug_printf ("GetProcessDEPPolicy: %E");
-      return;
-    }
-  debug_printf ("DEP Process Policy: %d (permanent = %d)", ppolicy, perm);
-  if (ppolicy > 0 && !perm && !SetProcessDEPPolicy (0))
-    debug_printf ("SetProcessDEPPolicy: %E");
-}
-#endif
-
-/* Retrieve and store system directory for later use.  Note that the 
+/* Retrieve and store system directory for later use.  Note that the
    directory is stored with a trailing backslash! */
 static void
 init_windows_system_directory ()
 {
-  windows_system_directory_length =
-       GetSystemDirectoryW (windows_system_directory, MAX_PATH);
-  if (windows_system_directory_length == 0)
-    api_fatal ("can't find windows system directory");
-  windows_system_directory[windows_system_directory_length++] = L'\\';
-  windows_system_directory[windows_system_directory_length] = L'\0';
+  if (!windows_system_directory_length)
+    {
+      windows_system_directory_length =
+           GetSystemDirectoryW (windows_system_directory, MAX_PATH);
+      if (windows_system_directory_length == 0)
+       api_fatal ("can't find windows system directory");
+      windows_system_directory[windows_system_directory_length++] = L'\\';
+      windows_system_directory[windows_system_directory_length] = L'\0';
+
+      system_wow64_directory_length =
+       GetSystemWow64DirectoryW (system_wow64_directory, MAX_PATH);
+      if (system_wow64_directory_length)
+       {
+         system_wow64_directory[system_wow64_directory_length++] = L'\\';
+         system_wow64_directory[system_wow64_directory_length] = L'\0';
+       }
+    }
 }
 
-void __stdcall
+void
 dll_crt0_0 ()
 {
+  child_proc_info = get_cygwin_startup_info ();
   init_windows_system_directory ();
   init_global_security ();
   initial_env ();
@@ -715,26 +695,32 @@ dll_crt0_0 ()
                   GetCurrentProcess (), &hMainThread,
                   0, false, DUPLICATE_SAME_ACCESS);
 
-  OpenProcessToken (GetCurrentProcess (), MAXIMUM_ALLOWED, &hProcToken);
+  NtOpenProcessToken (NtCurrentProcess (), MAXIMUM_ALLOWED, &hProcToken);
   set_cygwin_privileges (hProcToken);
 
   device::init ();
   do_global_ctors (&__CTOR_LIST__, 1);
   cygthread::init ();
 
-  child_proc_info = get_cygwin_startup_info ();
   if (!child_proc_info)
-    memory_init (true);
+    {
+      memory_init (true);
+      /* WOW64 process?  Check if we have been started from 64 bit process
+         and if our stack is at an unusual address.  Set wow64_has_64bit_parent
+        if so.  Problem description in wow64_test_for_64bit_parent. */
+      if (wincap.is_wow64 ())
+       wow64_has_64bit_parent = wow64_test_for_64bit_parent ();
+    }
   else
     {
       cygwin_user_h = child_proc_info->user_h;
       switch (child_proc_info->type)
        {
-         case _PROC_FORK:
+         case _CH_FORK:
            fork_info->handle_fork ();
            break;
-         case _PROC_SPAWN:
-         case _PROC_EXEC:
+         case _CH_SPAWN:
+         case _CH_EXEC:
            spawn_info->handle_spawn ();
            break;
        }
@@ -748,28 +734,7 @@ dll_crt0_0 ()
   events_init ();
   tty_list::init_session ();
 
-#if 0
-  /* Setting the TS-aware flag in the application's PE header is sufficient.
-     Just keep this in as a reminder. */
-
-  /* The disable_dep function disables DEP for all Cygwin processes if
-     the process runs on a Windows Server 2008 with Terminal Services
-     installed.  This combination (TS+DEP) breaks *some* Cygwin
-     applications.  The Terminal Service specific DLL tsappcmp.dll
-     changes the page protection of some pages in the application's text
-     segment from PAGE_EXECUTE_WRITECOPY to PAGE_WRITECOPY for no
-     apparent reason.  This occurs before any Cygwin or applicaton code
-     had a chance to run.  MS has no explanation for this so far, but is
-     rather busy trying to avoid giving support for this problem (as of
-     2008-11-11).
-
-     Unfortunately disabling DEP seems to have a not negligible
-     performance hit.  In the long run, either MS has to fix their
-     problem, or we have to find a better workaround, if any exists.
-     Idle idea: Adding EXECUTE protection to all text segment pages? */
-  if (wincap.ts_has_dep_problem ())
-    disable_dep ();
-#endif
+  _main_tls = &_my_tls;
 
   /* Initialize signal processing here, early, in the hopes that the creation
      of a thread early in the process will cause more predictability in memory
@@ -791,6 +756,7 @@ dll_crt0_1 (void *)
 
   if (dynamically_loaded)
     sigproc_init ();
+
   check_sanity_and_sync (user_data);
 
   /* Initialize malloc and then call user_shared_initialize since it relies
@@ -800,11 +766,20 @@ dll_crt0_1 (void *)
   malloc_init ();
   user_shared->initialize ();
 
-#ifdef CGF
+#ifdef CYGHEAP_DEBUG
   int i = 0;
   const int n = 2 * 1024 * 1024;
   while (i--)
-    small_printf ("cmalloc returns %p\n", cmalloc (HEAP_STR, n));
+    {
+      void *p = cmalloc (HEAP_STR, n);
+      if (p)
+       small_printf ("cmalloc returns %p\n", cmalloc (HEAP_STR, n));
+      else
+       {
+         small_printf ("total allocated %p\n", (i - 1) * n);
+         break;
+       }
+    }
 #endif
 
   ProtectHandle (hMainThread);
@@ -844,7 +819,7 @@ dll_crt0_1 (void *)
 
         NOTE: Don't do anything that involves the stack until you've completed
         this step. */
-      if (fork_info->stacksize)
+      if (fork_info->stackaddr)
        {
          _tlsbase = (char *) fork_info->stackbottom;
          _tlstop = (char *) fork_info->stacktop;
@@ -853,6 +828,8 @@ dll_crt0_1 (void *)
       longjmp (fork_info->jmp, true);
     }
 
+  __sinit (_impure_ptr);
+
 #ifdef DEBUGGING
   {
   extern void fork_init ();
@@ -860,9 +837,7 @@ dll_crt0_1 (void *)
   }
 #endif
   pinfo_init (envp, envc);
-
-  if (!old_title && GetConsoleTitle (title_buf, TITLESIZE))
-    old_title = title_buf;
+  strace.dll_info ();
 
   /* Allocate cygheap->fdtab */
   dtable_init ();
@@ -911,6 +886,8 @@ dll_crt0_1 (void *)
     ++__progname;
   else
     __progname = __argv[0];
+  program_invocation_name = __argv[0];
+  program_invocation_short_name = __progname;
   if (__progname)
     {
       char *cp = strchr (__progname, '\0') - 4;
@@ -918,18 +895,6 @@ dll_crt0_1 (void *)
        *cp = '\0';
     }
 
-  /* Set new console title if appropriate. */
-
-  if (display_title && !dynamically_loaded)
-    {
-      char *cp = __progname;
-      if (strip_title_path)
-       for (char *ptr = cp; *ptr && *ptr != ' '; ptr++)
-         if (isdirsep (*ptr))
-           cp = ptr + 1;
-      set_console_title (cp);
-    }
-
   (void) xdr_set_vprintf (&cygxdr_vwarnx);
   cygwin_finished_initializing = true;
   /* Call init of loaded dlls. */
@@ -943,7 +908,10 @@ dll_crt0_1 (void *)
   set_errno (0);
 
   if (dynamically_loaded)
-    return;
+    {
+      _setlocale_r (_REENT, LC_CTYPE, "C");
+      return;
+    }
 
   /* Disable case-insensitive globbing */
   ignore_case_with_glob = false;
@@ -956,7 +924,18 @@ dll_crt0_1 (void *)
   _setlocale_r (_REENT, LC_CTYPE, "C");
 
   if (user_data->main)
-    cygwin_exit (user_data->main (__argc, __argv, *user_data->envptr));
+    {
+      /* Create a copy of Cygwin's version of __argv so that, if the user makes
+        a change to an element of argv[] it does not affect Cygwin's argv.
+        Changing the the contents of what argv[n] points to will still
+        affect Cygwin.  This is similar (but not exactly like) Linux. */
+      char *newargv[__argc + 1];
+      char **nav = newargv;
+      char **oav = __argv;
+      while ((*nav++ = *oav++) != NULL)
+       continue;
+      cygwin_exit (user_data->main (__argc, newargv, *user_data->envptr));
+    }
   __asm__ ("                           \n\
        .global __cygwin_exit_return    \n\
 __cygwin_exit_return:                  \n\
@@ -966,17 +945,42 @@ __cygwin_exit_return:                     \n\
 extern "C" void __stdcall
 _dll_crt0 ()
 {
+  /* Handle WOW64 process started from native 64 bit process.  See comment
+     in wow64_test_for_64bit_parent for a full problem description. */
+  if (wow64_has_64bit_parent && !dynamically_loaded)
+    {
+      /* Must be static since it's referenced after the stack pointers have
+        been moved. */
+      static PVOID allocationbase = 0;
+
+      /* Check if we just move the stack.  See comment in
+        wow64_revert_to_original_stack for the gory details. */
+      PVOID stackaddr = wow64_revert_to_original_stack (allocationbase);
+      if (stackaddr)
+       {
+         /* 2nd half of the stack move.  Set stack pointers to new address. */
+         __asm__ ("\n\
+                  movl  %[ADDR], %%esp \n\
+                  movl  %%esp, %%ebp   \n"
+                  : : [ADDR] "r" (stackaddr));
+         /* Now we're back on the original stack.  Free up space taken by the
+            former main thread stack and set DeallocationStack correctly. */
+         VirtualFree (NtCurrentTeb ()->DeallocationStack, 0, MEM_RELEASE);
+         NtCurrentTeb ()->DeallocationStack = allocationbase;
+       }
+      else
+       /* Fall back to respawn if wow64_revert_to_original_stack fails. */
+       wow64_respawn_process ();
+    }
+#ifdef __i386__
+  _feinitialise ();
+#endif
   main_environ = user_data->envptr;
   if (in_forkee)
     {
       fork_info->alloc_stack ();
       _main_tls = &_my_tls;
     }
-  else
-    {
-      _main_tls = &_my_tls;
-      __sinit (_impure_ptr);
-    }
 
   _main_tls->call ((DWORD (*) (void *, void *)) dll_crt0_1, NULL);
 }
@@ -1023,12 +1027,14 @@ __main (void)
      queued call to DLL dtors now.  */
   atexit (dll_global_dtors);
   do_global_ctors (user_data->ctors, false);
-  /* Now we have run global ctors, register their dtors.  */
-  atexit (do_global_dtors);
-  /* At exit, global dtors will run first, so the app can still
+  /* Now we have run global ctors, register their dtors.
+
+     At exit, global dtors will run first, so the app can still
      use shared library functions while terminating; then the
      DLLs will be destroyed; finally newlib will shut down stdio
      and terminate itself.  */
+  atexit (do_global_dtors);
+  sig_dispatch_pending (true);
 }
 
 void __stdcall
@@ -1053,13 +1059,6 @@ do_exit (int status)
       events_terminate ();
     }
 
-  UINT n = (UINT) status;
-  if (exit_state < ES_THREADTERM)
-    {
-      exit_state = ES_THREADTERM;
-      cygthread::terminate ();
-    }
-
   if (exit_state < ES_SIGNAL)
     {
       exit_state = ES_SIGNAL;
@@ -1075,6 +1074,13 @@ do_exit (int status)
       close_all_files ();
     }
 
+  UINT n = (UINT) status;
+  if (exit_state < ES_THREADTERM)
+    {
+      exit_state = ES_THREADTERM;
+      cygthread::terminate ();
+    }
+
   myself->stopsig = 0;
 
   if (exit_state < ES_HUP_PGRP)
@@ -1109,20 +1115,6 @@ do_exit (int status)
 
     }
 
-  if (exit_state < ES_TITLE)
-    {
-      exit_state = ES_TITLE;
-      /* restore console title */
-      if (old_title && display_title)
-       set_console_title (old_title);
-    }
-
-  if (exit_state < ES_TTY_TERMINATE)
-    {
-      exit_state = ES_TTY_TERMINATE;
-      cygwin_shared->tty.terminate ();
-    }
-
   myself.exit (n);
 }
 
@@ -1151,13 +1143,10 @@ _exit (int n)
 extern "C" void cygwin_stackdump ();
 
 extern "C" void
-__api_fatal (const char *fmt, ...)
+vapi_fatal (const char *fmt, va_list ap)
 {
   char buf[4096];
-  va_list ap;
-
-  va_start (ap, fmt);
-  int n = __small_sprintf (buf, "%P: *** fatal error - ");
+  int n = __small_sprintf (buf, "%P: *** fatal error %s- ", in_forkee ? "in forked process " : "");
   __small_vsprintf (buf + n, fmt, ap);
   va_end (ap);
   strace.prntf (_STRACE_SYSTEM, NULL, "%s", buf);
@@ -1169,12 +1158,21 @@ __api_fatal (const char *fmt, ...)
   myself.exit (__api_fatal_exit_val);
 }
 
+extern "C" void
+api_fatal (const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  vapi_fatal (fmt, ap);
+}
+
 void
 multiple_cygwin_problem (const char *what, unsigned magic_version, unsigned version)
 {
   if (_cygwin_testing && (strstr (what, "proc") || strstr (what, "cygheap")))
     {
-      child_proc_info->type = _PROC_WHOOPS;
+      child_proc_info->type = _CH_WHOOPS;
       return;
     }