aboutsummaryrefslogtreecommitdiff
path: root/sysdeps
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
parent87220b3563eedfa50296d8eb206d506a571cc0a1 (diff)
downloadglibc-99b306dc44a21202c8db071e3565235d8f946bbd.tar
glibc-99b306dc44a21202c8db071e3565235d8f946bbd.tar.gz
glibc-99b306dc44a21202c8db071e3565235d8f946bbd.tar.bz2
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')
-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>