Back to index

glibc  2.9
trampoline.c
Go to the documentation of this file.
00001 /* Set thread_state for sighandler, and sigcontext to recover.  i386 version.
00002    Copyright (C) 1994,1995,1996,1997,1998,1999,2005,2008
00003        Free Software Foundation, Inc.
00004    This file is part of the GNU C Library.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <hurd/signal.h>
00022 #include <hurd/userlink.h>
00023 #include <thread_state.h>
00024 #include <mach/machine/eflags.h>
00025 #include <assert.h>
00026 #include <errno.h>
00027 #include "hurdfault.h"
00028 #include <intr-msg.h>
00029 
00030 
00031 struct sigcontext *
00032 _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler,
00033                      int signo, struct hurd_signal_detail *detail,
00034                      volatile int rpc_wait,
00035                      struct machine_thread_all_state *state)
00036 {
00037   void trampoline (void);
00038   void rpc_wait_trampoline (void);
00039   void firewall (void);
00040   extern const void _hurd_intr_rpc_msg_in_trap;
00041   extern const void _hurd_intr_rpc_msg_cx_sp;
00042   extern const void _hurd_intr_rpc_msg_sp_restored;
00043   void *volatile sigsp;
00044   struct sigcontext *scp;
00045   struct
00046     {
00047       int signo;
00048       long int sigcode;
00049       struct sigcontext *scp;      /* Points to ctx, below.  */
00050       void *sigreturn_addr;
00051       void *sigreturn_returns_here;
00052       struct sigcontext *return_scp; /* Same; arg to sigreturn.  */
00053       struct sigcontext ctx;
00054       struct hurd_userlink link;
00055     } *stackframe;
00056 
00057   if (ss->context)
00058     {
00059       /* We have a previous sigcontext that sigreturn was about
00060         to restore when another signal arrived.  We will just base
00061         our setup on that.  */
00062       if (! _hurdsig_catch_memory_fault (ss->context))
00063        {
00064          memcpy (&state->basic, &ss->context->sc_i386_thread_state,
00065                 sizeof (state->basic));
00066          memcpy (&state->fpu, &ss->context->sc_i386_float_state,
00067                 sizeof (state->fpu));
00068          state->set |= (1 << i386_THREAD_STATE) | (1 << i386_FLOAT_STATE);
00069        }
00070     }
00071 
00072   if (! machine_get_basic_state (ss->thread, state))
00073     return NULL;
00074 
00075   /* Save the original SP in the gratuitous `esp' slot.
00076      We may need to reset the SP (the `uesp' slot) to avoid clobbering an
00077      interrupted RPC frame.  */
00078   state->basic.esp = state->basic.uesp;
00079 
00080   if ((ss->actions[signo].sa_flags & SA_ONSTACK) &&
00081       !(ss->sigaltstack.ss_flags & (SS_DISABLE|SS_ONSTACK)))
00082     {
00083       sigsp = ss->sigaltstack.ss_sp + ss->sigaltstack.ss_size;
00084       ss->sigaltstack.ss_flags |= SS_ONSTACK;
00085       /* XXX need to set up base of new stack for
00086         per-thread variables, cthreads.  */
00087     }
00088   /* This code has intimate knowledge of the special mach_msg system call
00089      done in intr-msg.c; that code does (see intr-msg.h):
00090                                    movl %esp, %ecx
00091                                    leal ARGS, %esp
00092        _hurd_intr_rpc_msg_cx_sp:   movl $-25, %eax
00093        _hurd_intr_rpc_msg_do_trap: lcall $7, $0
00094        _hurd_intr_rpc_msg_in_trap: movl %ecx, %esp
00095        _hurd_intr_rpc_msg_sp_restored:
00096      We must check for the window during which %esp points at the
00097      mach_msg arguments.  The space below until %ecx is used by
00098      the _hurd_intr_rpc_mach_msg frame, and must not be clobbered.  */
00099   else if (state->basic.eip >= (int) &_hurd_intr_rpc_msg_cx_sp &&
00100           state->basic.eip < (int) &_hurd_intr_rpc_msg_sp_restored)
00101     /* The SP now points at the mach_msg args, but there is more stack
00102        space used below it.  The real SP is saved in %ecx; we must push the
00103        new frame below there, and restore that value as the SP on
00104        sigreturn.  */
00105     sigsp = (char *) (state->basic.uesp = state->basic.ecx);
00106   else
00107     sigsp = (char *) state->basic.uesp;
00108 
00109   /* Push the arguments to call `trampoline' on the stack.  */
00110   sigsp -= sizeof (*stackframe);
00111   stackframe = sigsp;
00112 
00113   if (_hurdsig_catch_memory_fault (stackframe))
00114     {
00115       /* We got a fault trying to write the stack frame.
00116         We cannot set up the signal handler.
00117         Returning NULL tells our caller, who will nuke us with a SIGILL.  */
00118       return NULL;
00119     }
00120   else
00121     {
00122       int ok;
00123 
00124       extern void _hurdsig_longjmp_from_handler (void *, jmp_buf, int);
00125 
00126       /* Add a link to the thread's active-resources list.  We mark this as
00127         the only user of the "resource", so the cleanup function will be
00128         called by any longjmp which is unwinding past the signal frame.
00129         The cleanup function (in sigunwind.c) will make sure that all the
00130         appropriate cleanups done by sigreturn are taken care of.  */
00131       stackframe->link.cleanup = &_hurdsig_longjmp_from_handler;
00132       stackframe->link.cleanup_data = &stackframe->ctx;
00133       stackframe->link.resource.next = NULL;
00134       stackframe->link.resource.prevp = NULL;
00135       stackframe->link.thread.next = ss->active_resources;
00136       stackframe->link.thread.prevp = &ss->active_resources;
00137       if (stackframe->link.thread.next)
00138        stackframe->link.thread.next->thread.prevp
00139          = &stackframe->link.thread.next;
00140       ss->active_resources = &stackframe->link;
00141 
00142       /* Set up the arguments for the signal handler.  */
00143       stackframe->signo = signo;
00144       stackframe->sigcode = detail->code;
00145       stackframe->scp = stackframe->return_scp = scp = &stackframe->ctx;
00146       stackframe->sigreturn_addr = &__sigreturn;
00147       stackframe->sigreturn_returns_here = firewall; /* Crash on return.  */
00148 
00149       /* Set up the sigcontext from the current state of the thread.  */
00150 
00151       scp->sc_onstack = ss->sigaltstack.ss_flags & SS_ONSTACK ? 1 : 0;
00152 
00153       /* struct sigcontext is laid out so that starting at sc_gs mimics a
00154         struct i386_thread_state.  */
00155       memcpy (&scp->sc_i386_thread_state,
00156              &state->basic, sizeof (state->basic));
00157 
00158       /* struct sigcontext is laid out so that starting at sc_fpkind mimics
00159         a struct i386_float_state.  */
00160       ok = machine_get_state (ss->thread, state, i386_FLOAT_STATE,
00161                            &state->fpu, &scp->sc_i386_float_state,
00162                            sizeof (state->fpu));
00163 
00164       _hurdsig_end_catch_fault ();
00165 
00166       if (! ok)
00167        return NULL;
00168     }
00169 
00170   /* Modify the thread state to call the trampoline code on the new stack.  */
00171   if (rpc_wait)
00172     {
00173       /* The signalee thread was blocked in a mach_msg_trap system call,
00174         still waiting for a reply.  We will have it run the special
00175         trampoline code which retries the message receive before running
00176         the signal handler.
00177 
00178         To do this we change the OPTION argument on its stack to enable only
00179         message reception, since the request message has already been
00180         sent.  */
00181 
00182       struct mach_msg_trap_args *args = (void *) state->basic.esp;
00183 
00184       if (_hurdsig_catch_memory_fault (args))
00185        {
00186          /* Faulted accessing ARGS.  Bomb.  */
00187          return NULL;
00188        }
00189 
00190       assert (args->option & MACH_RCV_MSG);
00191       /* Disable the message-send, since it has already completed.  The
00192         calls we retry need only wait to receive the reply message.  */
00193       args->option &= ~MACH_SEND_MSG;
00194 
00195       /* Limit the time to receive the reply message, in case the server
00196         claimed that `interrupt_operation' succeeded but in fact the RPC
00197         is hung.  */
00198       args->option |= MACH_RCV_TIMEOUT;
00199       args->timeout = _hurd_interrupted_rpc_timeout;
00200 
00201       _hurdsig_end_catch_fault ();
00202 
00203       state->basic.eip = (int) rpc_wait_trampoline;
00204       /* The reply-receiving trampoline code runs initially on the original
00205         user stack.  We pass it the signal stack pointer in %ebx.  */
00206       state->basic.uesp = state->basic.esp; /* Restore mach_msg syscall SP.  */
00207       state->basic.ebx = (int) sigsp;
00208       /* After doing the message receive, the trampoline code will need to
00209         update the %eax value to be restored by sigreturn.  To simplify
00210         the assembly code, we pass the address of its slot in SCP to the
00211         trampoline code in %ecx.  */
00212       state->basic.ecx = (int) &scp->sc_eax;
00213     }
00214   else
00215     {
00216       state->basic.eip = (int) trampoline;
00217       state->basic.uesp = (int) sigsp;
00218     }
00219   /* We pass the handler function to the trampoline code in %edx.  */
00220   state->basic.edx = (int) handler;
00221 
00222   /* The x86 ABI says the DF bit is clear on entry to any function.  */
00223   state->basic.efl &= ~EFL_DF;
00224 
00225   return scp;
00226 }
00227 
00228 /* The trampoline code follows.  This used to be located inside
00229    _hurd_setup_sighandler, but was optimized away by gcc 2.95.  */
00230 
00231 asm ("rpc_wait_trampoline:\n");
00232   /* This is the entry point when we have an RPC reply message to receive
00233      before running the handler.  The MACH_MSG_SEND bit has already been
00234      cleared in the OPTION argument on our stack.  The interrupted user
00235      stack pointer has not been changed, so the system call can find its
00236      arguments; the signal stack pointer is in %ebx.  For our convenience,
00237      %ecx points to the sc_eax member of the sigcontext.  */
00238 asm (/* Retry the interrupted mach_msg system call.  */
00239      "movl $-25, %eax\n"    /* mach_msg_trap */
00240      "lcall $7, $0\n"
00241      /* When the sigcontext was saved, %eax was MACH_RCV_INTERRUPTED.  But
00242        now the message receive has completed and the original caller of
00243        the RPC (i.e. the code running when the signal arrived) needs to
00244        see the final return value of the message receive in %eax.  So
00245        store the new %eax value into the sc_eax member of the sigcontext
00246        (whose address is in %ecx to make this code simpler).  */
00247      "movl %eax, (%ecx)\n"
00248      /* Switch to the signal stack.  */
00249      "movl %ebx, %esp\n");
00250 
00251  asm ("trampoline:\n");
00252   /* Entry point for running the handler normally.  The arguments to the
00253      handler function are already on the top of the stack:
00254 
00255        0(%esp)       SIGNO
00256        4(%esp)       SIGCODE
00257        8(%esp)       SCP
00258      */
00259 asm ("call *%edx\n"         /* Call the handler function.  */
00260      "addl $12, %esp\n"            /* Pop its args.  */
00261      /* The word at the top of stack is &__sigreturn; following are a dummy
00262        word to fill the slot for the address for __sigreturn to return to,
00263        and a copy of SCP for __sigreturn's argument.  "Return" to calling
00264        __sigreturn (SCP); this call never returns.  */
00265      "ret");
00266 
00267 asm ("firewall:\n"
00268      "hlt");