Sat Apr 22 14:48:03 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
authorRoland McGrath <roland@gnu.org>
Mon, 24 Apr 1995 09:00:07 +0000 (09:00 +0000)
committerRoland McGrath <roland@gnu.org>
Mon, 24 Apr 1995 09:00:07 +0000 (09:00 +0000)
* mach/Machrules [interface-library]
  (LDFLAGS-$(interface-library:lib%=%.so)): New variable, set to
  -nostdlib.

* sysdeps/mach/hurd/fork.c: Subfunction `unlockss' removed.  Lock
  _hurd_siglock only around initial task creation in parent.  Use
  _hurd_critical_section_unlock at end.  Handle dead name rights
  properly, and deal with a send right dying while we try to copy
  it.  For the time being, use assert_perror for kernel and proc RPC
  failures.

Fri Apr 21 01:10:15 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>

* extra-lib.mk: Don't include $(o-iterator); construct it by hand
  using $(object-suffixes-$(lib)) instead.

* sysdeps/mach/hurd/Makefile (LDLIBS-c.so): New variable.

* Makerules (lib%.so: lib%_pic.a): Pass $(LDFLAGS-$(notdir $*).so).
(LDFLAGS-c.so): New variable.

* resolv/res_init.c (res_init): Don't clobber _res.options with
  RES_DEFAULT.  If RES_INIT is clear, OR in RES_DEFAULT.

* hurd/hurd.h: Declare _hurd_startup.
* hurd/hurdstartup.c: New file.
* hurd/Makefile (routines): Add hurdstartup.

ChangeLog
Makerules
extra-lib.mk
hurd/Makefile
hurd/hurd.h
hurd/hurdstartup.c [new file with mode: 0644]
mach/Machrules
resolv/res_init.c
sysdeps/mach/hurd/Makefile
sysdeps/mach/hurd/fork.c

index 193ca76..d33d7af 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+Sat Apr 22 14:48:03 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>
+
+       * mach/Machrules [interface-library]
+       (LDFLAGS-$(interface-library:lib%=%.so)): New variable, set to
+       -nostdlib.
+
+       * sysdeps/mach/hurd/fork.c: Subfunction `unlockss' removed.  Lock
+       _hurd_siglock only around initial task creation in parent.  Use
+       _hurd_critical_section_unlock at end.  Handle dead name rights
+       properly, and deal with a send right dying while we try to copy
+       it.  For the time being, use assert_perror for kernel and proc RPC
+       failures.
+
+Fri Apr 21 01:10:15 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>
+
+       * extra-lib.mk: Don't include $(o-iterator); construct it by hand
+       using $(object-suffixes-$(lib)) instead.
+
+       * sysdeps/mach/hurd/Makefile (LDLIBS-c.so): New variable.
+
+       * Makerules (lib%.so: lib%_pic.a): Pass $(LDFLAGS-$(notdir $*).so).
+       (LDFLAGS-c.so): New variable.
+
+       * resolv/res_init.c (res_init): Don't clobber _res.options with
+       RES_DEFAULT.  If RES_INIT is clear, OR in RES_DEFAULT.
+
+       * hurd/hurd.h: Declare _hurd_startup.
+       * hurd/hurdstartup.c: New file.
+       * hurd/Makefile (routines): Add hurdstartup.
+
 Thu Apr 20 22:23:58 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>
 
        * hurd/hurdsig.c: Use assert_perror for many calls which should
index 64282fb..f19a9f7 100644 (file)
--- a/Makerules
+++ b/Makerules
@@ -342,8 +342,14 @@ ifeq (yes,$(build-shared))
 # on other shared objects.
 lib%.so: lib%_pic.a
        $(LINK.o) -shared -o $@ -Wl,--whole-archive $< \
+                 $(LDFLAGS-$(notdir $*).so) \
                  -L$(firstword $(objdir) .)  -L$(common-objpfx:%/=%) \
                  $(LDLIBS-$(notdir $*).so)
+
+# Don't try to use -lc when making libc.so itself.
+# Also omits crti.o and crtn.o, which we do not want
+# since we define our own `.init' section specially.
+LDFLAGS-c.so = -nostdlib
 endif
 
 libobjs: $(foreach o,$(object-suffixes),\
index d4d2b52..c62450a 100644 (file)
@@ -33,4 +33,4 @@ $(objpfx)$(patsubst %,$(libtype$o),$(lib:lib%=%)): \
   $($(lib)-routines:%=$(objpfx)%$o); $$(build-extra-lib)
 endef
 object-suffixes-left = $(object-suffixes-$(lib))
-include $(o-iterator)
+include $(patsubst %,$(..)o-iterator.mk,$(object-suffixes-$(lib)))
index 254e89c..0ef626f 100644 (file)
@@ -39,7 +39,8 @@ user-interfaces               := $(addprefix hurd/,\
                                       fs fsys io term socket ifsock)
 server-interfaces      := hurd/msg
 
-routines = hurdinit hurdid hurdlookup hurdpid hurdrlimit hurdprio hurdexec \
+routines = hurdstartup hurdinit \
+          hurdid hurdlookup hurdpid hurdrlimit hurdprio hurdexec \
           setauth \
           pid2task task2pid \
           getuids setuids getumask fchroot \
index 968910f..77b8acf 100644 (file)
@@ -227,6 +227,23 @@ extern error_t _hurd_exec (task_t task,
 extern void _hurd_exit (int status) __attribute__ ((noreturn));
 
 
+/* Initialize Mach RPCs and essential Hurd things (_hurd_preinit_hook); do
+   initial handshake with the exec server (or extract the arguments from
+   the stack in the case of the bootstrap task); if cthreads is in use,
+   initialize it now and switch the calling thread to a cthread stack;
+   finally, call *MAIN with the information gleaned.  That function is not
+   expected to return.  ARGPTR should be the address of the first argument
+   of the entry point function that is called with the stack exactly as the
+   exec server or kernel sets it.  */
+
+extern void _hurd_startup (void **argptr,
+                          void (*main) (int argc, char **argv, char **envp,
+                                        mach_port_t *portarray,
+                                        mach_msg_type_number_t portarraysize,
+                                        int *intarray,
+                                        mach_msg_type_number_t intarraysize))
+     __attribute__ ((noreturn));
+
 /* Initialize the library data structures from the
    ints and ports passed to us by the exec server.
    Then vm_deallocate PORTARRAY and INTARRAY.  */
diff --git a/hurd/hurdstartup.c b/hurd/hurdstartup.c
new file mode 100644 (file)
index 0000000..1612859
--- /dev/null
@@ -0,0 +1,302 @@
+/* Initial program startup for running under the GNU Hurd.
+Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <hurd.h>
+#include <hurd/exec.h>
+#include <sysdep.h>
+#include <hurd/threadvar.h>
+#include <unistd.h>
+#include "set-hooks.h"
+#include "hurdmalloc.h"                /* XXX */
+
+mach_port_t *_hurd_init_dtable;
+mach_msg_type_number_t _hurd_init_dtablesize;
+
+unsigned int __hurd_threadvar_max;
+unsigned long int __hurd_threadvar_stack_mask;
+unsigned long int __hurd_threadvar_stack_offset;
+
+/* These are set up by _hurdsig_init.  */
+unsigned long int __hurd_sigthread_stack_base;
+unsigned long int __hurd_sigthread_stack_end;
+unsigned long int *__hurd_sigthread_variables;
+
+vm_address_t _hurd_stack_base;
+vm_size_t _hurd_stack_size;
+
+/* Things that want to be run before _hurd_init or much anything else.
+   Importantly, these are called before anything tries to use malloc.  */
+DEFINE_HOOK (_hurd_preinit_hook, (void));
+
+extern void __mach_init (void);
+extern void __libc_init (int argc, char **argv, char **envp);
+
+void *(*_cthread_init_routine) (void); /* Returns new SP to use.  */
+void (*_cthread_exit_routine) (int status) __attribute__ ((__noreturn__));
+
+int _hurd_split_args (char *, size_t, char **);
+
+/* These communicate values from _hurd_startup to start1,
+   where we cannot use the stack for anything.  */
+struct info
+  {
+    char *args, *env;
+    mach_port_t *portarray;
+    int *intarray;
+    mach_msg_type_number_t argslen, envlen, portarraysize, intarraysize;
+    int flags;
+    char **argv, **envp;
+    int argc;
+    void (*hurd_main) (int, char **, char **,
+                      mach_port_t *, mach_msg_type_number_t,
+                      int *, mach_msg_type_number_t);
+  };
+
+static void start1 (struct info *) __attribute__ ((__noreturn__));
+
+
+/* Entry point.  This is the first thing in the text segment.
+
+   The exec server started the initial thread in our task with this spot the
+   PC, and a stack that is presumably big enough.  We do basic Mach
+   initialization so mig-generated stubs work, and then do an exec_startup
+   RPC on our bootstrap port, to which the exec server responds with the
+   information passed in the exec call, as well as our original bootstrap
+   port, and the base address and size of the preallocated stack.
+
+   If using cthreads, we are given a new stack by cthreads initialization and
+   deallocate the stack set up by the exec server.  On the new stack we call
+   `start1' (above) to do the rest of the startup work.  Since the stack may
+   disappear out from under us in a machine-dependent way, we use a pile of
+   static variables to communicate the information from exec_startup to start1.
+   This is unfortunate but preferable to machine-dependent frobnication to copy
+   the state from the old stack to the new one.  */
+
+void
+_hurd_startup (void **argptr,
+              void (*main) (int, char **, char **,
+                            mach_port_t *, mach_msg_type_number_t,
+                            int *, mach_msg_type_number_t))
+{
+  error_t err;
+  mach_port_t in_bootstrap;
+  struct info i;
+
+  /* Basic Mach initialization, must be done before RPCs can be done.  */
+  __mach_init ();
+
+  /* Run things that want to do initialization as soon as possible.  We do
+     this before exec_startup so that no out of line data arrives and
+     clutters up the address space before brk initialization.  */
+
+  RUN_HOOK (_hurd_preinit_hook, ());
+
+  if (err = __task_get_special_port (__mach_task_self (), TASK_BOOTSTRAP_PORT,
+                                    &in_bootstrap))
+    LOSE;
+
+  if (in_bootstrap != MACH_PORT_NULL)
+    {
+      /* Call the exec server on our bootstrap port and
+        get all our standard information from it.  */
+
+      i.argslen = i.envlen = 0;
+      _hurd_init_dtablesize = i.portarraysize = i.intarraysize = 0;
+
+      err = __exec_startup (in_bootstrap,
+                           &_hurd_stack_base, &_hurd_stack_size,
+                           &i.flags,
+                           &i.args, &i.argslen, &i.env, &i.envlen,
+                           &_hurd_init_dtable, &_hurd_init_dtablesize,
+                           &i.portarray, &i.portarraysize,
+                           &i.intarray, &i.intarraysize);
+      __mach_port_deallocate (__mach_task_self (), in_bootstrap);
+    }
+
+  if (err || in_bootstrap == MACH_PORT_NULL)
+    {
+      /* Either we have no bootstrap port, or the RPC to the exec server
+        failed.  Try to snarf the args in the canonical Mach way.
+        Hopefully either they will be on the stack as expected, or the
+        stack will be zeros so we don't crash.  Set all our other
+        variables to have empty information.  */
+
+      /* SNARF_ARGS (ARGPTR, ARGC, ARGV, ENVP) snarfs the arguments and
+        environment from the stack, assuming they were put there by the
+        microkernel.  */
+      SNARF_ARGS (argptr, i.argc, i.argv, i.envp);
+
+      i.flags = 0;
+      i.args = i.env = NULL;
+      i.argslen = i.envlen = 0;
+      _hurd_init_dtable = NULL;
+      _hurd_init_dtablesize = 0;
+      i.portarray = NULL;
+      i.portarraysize = 0;
+      i.intarray = NULL;
+      i.intarraysize = 0;
+    }
+  else
+    i.argv = i.envp = NULL;
+
+  i.hurd_main = main;
+
+  /* The user might have defined a value for this, to get more variables.
+     Otherwise it will be zero on startup.  We must make sure it is set
+     properly before before cthreads initialization, so cthreads can know
+     how much space to leave for thread variables.  */
+  if (__hurd_threadvar_max < _HURD_THREADVAR_MAX)
+    __hurd_threadvar_max = _HURD_THREADVAR_MAX;
+
+  /* Do cthreads initialization and switch to the cthread stack.  */
+
+  if (_cthread_init_routine != NULL)
+    CALL_WITH_SP (start1, i, (*_cthread_init_routine) ());
+  else
+    start1 (&i);
+
+  /* Should never get here.  */
+  LOSE;
+}
+
+
+static void
+start1 (struct info *info)
+{
+  register int envc = 0;
+
+  {
+    /* Check if the stack we are now on is different from
+       the one described by _hurd_stack_{base,size}.  */
+
+    char dummy;
+    const vm_address_t newsp = (vm_address_t) &dummy;
+
+    if (_hurd_stack_size != 0 && (newsp < _hurd_stack_base ||
+                                 newsp - _hurd_stack_base > _hurd_stack_size))
+      /* The new stack pointer does not intersect with the
+        stack the exec server set up for us, so free that stack.  */
+      __vm_deallocate (__mach_task_self (),
+                      _hurd_stack_base, _hurd_stack_size);
+  }
+
+  if (__hurd_threadvar_stack_mask == 0)
+    {
+      /* We are not using cthreads, so we will have just a single allocated
+        area for the per-thread variables of the main user thread.  */
+      unsigned long int i;
+      __hurd_threadvar_stack_offset
+       = (unsigned long int) malloc (__hurd_threadvar_max *
+                                     sizeof (unsigned long int));
+      if (__hurd_threadvar_stack_offset == 0)
+       __libc_fatal ("Can't allocate single-threaded per-thread variables.");
+      for (i = 0; i < __hurd_threadvar_max; ++i)
+       ((unsigned long int *) __hurd_threadvar_stack_offset)[i] = 0;
+    }
+
+
+  /* Turn the block of null-separated strings we were passed for the
+     arguments and environment into vectors of pointers to strings.  */
+
+  if (! info->argv)
+    {
+      if (info->args)
+       /* Count up the arguments so we can allocate ARGV.  */
+       info->argc = _hurd_split_args (args, argslen, NULL);
+      if (! info->args || info->argc == 0)
+       {
+         /* No arguments passed; set argv to { NULL }.  */
+         info->argc = 0;
+         info->args = NULL;
+         info->argv = (char **) &info->args;
+       }
+    }
+
+  if (! info->envp)
+    {
+      if (info->env)
+       /* Count up the environment variables so we can allocate ENVP.  */
+       envc = _hurd_split_args (info->env, info->envlen, NULL);
+      if (! info->env || envc == 0)
+       {
+         /* No environment passed; set __environ to { NULL }.  */
+         info->env = NULL;
+         info->envp = (char **) &env;
+       }
+    }
+
+  if (! info->argv)
+    {
+      /* There were some arguments.
+        Allocate space for the vectors of pointers and fill them in.  */
+      info->argv = __alloca ((info->argc + 1) * sizeof (char *));
+      _hurd_split_args (info->args, info->argslen, info->argv);
+    }
+  
+  if (! info->envp)
+    {
+      /* There was some environment.
+        Allocate space for the vectors of pointers and fill them in.  */
+      info->envp = __alloca ((envc + 1) * sizeof (char *));
+      _hurd_split_args (info->env, info->envlen, info->envp);
+    }
+
+  (*info->hurd_main) (info->argc, info->argv, info->envp,
+                     info->portarray, info->portarraysize,
+                     info->intarray, info->intarraysize);
+
+  /* Should never get here.  */
+  LOSE;
+}
+
+/* Split ARGSLEN bytes at ARGS into words, breaking at NUL characters.  If
+   ARGV is not a null pointer, store a pointer to the start of each word in
+   ARGV[n], and null-terminate ARGV.  Return the number of words split.  */
+
+int
+_hurd_split_args (char *args, size_t argslen, char **argv)
+{
+  char *p = args;
+  size_t n = argslen;
+  int argc = 0;
+
+  while (n > 0)
+    {
+      char *end = memchr (p, '\0', n);
+
+      if (argv)
+       argv[argc] = p;
+      ++argc;
+
+      if (end == NULL)
+       /* The last argument is unterminated.  */
+       break;
+
+      n -= end + 1 - p;
+      p = end + 1;
+    }
+
+  if (argv)
+    argv[argc] = NULL;
+  return argc;
+}
index 93916a0..f810179 100644 (file)
@@ -195,4 +195,10 @@ ifdef interface-library
 $(interface-library)-routines = $(interface-routines)
 extra-libs += $(interface-library)
 
+# Avoid -lmachuser requiring -lc, which may not be built yet.  If the
+# shared object is absent, ld may choose a static library someplace and
+# produce a bogus libmachuser.so.
+interface.so = $(interface-library:lib%=%.so)
+LDFLAGS-$(interface.so) = -nostdlib
+
 endif
index 0092dbc..98807ff 100644 (file)
@@ -177,8 +177,8 @@ res_init()
                _res.retrans = RES_TIMEOUT;
        if (!_res.retry)
                _res.retry = 4;
-       if (!_res.options)
-               _res.options = RES_DEFAULT;
+       if (!(_res.options & RES_INIT))
+               _res.options |= RES_DEFAULT;
 
 #ifdef USELOOPBACK
        _res.nsaddr.sin_addr = inet_makeaddr(IN_LOOPBACKNET, 1);
index 02430fa..d1e8580 100644 (file)
@@ -128,6 +128,10 @@ ifeq (,$(subdir))
 install-others += $(libdir)/libc.a
 $(libdir)/libc.a: $(hurd)/libc-ldscript; $(do-install)
 endif
+
+# For the shared library, we don't need to do the linker script machination.
+# Instead, we specify the required libraries when building the shared object.
+LDLIBS-c.so = -lmachuser -lhurduser
 \f
 
 endif  # in-Makerules
index b9170f1..8e625b8 100644 (file)
@@ -67,24 +67,14 @@ __fork (void)
   error_t err;
   thread_t thread_self = __mach_thread_self ();
   struct hurd_sigstate *volatile ss;
-  sigset_t pending;
-
-  void unlockss (void)
-    {
-      __spin_lock (&ss->lock);
-      ss->critical_section = 0;
-      pending = ss->pending & ~ss->blocked;
-      __spin_unlock (&ss->lock);
-      /* XXX Copying mutex into child and calling mutex_unlock lossy.  */
-      __mutex_unlock (&_hurd_siglock);
-      ss = NULL;               /* Make sure we crash if we use it again.  */
-    }
 
   ss = _hurd_self_sigstate ();
   __spin_lock (&ss->lock);
   ss->critical_section = 1;
   __spin_unlock (&ss->lock);
-  __mutex_lock (&_hurd_siglock);
+
+#undef LOSE
+#define LOSE assert_perror (err) /* XXX */
 
   if (! setjmp (env))
     {
@@ -108,6 +98,7 @@ __fork (void)
       /* Lock things that want to be locked before we fork.  */
       for (i = 0; i < _hurd_fork_locks.n; ++i)
        __mutex_lock (_hurd_fork_locks.locks[i]);
+      __mutex_lock (&_hurd_siglock);
       
       newtask = MACH_PORT_NULL;
       thread = sigthread = MACH_PORT_NULL;
@@ -121,31 +112,37 @@ __fork (void)
       ports_locked = 1;
 
       /* Create the child task.  It will inherit a copy of our memory.  */
-      if (err = __task_create (__mach_task_self (), 1, &newtask))
-       goto lose;
+      err = __task_create (__mach_task_self (), 1, &newtask);
+
+      /* Unlock the global signal state lock, so we do not
+        block the signal thread any longer than necessary.  */
+      __mutex_unlock (&_hurd_siglock);
+
+      if (err)
+       LOSE;
 
       /* Fetch the names of all ports used in this task.  */
       if (err = __mach_port_names (__mach_task_self (),
                                   &portnames, &nportnames,
                                   &porttypes, &nporttypes))
-       goto lose;
+       LOSE;
       if (nportnames != nporttypes)
        {
          err = EGRATUITOUS;
-         goto lose;
+         LOSE;
        }
 
       /* Get send rights for all the threads in this task.
         We want to avoid giving these rights to the child.  */
       if (err = __task_threads (__mach_task_self (), &threads, &nthreads))
-       goto lose;
+       LOSE;
 
       /* Get the child process's proc server port.  We will insert it into
         the child with the same name as we use for our own proc server
         port; and we will need it to set the child's message port.  */
       if (err = __proc_task2proc (_hurd_ports[INIT_PORT_PROC].port,
                                  newtask, &newproc))
-       goto lose;
+       LOSE;
 
       /* Insert all our port rights into the child task.  */
       thread_refs = sigthread_refs = 0;
@@ -187,7 +184,7 @@ __fork (void)
                           }));
                }
              else if (err)
-               goto lose;
+               LOSE;
              if (porttypes[i] & MACH_PORT_TYPE_SEND)
                {
                  /* Give the child as many send rights for its receive
@@ -199,12 +196,12 @@ __fork (void)
                                                  portnames[i],
                                                  MACH_PORT_RIGHT_SEND,
                                                  &refs))
-                   goto lose;
+                   LOSE;
                  if (err = __mach_port_extract_right (newtask,
                                                       portnames[i],
                                                       MACH_MSG_TYPE_MAKE_SEND,
                                                       &port, &poly))
-                   goto lose;
+                   LOSE;
                  if (portnames[i] == _hurd_msgport)
                    {
                      /* We just created a receive right for the child's
@@ -213,7 +210,7 @@ __fork (void)
                         for it, give it to the proc server.  */
                      mach_port_t old;
                      if (err = __proc_setmsgport (newproc, port, &old))
-                       goto lose;
+                       LOSE;
                      if (old != MACH_PORT_NULL)
                        /* XXX what to do here? */
                        __mach_port_deallocate (__mach_task_self (), old);
@@ -222,13 +219,13 @@ __fork (void)
                                                      portnames[i],
                                                      port,
                                                      MACH_MSG_TYPE_MOVE_SEND))
-                   goto lose;
+                   LOSE;
                  if (refs > 1 &&
                      (err = __mach_port_mod_refs (newtask,
                                                   portnames[i],
                                                   MACH_PORT_RIGHT_SEND,
                                                   refs - 1)))
-                   goto lose;
+                   LOSE;
                }
              if (porttypes[i] & MACH_PORT_TYPE_SEND_ONCE)
                {
@@ -241,15 +238,16 @@ __fork (void)
                       portnames[i],
                       MACH_MSG_TYPE_MAKE_SEND_ONCE,
                       &port, &poly))
-                   goto lose;
+                   LOSE;
                  if (err = __mach_port_insert_right
                      (newtask,
                       portnames[i], port,
                       MACH_MSG_TYPE_MOVE_SEND_ONCE))
-                   goto lose;
+                   LOSE;
                }
            }
-         else if (porttypes[i] & MACH_PORT_TYPE_SEND)
+         else if (porttypes[i] &
+                  (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_DEAD_NAME))
            {
              /* This is a send right or a dead name.
                 Give the child as many references for it as we have.  */
@@ -266,7 +264,7 @@ __fork (void)
                {
                  /* Get the proc server port for the new task.  */
                  if (err = __proc_task2proc (portnames[i], newtask, &insert))
-                   goto lose;
+                   LOSE;
                }
              else if (portnames[i] == thread_self)
                {
@@ -281,7 +279,7 @@ __fork (void)
                      rights created when a thread is created).  */
                  if (err = __mach_port_allocate_name
                      (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
-                   goto lose;
+                   LOSE;
                }
              else if (portnames[i] == _hurd_msgport_thread)
                /* For the name we use for our signal thread's thread port,
@@ -293,7 +291,7 @@ __fork (void)
                  /* Allocate a dead name right as a placeholder.  */
                  if (err = __mach_port_allocate_name
                      (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
-                   goto lose;
+                   LOSE;
                }
              else
                {
@@ -313,38 +311,61 @@ __fork (void)
                                              portnames[i],
                                              MACH_PORT_RIGHT_SEND,
                                              record_refs ?: &refs))
-               goto lose;
+               LOSE;
              if (insert == MACH_PORT_NULL)
                continue;
-             /* Insert the chosen send right into the child.  */
-             err = __mach_port_insert_right (newtask,
-                                             portnames[i],
-                                             insert,
-                                             MACH_MSG_TYPE_COPY_SEND);
-             if (err == KERN_NAME_EXISTS)
+             if (insert == portnames[i] &&
+                 (porttypes[i] & MACH_PORT_TYPE_DEAD_NAME))
+               /* This is a dead name; allocate another dead name
+                  with the same name in the child.  */
+             allocate_dead_name:
+               err = __mach_port_allocate_name (newtask,
+                                                MACH_PORT_RIGHT_DEAD_NAME,
+                                                portnames[i]);
+             else
+               /* Insert the chosen send right into the child.  */
+               err = __mach_port_insert_right (newtask,
+                                               portnames[i],
+                                               insert,
+                                               MACH_MSG_TYPE_COPY_SEND);
+             switch (err)
                {
-                 /* It already has a send right under this name (?!).
-                    Well, it starts out with a send right for its task
-                    port, and inherits the bootstrap and exception ports
-                    from us.  */
-                 mach_port_t childport;
-                 mach_msg_type_name_t poly;
-                 assert (__mach_port_extract_right (newtask, portnames[i],
-                                                    MACH_MSG_TYPE_COPY_SEND,
-                                                    &childport, &poly) == 0 &&
-                         childport == insert &&
-                         __mach_port_deallocate (__mach_task_self (),
-                                                 childport) == 0);
+               case KERN_NAME_EXISTS:
+                 {
+                   /* It already has a send right under this name (?!).
+                      Well, it starts out with a send right for its task
+                      port, and inherits the bootstrap and exception ports
+                      from us.  */
+                   mach_port_t childport;
+                   mach_msg_type_name_t poly;
+                   assert (__mach_port_extract_right (newtask, portnames[i],
+                                                      MACH_MSG_TYPE_COPY_SEND,
+                                                      &childport,
+                                                      &poly) == 0 &&
+                           childport == insert &&
+                           __mach_port_deallocate (__mach_task_self (),
+                                                   childport) == 0);
+                   break;
+                 }
+
+               case KERN_INVALID_CAPABILITY:
+                 /* The port just died.  It was a send right,
+                    and now it's a dead name.  */
+                 goto allocate_dead_name;
+
+               default:
+                 LOSE;
+                 break;
+
+               case KERN_SUCCESS:
+                 /* Give the child as many user references as we have.  */
+                 if (refs > 1 &&
+                     (err = __mach_port_mod_refs (newtask,
+                                                  portnames[i],
+                                                  MACH_PORT_RIGHT_SEND,
+                                                  refs - 1)))
+                   LOSE;
                }
-             else if (err)
-               goto lose;
-             /* Give the child as many user references as we have.  */
-             if (refs > 1 &&
-                 (err = __mach_port_mod_refs (newtask,
-                                              portnames[i],
-                                              MACH_PORT_RIGHT_SEND,
-                                              refs - 1)))
-               goto lose;
            }
        }
 
@@ -354,13 +375,10 @@ __fork (void)
        __spin_unlock (&_hurd_ports[i].lock);
       ports_locked = 0;
 
-      /* Unlock the signal state.  The child must unlock its own copy too.  */
-      unlockss ();
-
       /* Create the child main user thread and signal thread.  */
       if ((err = __thread_create (newtask, &thread)) ||
          (err = __thread_create (newtask, &sigthread)))
-       goto lose;
+       LOSE;
 
       /* Insert send rights for those threads.  We previously allocated
          dead name rights with the names we want to give the thread ports
@@ -369,7 +387,7 @@ __fork (void)
       if ((err = __mach_port_deallocate (newtask, thread_self)) ||
          (err = __mach_port_insert_right (newtask, thread_self,
                                           thread, MACH_MSG_TYPE_COPY_SEND)))
-       goto lose;
+       LOSE;
       /* We have one extra user reference created at the beginning of this
         function, accounted for by mach_port_names (and which will thus be
         accounted for in the child below).  This extra right gets consumed
@@ -378,18 +396,18 @@ __fork (void)
          (err = __mach_port_mod_refs (newtask, thread_self,
                                       MACH_PORT_RIGHT_SEND,
                                       thread_refs - 1)))
-       goto lose;
+       LOSE;
       if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none.  */
          && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread)) ||
              (err = __mach_port_insert_right (newtask, _hurd_msgport_thread,
                                               sigthread,
                                               MACH_MSG_TYPE_COPY_SEND))))
-       goto lose;
+       LOSE;
       if (sigthread_refs > 1 &&
          (err = __mach_port_mod_refs (newtask, _hurd_msgport_thread,
                                       MACH_PORT_RIGHT_SEND,
                                       sigthread_refs - 1)))
-       goto lose;
+       LOSE;
 
       /* This seems like a convenient juncture to copy the proc server's
         idea of what addresses our argv and envp are found at from the
@@ -400,7 +418,7 @@ __fork (void)
        err = (__USEPORT (PROC, __proc_get_arg_locations (port, &argv, &envp))
               ?: __proc_set_arg_locations (newproc, argv, envp));
        if (err)
-         goto lose;
+         LOSE;
       }
            
       /* Set the child signal thread up to run the msgport server function
@@ -412,7 +430,7 @@ __fork (void)
       if (err = __thread_get_state (_hurd_msgport_thread,
                                    MACHINE_THREAD_STATE_FLAVOR,
                                    (natural_t *) &state, &statecount))
-       goto lose;
+       LOSE;
 #if STACK_GROWTH_UP
       state.SP = __hurd_sigthread_stack_base;
 #else
@@ -422,7 +440,7 @@ __fork (void)
                                   (unsigned long int) _hurd_msgport_receive);
       if (err = __thread_set_state (sigthread, MACHINE_THREAD_STATE_FLAVOR,
                                    (natural_t *) &state, statecount))
-       goto lose;
+       LOSE;
       /* We do not thread_resume SIGTHREAD here because the child
         fork needs to do more setup before it can take signals.  */
 
@@ -430,13 +448,13 @@ __fork (void)
       _hurd_longjmp_thread_state (&state, env, 1);
       if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
                                    (natural_t *) &state, statecount))
-       goto lose;
+       LOSE;
 
       /* Get the PID of the child from the proc server.  We must do this
         before calling proc_child below, because at that point any
         authorized POSIX.1 process may kill the child task with SIGKILL.  */
       if (err = __USEPORT (PROC, __proc_task2pid (port, newtask, &pid)))
-       goto lose;
+       LOSE;
 
       /* Register the child with the proc server.  It is important that
         this be that last thing we do before starting the child thread
@@ -445,7 +463,7 @@ __fork (void)
         this point, and the child must have a message port so it responds
         to POSIX.1 signals.  */
       if (err = __USEPORT (PROC, __proc_child (port, newtask)))
-       goto lose;
+       LOSE;
 
       /* This must be the absolutely last thing we do; we can't assume that
         the child will remain alive for even a moment once we do this.  We
@@ -527,9 +545,7 @@ __fork (void)
        }
       ss->next = NULL;
       _hurd_sigstates = ss;
-
-      /* Unlock our copies of the signal state locks.  */
-      unlockss ();
+      __mutex_unlock (&_hurd_siglock);
 
       /* Fetch our new process IDs from the proc server.  No need to
         refetch our pgrp; it is always inherited from the parent (so
@@ -565,8 +581,7 @@ __fork (void)
   for (i = 0; i < _hurd_fork_locks.n; ++i)
     __mutex_unlock (_hurd_fork_locks.locks[i]);
 
-  if (pending)
-    __msg_sig_post (_hurd_msgport, 0, __mach_task_self ());
+  _hurd_critical_section_unlock (ss);
 
   return err ? __hurd_fail (err) : pid;
 }