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.  Alpha version.
00002    Copyright (C) 1994,95,97,98,2002 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <hurd/signal.h>
00021 #include "thread_state.h"
00022 #include "hurdfault.h"
00023 #include <assert.h>
00024 
00025 struct mach_msg_trap_args
00026   {
00027     /* This is the order of arguments to mach_msg_trap.  */
00028     mach_msg_header_t *msg;
00029     mach_msg_option_t option;
00030     mach_msg_size_t send_size;
00031     mach_msg_size_t rcv_size;
00032     mach_port_t rcv_name;
00033     mach_msg_timeout_t timeout;
00034     mach_port_t notify;
00035   };
00036 
00037 
00038 struct sigcontext *
00039 _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler,
00040                      int signo, struct hurd_signal_detail *detail,
00041                      int rpc_wait, struct machine_thread_all_state *state)
00042 {
00043   __label__ trampoline, rpc_wait_trampoline;
00044   void *sigsp;
00045   struct sigcontext *scp;
00046 
00047   if (ss->context)
00048     {
00049       /* We have a previous sigcontext that sigreturn was about
00050         to restore when another signal arrived.  We will just base
00051         our setup on that.  */
00052       if (! _hurdsig_catch_memory_fault (ss->context))
00053        {
00054          memcpy (&state->basic, &ss->context->sc_alpha_thread_state,
00055                 sizeof (state->basic));
00056          memcpy (&state->exc, &ss->context->sc_alpha_exc_state,
00057                 sizeof (state->exc));
00058          state->set = (1 << ALPHA_THREAD_STATE) | (1 << ALPHA_EXC_STATE);
00059          if (state->exc.used_fpa)
00060            {
00061              memcpy (&state->fpu, &ss->context->sc_alpha_float_state,
00062                     sizeof (state->fpu));
00063              state->set |= (1 << ALPHA_FLOAT_STATE);
00064            }
00065          assert (! rpc_wait);
00066          /* The intr_port slot was cleared before sigreturn sent us the
00067             sig_post that made us notice this pending signal, so
00068             _hurd_internal_post_signal wouldn't do interrupt_operation.
00069             After we return, our caller will set SCP->sc_intr_port (in the
00070             new context) from SS->intr_port and clear SS->intr_port.  Now
00071             that we are restoring this old context recorded by sigreturn,
00072             we want to restore its intr_port too; so store it in
00073             SS->intr_port now, so it will end up in SCP->sc_intr_port
00074             later.  */
00075          ss->intr_port = ss->context->sc_intr_port;
00076        }
00077       _hurdsig_end_catch_fault ();
00078 
00079       /* If the sigreturn context was bogus, just ignore it.  */
00080       ss->context = NULL;
00081     }
00082   else if (! machine_get_basic_state (ss->thread, state))
00083     return NULL;
00084 
00085   if ((ss->actions[signo].sa_flags & SA_ONSTACK) &&
00086       !(ss->sigaltstack.ss_flags & (SS_DISABLE|SS_ONSTACK)))
00087     {
00088       sigsp = ss->sigaltstack.ss_sp + ss->sigaltstack.ss_size;
00089       ss->sigaltstack.ss_flags |= SS_ONSTACK;
00090       /* XXX need to set up base of new stack for
00091         per-thread variables, cthreads.  */
00092     }
00093   else
00094     sigsp = (char *) state->basic.SP;
00095 
00096   /* Set up the sigcontext structure on the stack.  This is all the stack
00097      needs, since the args are passed in registers (below).  */
00098   sigsp -= sizeof (*scp);
00099   scp = sigsp;
00100 
00101   if (_hurdsig_catch_memory_fault (scp))
00102     {
00103       /* We got a fault trying to write the stack frame.
00104         We cannot set up the signal handler.
00105         Returning NULL tells our caller, who will nuke us with a SIGILL.  */
00106       return NULL;
00107     }
00108   else
00109     {
00110       /* Set up the sigcontext from the current state of the thread.  */
00111 
00112       scp->sc_onstack = ss->sigaltstack.ss_flags & SS_ONSTACK ? 1 : 0;
00113 
00114       /* struct sigcontext is laid out so that starting at sc_regs
00115         mimics a struct alpha_thread_state.  */
00116       memcpy (&scp->sc_alpha_thread_state,
00117              &state->basic, sizeof (state->basic));
00118 
00119       /* struct sigcontext is laid out so that starting at sc_badvaddr
00120         mimics a struct mips_exc_state.  */
00121       if (! machine_get_state (ss->thread, state, ALPHA_EXC_STATE,
00122                             &state->exc, &scp->sc_alpha_exc_state,
00123                             sizeof (state->exc)))
00124        return NULL;
00125 
00126       if (state->exc.used_fpa &&
00127          /* struct sigcontext is laid out so that starting at sc_fpregs
00128             mimics a struct alpha_float_state.  This state
00129             is only meaningful if the coprocessor was used.  */
00130          ! machine_get_state (ss->thread, state, ALPHA_FLOAT_STATE,
00131                             &state->fpu,
00132                             &scp->sc_alpha_float_state,
00133                             sizeof (state->fpu)))
00134        return NULL;
00135 
00136       _hurdsig_end_catch_fault ();
00137     }
00138 
00139   /* Modify the thread state to call the trampoline code on the new stack.  */
00140   if (rpc_wait)
00141     {
00142       /* The signalee thread was blocked in a mach_msg_trap system call,
00143         still waiting for a reply.  We will have it run the special
00144         trampoline code which retries the message receive before running
00145         the signal handler.
00146 
00147         To do this we change the OPTION argument in its registers to
00148         enable only message reception, since the request message has
00149         already been sent.  */
00150 
00151       /* The system call arguments are stored in consecutive registers
00152         starting with a0 ($16).  */
00153       struct mach_msg_trap_args *args = (void *) &state->basic.r16;
00154 
00155       assert (args->option & MACH_RCV_MSG);
00156       /* Disable the message-send, since it has already completed.  The
00157         calls we retry need only wait to receive the reply message.  */
00158       args->option &= ~MACH_SEND_MSG;
00159 
00160       /* Limit the time to receive the reply message, in case the server
00161         claimed that `interrupt_operation' succeeded but in fact the RPC
00162         is hung.  */
00163       args->option |= MACH_RCV_TIMEOUT;
00164       args->timeout = _hurd_interrupted_rpc_timeout;
00165 
00166       state->basic.pc = (long int) &&rpc_wait_trampoline;
00167       /* After doing the message receive, the trampoline code will need to
00168         update the v0 ($0) value to be restored by sigreturn.  To simplify
00169         the assembly code, we pass the address of its slot in SCP to the
00170         trampoline code in at ($28).  */
00171       state->basic.r28 = (long int) &scp->sc_regs[0];
00172       /* We must preserve the mach_msg_trap args in a0..a5 and t0
00173         ($16..$21, $1).  Pass the handler args to the trampoline code in
00174         t8..t10 ($22.$24).  */
00175       state->basic.r22 = signo;
00176       state->basic.r23 = detail->code;
00177       state->basic.r24 = (long int) scp;
00178     }
00179   else
00180     {
00181       state->basic.pc = (long int) &&trampoline;
00182       state->basic.r16 = signo;
00183       state->basic.r17 = detail->code;
00184       state->basic.r18 = (long int) scp;
00185     }
00186 
00187   state->basic.r30 = (long int) sigsp; /* $30 is the stack pointer.  */
00188 
00189   /* We pass the handler function to the trampoline code in ra ($26).  */
00190   state->basic.r26 = (long int) handler;
00191   /* In the callee-saved register t12/pv ($27), we store the
00192      address of __sigreturn itself, for the trampoline code to use.  */
00193   state->basic.r27 = (long int) &__sigreturn;
00194   /* In the callee-saved register t11/ai ($25), we save the SCP value to pass
00195      to __sigreturn after the handler returns.  */
00196   state->basic.r25 = (long int) scp;
00197 
00198   return scp;
00199 
00200   /* The trampoline code follows.  This is not actually executed as part of
00201      this function, it is just convenient to write it that way.  */
00202 
00203  rpc_wait_trampoline:
00204   /* This is the entry point when we have an RPC reply message to receive
00205      before running the handler.  The MACH_MSG_SEND bit has already been
00206      cleared in the OPTION argument in our registers.  For our convenience,
00207      at ($28) points to the sc_regs[0] member of the sigcontext (saved v0
00208      ($0)).  */
00209   asm volatile
00210     (/* Retry the interrupted mach_msg system call.  */
00211      "lda $0, -25($31)\n"   /* mach_msg_trap */
00212      "callsys\n"            /* Magic system call instruction.  */
00213      /* When the sigcontext was saved, v0 was MACH_RCV_INTERRUPTED.  But
00214        now the message receive has completed and the original caller of
00215        the RPC (i.e. the code running when the signal arrived) needs to
00216        see the final return value of the message receive in v0.  So
00217        store the new v0 value into the sc_regs[0] member of the sigcontext
00218        (whose address is in at to make this code simpler).  */
00219      "stq $0, 0($28)\n"
00220      /* Since the argument registers needed to have the mach_msg_trap
00221        arguments, we've stored the arguments to the handler function
00222        in registers t8..t10 ($22..$24).  */
00223      "mov $22, $16\n"
00224      "mov $23, $17\n"
00225      "mov $24, $18\n");
00226 
00227  trampoline:
00228   /* Entry point for running the handler normally.  The arguments to the
00229      handler function are already in the standard registers:
00230 
00231        a0     SIGNO
00232        a1     SIGCODE
00233        a2     SCP
00234 
00235      t12 also contains SCP; this value is callee-saved (and so should not get
00236      clobbered by running the handler).  We use this saved value to pass to
00237      __sigreturn, so the handler can clobber the argument registers if it
00238      likes.  */
00239   /* Call the handler function, saving return address in ra ($26).  */
00240   asm volatile ("jsr $26, ($26)");
00241   /* Reset gp ($29) from the return address (here) in ra ($26).  */
00242   asm volatile ("ldgp $29, 0($26)");
00243   asm volatile ("mov $25, $16"); /* Move saved SCP to argument register.  */
00244   /* Call __sigreturn (SCP); this cannot return.  */
00245   asm volatile ("jmp $31, ($27)");
00246 
00247   /* NOTREACHED */
00248   return NULL;
00249 }