| /* Copyright (C) 2003-2014 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| Contributed by Ulrich Drepper <drepper@redhat.com> |
| and Richard Henderson <rth@redhat.com>, 2003. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 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 |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <setjmp.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include "pthreadP.h" |
| #include <jmpbuf-unwind.h> |
| |
| #ifdef HAVE_FORCED_UNWIND |
| |
| #ifdef _STACK_GROWS_DOWN |
| # define FRAME_LEFT(frame, other, adj) \ |
| ((uintptr_t) frame - adj >= (uintptr_t) other - adj) |
| #elif _STACK_GROWS_UP |
| # define FRAME_LEFT(frame, other, adj) \ |
| ((uintptr_t) frame - adj <= (uintptr_t) other - adj) |
| #else |
| # error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP" |
| #endif |
| |
| static _Unwind_Reason_Code |
| unwind_stop (int version, _Unwind_Action actions, |
| _Unwind_Exception_Class exc_class, |
| struct _Unwind_Exception *exc_obj, |
| struct _Unwind_Context *context, void *stop_parameter) |
| { |
| struct pthread_unwind_buf *buf = stop_parameter; |
| struct pthread *self = THREAD_SELF; |
| struct _pthread_cleanup_buffer *curp = THREAD_GETMEM (self, cleanup); |
| int do_longjump = 0; |
| |
| /* Adjust all pointers used in comparisons, so that top of thread's |
| stack is at the top of address space. Without that, things break |
| if stack is allocated above the main stack. */ |
| uintptr_t adj = (uintptr_t) self->stackblock + self->stackblock_size; |
| |
| /* Do longjmp if we're at "end of stack", aka "end of unwind data". |
| We assume there are only C frame without unwind data in between |
| here and the jmp_buf target. Otherwise simply note that the CFA |
| of a function is NOT within it's stack frame; it's the SP of the |
| previous frame. */ |
| if ((actions & _UA_END_OF_STACK) |
| || ! _JMPBUF_CFA_UNWINDS_ADJ (buf->cancel_jmp_buf[0].jmp_buf, context, |
| adj)) |
| do_longjump = 1; |
| |
| if (__builtin_expect (curp != NULL, 0)) |
| { |
| /* Handle the compatibility stuff. Execute all handlers |
| registered with the old method which would be unwound by this |
| step. */ |
| struct _pthread_cleanup_buffer *oldp = buf->priv.data.cleanup; |
| void *cfa = (void *) (_Unwind_Ptr) _Unwind_GetCFA (context); |
| |
| if (curp != oldp && (do_longjump || FRAME_LEFT (cfa, curp, adj))) |
| { |
| do |
| { |
| /* Pointer to the next element. */ |
| struct _pthread_cleanup_buffer *nextp = curp->__prev; |
| |
| /* Call the handler. */ |
| curp->__routine (curp->__arg); |
| |
| /* To the next. */ |
| curp = nextp; |
| } |
| while (curp != oldp |
| && (do_longjump || FRAME_LEFT (cfa, curp, adj))); |
| |
| /* Mark the current element as handled. */ |
| THREAD_SETMEM (self, cleanup, curp); |
| } |
| } |
| |
| if (do_longjump) |
| __libc_unwind_longjmp ((struct __jmp_buf_tag *) buf->cancel_jmp_buf, 1); |
| |
| return _URC_NO_REASON; |
| } |
| |
| |
| static void |
| unwind_cleanup (_Unwind_Reason_Code reason, struct _Unwind_Exception *exc) |
| { |
| /* When we get here a C++ catch block didn't rethrow the object. We |
| cannot handle this case and therefore abort. */ |
| # define STR_N_LEN(str) str, strlen (str) |
| INTERNAL_SYSCALL_DECL (err); |
| INTERNAL_SYSCALL (write, err, 3, STDERR_FILENO, |
| STR_N_LEN ("FATAL: exception not rethrown\n")); |
| abort (); |
| } |
| |
| #endif /* have forced unwind */ |
| |
| |
| void |
| __cleanup_fct_attribute __attribute ((noreturn)) |
| __pthread_unwind (__pthread_unwind_buf_t *buf) |
| { |
| struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf; |
| struct pthread *self = THREAD_SELF; |
| |
| #ifdef HAVE_FORCED_UNWIND |
| /* This is not a catchable exception, so don't provide any details about |
| the exception type. We do need to initialize the field though. */ |
| THREAD_SETMEM (self, exc.exception_class, 0); |
| THREAD_SETMEM (self, exc.exception_cleanup, &unwind_cleanup); |
| |
| _Unwind_ForcedUnwind (&self->exc, unwind_stop, ibuf); |
| #else |
| /* Handle the compatibility stuff first. Execute all handlers |
| registered with the old method. We don't execute them in order, |
| instead, they will run first. */ |
| struct _pthread_cleanup_buffer *oldp = ibuf->priv.data.cleanup; |
| struct _pthread_cleanup_buffer *curp = THREAD_GETMEM (self, cleanup); |
| |
| if (curp != oldp) |
| { |
| do |
| { |
| /* Pointer to the next element. */ |
| struct _pthread_cleanup_buffer *nextp = curp->__prev; |
| |
| /* Call the handler. */ |
| curp->__routine (curp->__arg); |
| |
| /* To the next. */ |
| curp = nextp; |
| } |
| while (curp != oldp); |
| |
| /* Mark the current element as handled. */ |
| THREAD_SETMEM (self, cleanup, curp); |
| } |
| |
| /* We simply jump to the registered setjmp buffer. */ |
| __libc_unwind_longjmp ((struct __jmp_buf_tag *) ibuf->cancel_jmp_buf, 1); |
| #endif |
| /* NOTREACHED */ |
| |
| /* We better do not get here. */ |
| abort (); |
| } |
| hidden_def (__pthread_unwind) |
| |
| |
| void |
| __cleanup_fct_attribute __attribute ((noreturn)) |
| __pthread_unwind_next (__pthread_unwind_buf_t *buf) |
| { |
| struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf; |
| |
| __pthread_unwind ((__pthread_unwind_buf_t *) ibuf->priv.data.prev); |
| } |
| hidden_def (__pthread_unwind_next) |