diff options
Diffstat (limited to 'hurd')
-rw-r--r-- | hurd/Makefile | 2 | ||||
-rw-r--r-- | hurd/sigunwind.c | 135 |
2 files changed, 136 insertions, 1 deletions
diff --git a/hurd/Makefile b/hurd/Makefile index 278d8ec315..254e89cb50 100644 --- a/hurd/Makefile +++ b/hurd/Makefile @@ -51,7 +51,7 @@ routines = hurdinit hurdid hurdlookup hurdpid hurdrlimit hurdprio hurdexec \ ports-get ports-set hurdports hurdmsg \ $(sig) $(dtable) hurdinline port-cleanup sig = hurdsig hurdfault faultexc siginfo hurd-raise preempt-sig \ - trampoline longjmp-ts catch-exc exc2signal hurdkill + trampoline longjmp-ts catch-exc exc2signal hurdkill sigunwind dtable = dtable port2fd new-fd alloc-fd intern-fd \ getdport openport \ fd-close fd-read fd-write hurdioctl ctty-input ctty-output diff --git a/hurd/sigunwind.c b/hurd/sigunwind.c new file mode 100644 index 0000000000..7420f43ae4 --- /dev/null +++ b/hurd/sigunwind.c @@ -0,0 +1,135 @@ +/* longjmp cleanup function for unwinding past signal handlers. +Copyright (C) 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include "thread_state.h" +#include <setjmp.h> +#include <assert.h> + +extern void _hurd_longjmp_thread_state (struct machine_thread_state *, + jmp_buf env, int value); + + +/* _hurd_setup_sighandler puts a link on the `active resources' chain so that + _longjmp_unwind will call this function with the `struct sigcontext *' + describing the context interrupted by the signal, when `longjmp' is jumping + to an environment that unwinds past the interrupted frame. */ + +void +_hurdsig_longjmp_from_handler (void *data, jmp_buf env, int val) +{ + struct sigcontext *scp = data; + struct hurd_sigstate *ss = _hurd_self_sigstate (); + int onstack; + inline void cleanup (void) + { + /* Destroy the MiG reply port used by the signal handler, and restore + the reply port in use by the thread when interrupted. */ + mach_port_t *reply_port = + (mach_port_t *) __hurd_threadvar_location (_HURD_THREADVAR_MIG_REPLY); + if (*reply_port) + __mach_port_destroy (__mach_task_self (), *reply_port); + *reply_port = scp->sc_reply_port; + } + + __spin_lock (&ss->lock); + /* We should only ever be called from _longjmp_unwind (in jmp-unwind.c), + which calls us inside a critical section. */ + assert (ss->critical_section); + /* Are we on the alternate signal stack now? */ + onstack = (ss->sigaltstack.ss_flags & SA_ONSTACK); + __spin_unlock (&ss->lock); + + if (onstack && ! scp->sc_onstack) + { + /* We are unwinding off the signal stack. We must use sigreturn to + do it robustly. Mutate the sigcontext so that when sigreturn + resumes from that context, it will be as if `__longjmp (ENV, VAL)' + were done. */ + + struct hurd_userlink *link; + + /* Continue _longjmp_unwind's job of running the unwind + forms for frames being unwound, since we will not + return to its loop like this one, which called us. */ + for (link = ss->active_resources; + link && _JMPBUF_UNWINDS (env[0].__jmpbuf, link); + link = link->thread.next) + if (_hurd_userlink_unlink (link)) + { + if (link->cleanup == &_hurdsig_longjmp_from_handler) + { + /* We are unwinding past another signal handler invocation. + Just finish the cleanup for this (inner) one, and then + swap SCP to restore to the outer context. */ + cleanup (); + scp = link->cleanup_data; + } + else + (*link->cleanup) (link->cleanup_data, env, val); + } + +#define sc_machine_thread_state paste(sc_,machine_thread_state) +#define paste(a,b) paste1(a,b) +#define paste1(a,b) a##b + + /* There are no more unwind forms to be run! + Now we can just have the sigreturn do the longjmp for us. */ + _hurd_longjmp_thread_state + ((struct machine_thread_state *) &scp->sc_machine_thread_state, + env, val); + + /* Restore to the same current signal mask. If sigsetjmp saved the + mask, longjmp has already restored it as desired; if not, we + should leave it as it is. */ + scp->sc_mask = ss->blocked; + + /* sigreturn expects the link added by _hurd_setup_sighandler + to still be there, but _longjmp_unwind removed it just before + calling us. Put it back now so sigreturn can find it. */ + link = (void *) &scp[1]; + assert (! link->resource.next && ! link->resource.prevp); + assert (link->thread.next == ss->active_resources); + assert (link->thread.prevp = &ss->active_resources); + if (link->thread.next) + link->thread.next->thread.prevp = &link->thread.next; + ss->active_resources = link; + + /* We must momentarily exit the critical section so that sigreturn + does not get upset with us. But we don't want signal handlers + running right now, because we are presently in the bogus state of + having run all the unwind forms back to ENV's frame, but our SP is + still inside those unwound frames. */ + __spin_lock (&ss->lock); + ss->critical_section = 0; + ss->blocked = ~(sigset_t) 0 & ~_SIG_CANT_MASK; + __spin_unlock (&ss->lock); + + /* Restore to the modified signal context that now + performs `longjmp (ENV, VAL)'. */ + __sigreturn (scp); + assert (! "sigreturn returned!"); + } + + /* We are not unwinding off the alternate signal stack. So nothing + really funny is going on here. We can just clean up this handler + frame and let _longjmp_unwind continue unwinding. */ + cleanup (); + ss->intr_port = scp->sc_intr_port; +} |