about summary refs log tree commit diff
path: root/sysdeps/mach
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>1995-05-11 01:02:23 +0000
committerRoland McGrath <roland@gnu.org>1995-05-11 01:02:23 +0000
commit99b306dc44a21202c8db071e3565235d8f946bbd (patch)
tree9485bfe539e7b98ec92b6b5b00ff9daddc6fd341 /sysdeps/mach
parent87220b3563eedfa50296d8eb206d506a571cc0a1 (diff)
downloadglibc-99b306dc44a21202c8db071e3565235d8f946bbd.tar.gz
glibc-99b306dc44a21202c8db071e3565235d8f946bbd.tar.xz
glibc-99b306dc44a21202c8db071e3565235d8f946bbd.zip
Wed May 10 21:00:47 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
	* sysdeps/mach/i386/sysdep.h (RETURN_TO): New macro.

	* Makerules (install-lib.so): Add %.so for each %_pic.a.

	* sysdeps/mach/hurd/i386/init-first.c: New file.
Diffstat (limited to 'sysdeps/mach')
-rw-r--r--sysdeps/mach/hurd/dl-sysdep.c494
-rw-r--r--sysdeps/mach/hurd/i386/init-first.c172
-rw-r--r--sysdeps/mach/i386/sysdep.h5
3 files changed, 671 insertions, 0 deletions
diff --git a/sysdeps/mach/hurd/dl-sysdep.c b/sysdeps/mach/hurd/dl-sysdep.c
new file mode 100644
index 0000000000..1dca319433
--- /dev/null
+++ b/sysdeps/mach/hurd/dl-sysdep.c
@@ -0,0 +1,494 @@
+/* Operating system support for run-time dynamic linker.  Hurd version.
+Copyright (C) 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 <hurd.h>
+#include <link.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <sysdep.h>
+#include <mach/mig_support.h>
+#include "hurdstartup.h"
+#include <mach/host_info.h>
+#include "../stdio/_itoa.h"
+#include <hurd/auth.h>
+#include <hurd/term.h>
+#include <stdarg.h>
+
+#include "dl-machine.h"
+
+extern int _dl_argc;
+extern char **_dl_argv;
+extern char **_environ;
+
+struct hurd_startup_data *_dl_hurd_data;
+
+Elf32_Addr
+_dl_sysdep_start (void **start_argptr,
+		  void (*dl_main) (const Elf32_Phdr *phdr, Elf32_Word phent,
+				   Elf32_Addr *user_entry))
+{
+  void go (int *argdata)
+    {
+      char **p;
+
+      /* Cache the information in various global variables.  */
+      _dl_argc = *argdata++;
+      _dl_argv = (void *) argdata;
+      _environ = &_dl_argv[_dl_argc + 1];
+      for (p = _environ; *p; ++p);
+      _dl_hurd_data = (void *) ++p;
+
+      _dl_secure = _dl_hurd_data->flags & EXEC_SECURE;
+
+      /* Call elf/rtld.c's main program.  It will set everything
+	 up and leave us to transfer control to USER_ENTRY.  */
+      (*dl_main) ((const Elf32_Phdr *) _dl_hurd_data->phdr,
+		  _dl_hurd_data->phdrsz / sizeof (Elf32_Phdr),
+		  &_dl_hurd_data->user_entry);
+
+      {
+	extern void _dl_start_user (void);
+	/* Unwind the stack to ARGDATA and simulate a return from _dl_start
+	   to the RTLD_START code which will run the user's entry point.  */
+	RETURN_TO (argdata, &_dl_start_user, _dl_hurd_data->user_entry);
+      }
+    }
+
+  /* See hurd/hurdstartup.c; this deals with getting information
+     from the exec server and slicing up the arguments.
+     Then it will call `go', above.  */
+  _hurd_startup (start_argptr, &go);
+
+  LOSE;
+}
+
+/* This is called when all other dynamic linking is finished, before the
+   dynamic linker re-relocates itself when ld.so itself appears in a
+   DT_NEEDED entry.  It is called whether of not ld.so is being linked in.
+
+   We take this opportunity to deallocate the reply port and task-self send
+   right user reference we have acquired, since they will not be used again
+   before the library and user code runs.  The C library will acquire its
+   own ports in its initialization.  */
+
+void
+_dl_sysdep_prepare_for_ld_reloc (void)
+{
+  __mig_dealloc_reply_port (__mig_get_reply_port ());
+  __mach_port_deallocate (__mach_task_self (), __mach_task_self ());
+}
+
+int
+_dl_sysdep_open_zero_fill (void)
+{
+  return (int) MACH_PORT_NULL;
+}
+
+
+void
+_dl_sysdep_fatal (const char *msg, ...)
+{
+  extern __typeof (__io_write) __hurd_intr_rpc_io_write;
+  va_list ap;
+
+  va_start (ap, msg);
+  do
+    {
+      size_t len = strlen (msg);
+      mach_msg_type_number_t nwrote;
+      do
+	{
+	  if (__hurd_intr_rpc_io_write (_hurd_init_dtable[2],
+					msg, len, -1, &nwrote))
+	    break;
+	  len -= nwrote;
+	  msg += nwrote;
+	} while (nwrote > 0);
+      msg = va_arg (ap, const char *);
+    } while (msg);
+  va_end (ap);
+
+  _exit (127);
+}
+
+
+/* Minimal open/close/mmap implementation sufficient for initial loading of
+   shared libraries.  These are weak definitions so that when the
+   dynamic linker re-relocates itself to be user-visible (for -ldl),
+   it will get the user's definition (i.e. usually libc's).  */
+
+int
+open (const char *file_name, int mode, ...)
+{
+  extern __typeof (__dir_lookup) __hurd_intr_rpc_dir_lookup;
+  extern __typeof (__io_reauthenticate) __hurd_intr_rpc_io_reauthenticate;
+  enum retry_type doretry;
+  char retryname[1024];		/* XXX string_t LOSES! */
+  file_t startdir, newpt, fileport;
+  int dealloc_dir;
+  int nloops;
+
+
+  assert (mode == O_RDONLY);
+
+
+  startdir = _dl_hurd_data->portarray[file_name[0] == '/' ?
+				      INIT_PORT_CRDIR : INIT_PORT_CWDIR];
+
+  while (file_name[0] == '/')
+    file_name++;
+
+  if (errno = __hurd_intr_rpc_dir_lookup (startdir, file_name, mode, 0,
+					  &doretry, retryname, &fileport))
+    return -1;
+
+  dealloc_dir = 0;
+  nloops = 0;
+  errno = 0;
+  
+  while (1)
+    {
+      if (dealloc_dir)
+	__mach_port_deallocate (__mach_task_self (), startdir);
+      if (errno)
+	return -1;
+
+      switch (doretry)
+	{
+	case FS_RETRY_REAUTH:
+	  {
+	    mach_port_t ref = __mach_reply_port ();
+	    errno = __hurd_intr_rpc_io_reauthenticate
+	      (fileport, ref, MACH_MSG_TYPE_MAKE_SEND);
+	    if (! errno)
+	      errno = __auth_user_authenticate
+		(_dl_hurd_data->portarray[INIT_PORT_AUTH],
+		 fileport,
+		 ref, MACH_MSG_TYPE_MAKE_SEND,
+		 &newpt);
+	    __mach_port_destroy (__mach_task_self (), ref);
+	  }
+	  __mach_port_deallocate (__mach_task_self (), fileport);
+	  if (errno)
+	    return -1;
+	  fileport = newpt;
+	  /* Fall through.  */
+
+	case FS_RETRY_NORMAL:
+#ifdef SYMLOOP_MAX
+	  if (nloops++ >= SYMLOOP_MAX)
+	    {
+	      errno = ELOOP;
+	      return -1;
+	    }
+#endif
+
+	  /* An empty RETRYNAME indicates we have the final port.  */
+	  if (retryname[0] == '\0')
+	    {
+	      mach_port_t memobj_rd, memobj_wr;
+	      extern __typeof (__io_map) __hurd_intr_rpc_io_map;
+
+	      dealloc_dir = 1;
+
+	    opened:
+	      /* We have the file open.  Now map it.  */
+	      errno = __hurd_intr_rpc_io_map (fileport,
+					      &memobj_rd, &memobj_wr);
+	      if (dealloc_dir)
+		__mach_port_deallocate (__mach_task_self (), fileport);
+	      if (errno)
+		return -1;
+	      if (memobj_wr != MACH_PORT_NULL)
+		__mach_port_deallocate (__mach_task_self (), memobj_wr);
+
+	      return (int) memobj_rd;
+	    }
+
+	  startdir = fileport;
+	  dealloc_dir = 1;
+	  file_name = retryname;
+	  break;
+
+	case FS_RETRY_MAGICAL:
+	  switch (retryname[0])
+	    {
+	    case '/':
+	      startdir = _dl_hurd_data->portarray[INIT_PORT_CRDIR];
+	      dealloc_dir = 0;
+	      if (fileport != MACH_PORT_NULL)
+		__mach_port_deallocate (__mach_task_self (), fileport);
+	      file_name = &retryname[1];
+	      break;
+
+	    case 'f':
+	      if (retryname[1] == 'd' && retryname[2] == '/')
+		{
+		  int fd;
+		  char *end;
+		  errno = 0;
+		  fd = (int) strtol (retryname, &end, 10);
+		  if (end == NULL || errno || /* Malformed number.  */
+		      /* Check for excess text after the number.  A slash
+			 is valid; it ends the component.  Anything else
+			 does not name a numeric file descriptor.  */
+		      (*end != '/' && *end != '\0'))
+		    {
+		      errno = ENOENT;
+		      return -1;
+		    }
+		  if (fd < 0 || fd >= _dl_hurd_data->dtablesize ||
+		      _dl_hurd_data->dtable[fd] == MACH_PORT_NULL)
+		    {
+		      /* If the name was a proper number, but the file
+			 descriptor does not exist, we return EBADF instead
+			 of ENOENT.  */
+		      errno = EBADF;
+		      return -1;
+		    }
+		  fileport = _dl_hurd_data->dtable[fd];
+		  if (*end == '\0')
+		    {
+		      /* This descriptor is the file port we want.  */
+		      dealloc_dir = 0;
+		      goto opened;
+		    }
+		  else
+		    {
+		      /* Do a normal retry on the remaining components.  */
+		      startdir = fileport;
+		      dealloc_dir = 1;
+		      file_name = end + 1; /* Skip the slash.  */
+		      break;
+		    }
+		}
+	      else
+		goto bad_magic;
+	      break;
+
+	    case 'm':
+	      if (retryname[1] == 'a' && retryname[2] == 'c' &&
+		  retryname[3] == 'h' && retryname[4] == 't' &&
+		  retryname[5] == 'y' && retryname[6] == 'p' &&
+		  retryname[7] == 'e')
+		{
+		  error_t err;
+		  struct host_basic_info hostinfo;
+		  mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
+		  char *p;
+		  if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
+					 (natural_t *) &hostinfo,
+					 &hostinfocnt))
+		    return err;
+		  if (hostinfocnt != HOST_BASIC_INFO_COUNT)
+		    return EGRATUITOUS;
+		  p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
+		  *--p = '/';
+		  p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
+		  if (p < retryname)
+		    abort ();	/* XXX write this right if this ever happens */
+		  if (p > retryname)
+		    strcpy (retryname, p);
+		  startdir = fileport;
+		  dealloc_dir = 1;
+		}
+	      else
+		goto bad_magic;
+	      break;
+
+	    case 't':
+	      if (retryname[1] == 't' && retryname[2] == 'y')
+		switch (retryname[3])
+		  {
+		    error_t opentty (file_t *result)
+		      {
+			error_t err;
+			file_t unauth;
+			err = __termctty_open_terminal
+			  (_dl_hurd_data->portarray[INIT_PORT_CTTYID],
+			   mode, &unauth);
+			if (! err)
+			  {
+			    mach_port_t ref = __mach_reply_port ();
+			    err = __hurd_intr_rpc_io_reauthenticate
+			      (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
+			    if (! err)
+			      err = __auth_user_authenticate
+				(_dl_hurd_data->portarray[INIT_PORT_AUTH],
+				 unauth,
+				 ref, MACH_MSG_TYPE_MAKE_SEND,
+				 result);
+			    __mach_port_deallocate (__mach_task_self (),
+						    unauth);
+			    __mach_port_destroy (__mach_task_self (), ref);
+			  }
+			return err;
+		      }
+
+		  case '\0':
+		    if (errno = opentty (&fileport))
+		      return -1;
+		    dealloc_dir = 1;
+		    goto opened;
+		  case '/':
+		    if (errno = opentty (&startdir))
+		      return -1;
+		    dealloc_dir = 1;
+		    strcpy (retryname, &retryname[4]);
+		    break;
+		  default:
+		    goto bad_magic;
+		  }
+	      else
+		goto bad_magic;
+	      break;
+
+	    default:
+	    bad_magic:
+	      errno = EGRATUITOUS;
+	      return -1;
+	    }
+	  break;		
+
+	default:
+	  errno = EGRATUITOUS;
+	  return -1;
+	}
+
+      errno = __hurd_intr_rpc_dir_lookup (startdir, file_name, mode, 0,
+					  &doretry, retryname, &fileport);
+    }
+}
+
+int
+close (int fd)
+{
+  if (fd != (int) MACH_PORT_NULL)
+    __mach_port_deallocate (__mach_task_self (), (mach_port_t) fd);
+  return 0;
+}
+
+caddr_t
+mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
+{
+  vm_prot_t vmprot;
+  vm_address_t mapaddr;
+
+  vmprot = VM_PROT_NONE;
+  if (prot & PROT_READ)
+    vmprot |= VM_PROT_READ;
+  if (prot & PROT_WRITE)
+    vmprot |= VM_PROT_WRITE;
+  if (prot & PROT_EXEC)
+    vmprot |= VM_PROT_EXECUTE;
+
+  mapaddr = (vm_address_t) addr;
+  errno = __vm_map (__mach_task_self (),
+		    &mapaddr, (vm_size_t) len, (vm_address_t) 0,
+		    !(flags & MAP_FIXED),
+		    (mach_port_t) fd, (vm_offset_t) offset,
+		    flags & (MAP_COPY|MAP_PRIVATE),
+		    vmprot, VM_PROT_ALL,
+		    (flags & MAP_INHERIT) ? VM_INHERIT_COPY : VM_INHERIT_NONE);
+  return errno ? (caddr_t) -1 : (caddr_t) mapaddr;
+}
+
+void
+_exit (int status)
+{
+  extern __typeof (__proc_mark_exit) __hurd_intr_rpc_proc_mark_exit;
+  __hurd_intr_rpc_proc_mark_exit (_dl_hurd_data->portarray[INIT_PORT_PROC],
+				  W_EXITCODE (status, 0));
+  while (__task_terminate (__mach_task_self ()))
+    __mach_task_self_ = (__mach_task_self) ();
+}
+
+weak_symbol (_exit)
+weak_symbol (open)
+weak_symbol (close)
+weak_symbol (mmap)
+
+/* Minimal `malloc' allocator for use while loading shared libraries.
+   Only small blocks are allocated, and none are ever freed.  */
+
+void *
+malloc (size_t n)
+{
+  static vm_address_t ptr, end;
+  void *block;
+
+  if (end == 0)
+    {
+      /* Consume any unused space in the last page of our data segment.  */
+      extern char _end;
+      ptr = (vm_address_t) &_end;
+      end = round_page (ptr);
+    }
+
+  /* Make sure the allocation pointer is ideally aligned.  */
+  ptr += sizeof (double) - 1;
+  ptr &= ~(sizeof (double) - 1);
+
+  if (ptr + n >= end)
+    {
+      /* Insufficient space left; allocate another page.  */
+      vm_address_t page;
+      assert (n <= __vm_page_size);
+      __vm_allocate (__mach_task_self (), &page, __vm_page_size, 1);
+      if (page != end)
+	ptr = page;
+      end = page + __vm_page_size;
+    }
+
+  block = (void *) ptr;
+  ptr += n;
+  return block;
+}
+
+weak_symbol (malloc)
+
+/* These should never be called.  */
+void *realloc (void *ptr, size_t n) { ptr += n; abort (); }
+void free (void *ptr) { ptr = ptr; abort (); }
+weak_symbol (realloc)
+weak_symbol (free)
+
+/* Avoid signal frobnication in setjmp/longjmp.  */
+
+int __sigjmp_save (sigjmp_buf env, int savemask) 
+{ env[0].__mask_was_saved = savemask; return 0; }
+weak_symbol (__sigjmp_save)
+
+void
+longjmp (jmp_buf env, int val) { __longjmp (env[0].__jmpbuf, val); }
+weak_symbol (longjmp)
+
+
+/* Stub out this function that is called by interruptible RPC stubs.  It
+   should never get called during initial dynamic linking, because we use
+   only the raw MiG stub functions __hurd_intr_rpc_*.  Since this defn is
+   weak, the real defn in libc.so will override it if we are linked into
+   the user program (-ldl).  */
+struct hurd_sigstate *
+_hurd_thread_sigstate (thread_t thread) { thread = thread; abort (); }
+weak_symbol (_hurd_thread_sigstate)
diff --git a/sysdeps/mach/hurd/i386/init-first.c b/sysdeps/mach/hurd/i386/init-first.c
new file mode 100644
index 0000000000..55ffe1aada
--- /dev/null
+++ b/sysdeps/mach/hurd/i386/init-first.c
@@ -0,0 +1,172 @@
+/* Initialization code run first thing by the ELF startup code.  For i386/Hurd.
+Copyright (C) 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 <hurd.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "hurdstartup.h"
+#include "set-hooks.h"
+#include "hurdmalloc.h"		/* XXX */
+
+extern void __mach_init (void);
+extern void __libc_init (int, char **, char **);
+
+void *(*_cthread_init_routine) (void); /* Returns new SP to use.  */
+void (*_cthread_exit_routine) (int status) __attribute__ ((__noreturn__));
+
+
+/* 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));
+
+
+static void
+init1 (int argc, char *arg0, ...)
+{
+  char **argv = &arg0;
+  char **envp = &argv[argc + 1];
+  struct hurd_startup_data *d;
+
+  __environ = envp;
+  while (*envp)
+    ++envp;
+  d = (void *) ++envp;
+
+  /* If we are the bootstrap task started by the kernel,
+     then after the environment pointers there is no Hurd
+     data block; the argument strings start there.  */
+  if ((void *) d != argv[0])
+    {
+      _hurd_init_dtable = d->dtable;
+      _hurd_init_dtablesize = d->dtablesize;
+
+      {
+	/* 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 (d->stack_size != 0 && (newsp < d->stack_base ||
+				   newsp - d->stack_base > d->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 (), d->stack_base, d->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;
+    }
+
+  if ((void *) d != argv[0] && (d->portarray || d->intarray))
+    /* Initialize library data structures, start signal processing, etc.  */
+    _hurd_init (d->flags, argv,
+		d->portarray, d->portarraysize,
+		d->intarray, d->intarraysize);
+
+  __libc_init (argc, argv, __environ);
+}
+
+static void 
+init (int *data, int retaddr)
+{
+  int argc = *data;
+  char **argv = (void *) (data + 1);
+  char **envp = &argv[argc + 1];
+  struct hurd_startup_data *d;
+
+  __environ = envp;
+  while (*envp)
+    ++envp;
+  d = (void *) ++envp;
+
+  /* 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;
+
+  if (_cthread_init_routine)
+    {
+      /* Initialize cthreads, which will allocate us a new stack to run on.  */
+      void *newsp = (*_cthread_init_routine) ();
+      /* Copy the argdata from the old stack to the new one.  */
+      newsp = memcpy (newsp - ((char *) &d[1] - (char *) data), data,
+		      (char *) &d[1] - (char *) data);
+      data = newsp;
+    }
+
+  /* Call `init1' (above) with the user code as the return address,
+     and the argument data immediately above that on the stack.  */
+  *--data = retaddr;
+  asm volatile ("movl %0, %%esp; jmp %*%1" : : "g" (data), "r" (&init1));
+}  
+
+
+#ifdef PIC
+/* This function is called to initialize the shared C library.
+   It is called just before the user _start code from i386/elf/start.S,
+   with the stack set up as that code gets it.  */
+
+static void soinit (int argc, ...) __attribute__ ((unused, section (".init")));
+
+static void
+soinit (int argc, ...)
+{
+  /* Initialize data structures so we can do RPCs.  */
+  __mach_init ();
+
+  RUN_HOOK (_hurd_preinit_hook, ());
+  
+  init (&argc, (&argc)[-1]);
+
+  (void) &soinit;		/* Avoid gcc optimizing this fn out.  */
+}
+#endif
+
+
+void
+__libc_init_first (int argc, ...)
+{
+#ifndef PIC
+  void doinit (int *data)
+    {
+      init (data, (&argc)[-1]);
+    }
+
+  /* Initialize data structures so we can do RPCs.  */
+  __mach_init ();
+
+  RUN_HOOK (_hurd_preinit_hook, ());
+  
+  _hurd_startup ((void **) &argc, &doinit);
+#endif
+}
diff --git a/sysdeps/mach/i386/sysdep.h b/sysdeps/mach/i386/sysdep.h
index 692310b320..5b4246e016 100644
--- a/sysdeps/mach/i386/sysdep.h
+++ b/sysdeps/mach/i386/sysdep.h
@@ -44,6 +44,11 @@ Cambridge, MA 02139, USA.  */
 		  "g" (ptr), "m" (*(long int *) (fn)) : "%esp"); 	      \
   } while (0)
 
+#define RETURN_TO(sp, pc, retval) \
+  asm volatile ("movl %0, %%esp; jmp %*%1 # %2" \
+		: : "g" (sp), "r" (pc), "a" (retval))
+
+
 #define STACK_GROWTH_DOWN
 
 #include_next <sysdep.h>