2005-12-28 Roland McGrath <roland@redhat.com>
[kopensolaris-gnu/glibc.git] / hurd / sigunwind.c
1 /* longjmp cleanup function for unwinding past signal handlers.
2    Copyright (C) 1995, 1996, 1997, 1998, 2005 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <hurd.h>
21 #include <thread_state.h>
22 #include <setjmp.h>
23 #include <assert.h>
24 #include <stdint.h>
25
26
27 /* _hurd_setup_sighandler puts a link on the `active resources' chain so that
28    _longjmp_unwind will call this function with the `struct sigcontext *'
29    describing the context interrupted by the signal, when `longjmp' is jumping
30    to an environment that unwinds past the interrupted frame.  */
31
32 void
33 _hurdsig_longjmp_from_handler (void *data, jmp_buf env, int val)
34 {
35   struct sigcontext *scp = data;
36   struct hurd_sigstate *ss = _hurd_self_sigstate ();
37   int onstack;
38   inline void cleanup (void)
39     {
40       /* Destroy the MiG reply port used by the signal handler, and restore
41          the reply port in use by the thread when interrupted.  */
42       mach_port_t *reply_port =
43         (mach_port_t *) __hurd_threadvar_location (_HURD_THREADVAR_MIG_REPLY);
44       if (*reply_port)
45         {
46           mach_port_t port = *reply_port;
47           /* Assigning MACH_PORT_DEAD here tells libc's mig_get_reply_port
48              not to get another reply port, but avoids mig_dealloc_reply_port
49              trying to deallocate it after the receive fails (which it will,
50              because the reply port will be bogus, regardless).  */
51           *reply_port = MACH_PORT_DEAD;
52           __mach_port_destroy (__mach_task_self (), port);
53         }
54       *reply_port = scp->sc_reply_port;
55     }
56
57   __spin_lock (&ss->lock);
58   /* We should only ever be called from _longjmp_unwind (in jmp-unwind.c),
59      which calls us inside a critical section.  */
60   assert (__spin_lock_locked (&ss->critical_section_lock));
61   /* Are we on the alternate signal stack now?  */
62   onstack = (ss->sigaltstack.ss_flags & SS_ONSTACK);
63   __spin_unlock (&ss->lock);
64
65   if (onstack && ! scp->sc_onstack)
66     {
67       /* We are unwinding off the signal stack.  We must use sigreturn to
68          do it robustly.  Mutate the sigcontext so that when sigreturn
69          resumes from that context, it will be as if `__longjmp (ENV, VAL)'
70          were done.  */
71
72       struct hurd_userlink *link;
73
74       inline uintptr_t demangle_ptr (uintptr_t x)
75         {
76 # ifdef PTR_DEMANGLE
77           PTR_DEMANGLE (x);
78 # endif
79           return x;
80         }
81
82       /* Continue _longjmp_unwind's job of running the unwind
83          forms for frames being unwound, since we will not
84          return to its loop like this one, which called us.  */
85       for (link = ss->active_resources;
86            link && _JMPBUF_UNWINDS (env[0].__jmpbuf, link, demangle_ptr);
87            link = link->thread.next)
88         if (_hurd_userlink_unlink (link))
89           {
90             if (link->cleanup == &_hurdsig_longjmp_from_handler)
91               {
92                 /* We are unwinding past another signal handler invocation.
93                    Just finish the cleanup for this (inner) one, and then
94                    swap SCP to restore to the outer context.  */
95                 cleanup ();
96                 scp = link->cleanup_data;
97               }
98             else
99               (*link->cleanup) (link->cleanup_data, env, val);
100           }
101
102 #define sc_machine_thread_state paste(sc_,machine_thread_state)
103 #define paste(a,b)      paste1(a,b)
104 #define paste1(a,b)     a##b
105
106       /* There are no more unwind forms to be run!
107          Now we can just have the sigreturn do the longjmp for us.  */
108       _hurd_longjmp_thread_state
109         ((struct machine_thread_state *) &scp->sc_machine_thread_state,
110          env, val);
111
112       /* Restore to the same current signal mask.  If sigsetjmp saved the
113          mask, longjmp has already restored it as desired; if not, we
114          should leave it as it is.  */
115       scp->sc_mask = ss->blocked;
116
117       /* sigreturn expects the link added by _hurd_setup_sighandler
118          to still be there, but _longjmp_unwind removed it just before
119          calling us.  Put it back now so sigreturn can find it.  */
120       link = (void *) &scp[1];
121       assert (! link->resource.next && ! link->resource.prevp);
122       assert (link->thread.next == ss->active_resources);
123       assert (link->thread.prevp == &ss->active_resources);
124       if (link->thread.next)
125         link->thread.next->thread.prevp = &link->thread.next;
126       ss->active_resources = link;
127
128       /* We must momentarily exit the critical section so that sigreturn
129          does not get upset with us.  But we don't want signal handlers
130          running right now, because we are presently in the bogus state of
131          having run all the unwind forms back to ENV's frame, but our SP is
132          still inside those unwound frames.  */
133       __spin_lock (&ss->lock);
134       __spin_unlock (&ss->critical_section_lock);
135       ss->blocked = ~(sigset_t) 0 & ~_SIG_CANT_MASK;
136       __spin_unlock (&ss->lock);
137
138       /* Restore to the modified signal context that now
139          performs `longjmp (ENV, VAL)'.  */
140       __sigreturn (scp);
141       assert (! "sigreturn returned!");
142     }
143
144   /* We are not unwinding off the alternate signal stack.  So nothing
145      really funny is going on here.  We can just clean up this handler
146      frame and let _longjmp_unwind continue unwinding.  */
147   cleanup ();
148   ss->intr_port = scp->sc_intr_port;
149 }