]>
Commit | Line | Data |
---|---|---|
e607b492 | 1 | /* longjmp cleanup function for unwinding past signal handlers. |
e9915a66 | 2 | Copyright (C) 1995,1996,1997,1998,2005,2006 Free Software Foundation, Inc. |
c84142e8 | 3 | This file is part of the GNU C Library. |
e607b492 | 4 | |
c84142e8 | 5 | The GNU C Library is free software; you can redistribute it and/or |
41bdb6e2 AJ |
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. | |
e607b492 | 9 | |
c84142e8 UD |
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 | |
41bdb6e2 | 13 | Lesser General Public License for more details. |
e607b492 | 14 | |
41bdb6e2 AJ |
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. */ | |
e607b492 RM |
19 | |
20 | #include <hurd.h> | |
8f480b4b | 21 | #include <thread_state.h> |
e9915a66 | 22 | #include <jmpbuf-unwind.h> |
e607b492 | 23 | #include <assert.h> |
d745642e | 24 | #include <stdint.h> |
e607b492 | 25 | |
e607b492 RM |
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) | |
574ce5e4 MB |
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 | } | |
e607b492 RM |
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. */ | |
8f0c527e | 60 | assert (__spin_lock_locked (&ss->critical_section_lock)); |
e607b492 | 61 | /* Are we on the alternate signal stack now? */ |
7ce241a0 | 62 | onstack = (ss->sigaltstack.ss_flags & SS_ONSTACK); |
e607b492 RM |
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 | ||
db169ed5 RM |
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 | ||
e607b492 RM |
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; | |
db169ed5 | 86 | link && _JMPBUF_UNWINDS (env[0].__jmpbuf, link, demangle_ptr); |
e607b492 RM |
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); | |
f9f7fcbe | 123 | assert (link->thread.prevp == &ss->active_resources); |
e607b492 RM |
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); | |
8f0c527e | 134 | __spin_unlock (&ss->critical_section_lock); |
e607b492 RM |
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 | } |