Back to index

glibc  2.9
sigunwind.c
Go to the documentation of this file.
00001 /* longjmp cleanup function for unwinding past signal handlers.
00002    Copyright (C) 1995,1996,1997,1998,2005,2006 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.h>
00021 #include <thread_state.h>
00022 #include <jmpbuf-unwind.h>
00023 #include <assert.h>
00024 #include <stdint.h>
00025 
00026 
00027 /* _hurd_setup_sighandler puts a link on the `active resources' chain so that
00028    _longjmp_unwind will call this function with the `struct sigcontext *'
00029    describing the context interrupted by the signal, when `longjmp' is jumping
00030    to an environment that unwinds past the interrupted frame.  */
00031 
00032 void
00033 _hurdsig_longjmp_from_handler (void *data, jmp_buf env, int val)
00034 {
00035   struct sigcontext *scp = data;
00036   struct hurd_sigstate *ss = _hurd_self_sigstate ();
00037   int onstack;
00038   inline void cleanup (void)
00039     {
00040       /* Destroy the MiG reply port used by the signal handler, and restore
00041         the reply port in use by the thread when interrupted.  */
00042       mach_port_t *reply_port =
00043        (mach_port_t *) __hurd_threadvar_location (_HURD_THREADVAR_MIG_REPLY);
00044       if (*reply_port)
00045        {
00046          mach_port_t port = *reply_port;
00047          /* Assigning MACH_PORT_DEAD here tells libc's mig_get_reply_port
00048             not to get another reply port, but avoids mig_dealloc_reply_port
00049             trying to deallocate it after the receive fails (which it will,
00050             because the reply port will be bogus, regardless).  */
00051          *reply_port = MACH_PORT_DEAD;
00052          __mach_port_destroy (__mach_task_self (), port);
00053        }
00054       *reply_port = scp->sc_reply_port;
00055     }
00056 
00057   __spin_lock (&ss->lock);
00058   /* We should only ever be called from _longjmp_unwind (in jmp-unwind.c),
00059      which calls us inside a critical section.  */
00060   assert (__spin_lock_locked (&ss->critical_section_lock));
00061   /* Are we on the alternate signal stack now?  */
00062   onstack = (ss->sigaltstack.ss_flags & SS_ONSTACK);
00063   __spin_unlock (&ss->lock);
00064 
00065   if (onstack && ! scp->sc_onstack)
00066     {
00067       /* We are unwinding off the signal stack.  We must use sigreturn to
00068         do it robustly.  Mutate the sigcontext so that when sigreturn
00069         resumes from that context, it will be as if `__longjmp (ENV, VAL)'
00070         were done.  */
00071 
00072       struct hurd_userlink *link;
00073 
00074       inline uintptr_t demangle_ptr (uintptr_t x)
00075        {
00076 # ifdef PTR_DEMANGLE
00077          PTR_DEMANGLE (x);
00078 # endif
00079          return x;
00080        }
00081 
00082       /* Continue _longjmp_unwind's job of running the unwind
00083         forms for frames being unwound, since we will not
00084         return to its loop like this one, which called us.  */
00085       for (link = ss->active_resources;
00086           link && _JMPBUF_UNWINDS (env[0].__jmpbuf, link, demangle_ptr);
00087           link = link->thread.next)
00088        if (_hurd_userlink_unlink (link))
00089          {
00090            if (link->cleanup == &_hurdsig_longjmp_from_handler)
00091              {
00092               /* We are unwinding past another signal handler invocation.
00093                  Just finish the cleanup for this (inner) one, and then
00094                  swap SCP to restore to the outer context.  */
00095               cleanup ();
00096               scp = link->cleanup_data;
00097              }
00098            else
00099              (*link->cleanup) (link->cleanup_data, env, val);
00100          }
00101 
00102 #define sc_machine_thread_state paste(sc_,machine_thread_state)
00103 #define paste(a,b)   paste1(a,b)
00104 #define paste1(a,b)  a##b
00105 
00106       /* There are no more unwind forms to be run!
00107         Now we can just have the sigreturn do the longjmp for us.  */
00108       _hurd_longjmp_thread_state
00109        ((struct machine_thread_state *) &scp->sc_machine_thread_state,
00110         env, val);
00111 
00112       /* Restore to the same current signal mask.  If sigsetjmp saved the
00113         mask, longjmp has already restored it as desired; if not, we
00114         should leave it as it is.  */
00115       scp->sc_mask = ss->blocked;
00116 
00117       /* sigreturn expects the link added by _hurd_setup_sighandler
00118         to still be there, but _longjmp_unwind removed it just before
00119         calling us.  Put it back now so sigreturn can find it.  */
00120       link = (void *) &scp[1];
00121       assert (! link->resource.next && ! link->resource.prevp);
00122       assert (link->thread.next == ss->active_resources);
00123       assert (link->thread.prevp == &ss->active_resources);
00124       if (link->thread.next)
00125        link->thread.next->thread.prevp = &link->thread.next;
00126       ss->active_resources = link;
00127 
00128       /* We must momentarily exit the critical section so that sigreturn
00129         does not get upset with us.  But we don't want signal handlers
00130         running right now, because we are presently in the bogus state of
00131         having run all the unwind forms back to ENV's frame, but our SP is
00132         still inside those unwound frames.  */
00133       __spin_lock (&ss->lock);
00134       __spin_unlock (&ss->critical_section_lock);
00135       ss->blocked = ~(sigset_t) 0 & ~_SIG_CANT_MASK;
00136       __spin_unlock (&ss->lock);
00137 
00138       /* Restore to the modified signal context that now
00139         performs `longjmp (ENV, VAL)'.  */
00140       __sigreturn (scp);
00141       assert (! "sigreturn returned!");
00142     }
00143 
00144   /* We are not unwinding off the alternate signal stack.  So nothing
00145      really funny is going on here.  We can just clean up this handler
00146      frame and let _longjmp_unwind continue unwinding.  */
00147   cleanup ();
00148   ss->intr_port = scp->sc_intr_port;
00149 }