Back to index

glibc  2.9
unwind.c
Go to the documentation of this file.
00001 /* Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Ulrich Drepper <drepper@redhat.com>
00004    and Richard Henderson <rth@redhat.com>, 2003.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <setjmp.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <unistd.h>
00025 #include "pthreadP.h"
00026 #include <jmpbuf-unwind.h>
00027 
00028 #ifdef HAVE_FORCED_UNWIND
00029 
00030 #ifdef _STACK_GROWS_DOWN
00031 # define FRAME_LEFT(frame, other, adj) \
00032   ((uintptr_t) frame - adj >= (uintptr_t) other - adj)
00033 #elif _STACK_GROWS_UP
00034 # define FRAME_LEFT(frame, other, adj) \
00035   ((uintptr_t) frame - adj <= (uintptr_t) other - adj)
00036 #else
00037 # error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
00038 #endif
00039 
00040 static _Unwind_Reason_Code
00041 unwind_stop (int version, _Unwind_Action actions,
00042             _Unwind_Exception_Class exc_class,
00043             struct _Unwind_Exception *exc_obj,
00044             struct _Unwind_Context *context, void *stop_parameter)
00045 {
00046   struct pthread_unwind_buf *buf = stop_parameter;
00047   struct pthread *self = THREAD_SELF;
00048   struct _pthread_cleanup_buffer *curp = THREAD_GETMEM (self, cleanup);
00049   int do_longjump = 0;
00050 
00051   /* Adjust all pointers used in comparisons, so that top of thread's
00052      stack is at the top of address space.  Without that, things break
00053      if stack is allocated above the main stack.  */
00054   uintptr_t adj = (uintptr_t) self->stackblock + self->stackblock_size;
00055 
00056   /* Do longjmp if we're at "end of stack", aka "end of unwind data".
00057      We assume there are only C frame without unwind data in between
00058      here and the jmp_buf target.  Otherwise simply note that the CFA
00059      of a function is NOT within it's stack frame; it's the SP of the
00060      previous frame.  */
00061   if ((actions & _UA_END_OF_STACK)
00062       || ! _JMPBUF_CFA_UNWINDS_ADJ (buf->cancel_jmp_buf[0].jmp_buf, context,
00063                                 adj))
00064     do_longjump = 1;
00065 
00066   if (__builtin_expect (curp != NULL, 0))
00067     {
00068       /* Handle the compatibility stuff.  Execute all handlers
00069         registered with the old method which would be unwound by this
00070         step.  */
00071       struct _pthread_cleanup_buffer *oldp = buf->priv.data.cleanup;
00072       void *cfa = (void *) _Unwind_GetCFA (context);
00073 
00074       if (curp != oldp && (do_longjump || FRAME_LEFT (cfa, curp, adj)))
00075        {
00076          do
00077            {
00078              /* Pointer to the next element.  */
00079              struct _pthread_cleanup_buffer *nextp = curp->__prev;
00080 
00081              /* Call the handler.  */
00082              curp->__routine (curp->__arg);
00083 
00084              /* To the next.  */
00085              curp = nextp;
00086            }
00087          while (curp != oldp
00088                && (do_longjump || FRAME_LEFT (cfa, curp, adj)));
00089 
00090          /* Mark the current element as handled.  */
00091          THREAD_SETMEM (self, cleanup, curp);
00092        }
00093     }
00094 
00095   if (do_longjump)
00096     __libc_unwind_longjmp ((struct __jmp_buf_tag *) buf->cancel_jmp_buf, 1);
00097 
00098   return _URC_NO_REASON;
00099 }
00100 
00101 
00102 static void
00103 unwind_cleanup (_Unwind_Reason_Code reason, struct _Unwind_Exception *exc)
00104 {
00105   /* When we get here a C++ catch block didn't rethrow the object.  We
00106      cannot handle this case and therefore abort.  */
00107 # define STR_N_LEN(str) str, strlen (str)
00108   INTERNAL_SYSCALL_DECL (err);
00109   INTERNAL_SYSCALL (write, err, 3, STDERR_FILENO,
00110                   STR_N_LEN ("FATAL: exception not rethrown\n"));
00111   abort ();
00112 }
00113 
00114 #endif /* have forced unwind */
00115 
00116 
00117 void
00118 __cleanup_fct_attribute __attribute ((noreturn))
00119 __pthread_unwind (__pthread_unwind_buf_t *buf)
00120 {
00121   struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf;
00122   struct pthread *self = THREAD_SELF;
00123 
00124 #ifdef HAVE_FORCED_UNWIND
00125   /* This is not a catchable exception, so don't provide any details about
00126      the exception type.  We do need to initialize the field though.  */
00127   THREAD_SETMEM (self, exc.exception_class, 0);
00128   THREAD_SETMEM (self, exc.exception_cleanup, unwind_cleanup);
00129 
00130   _Unwind_ForcedUnwind (&self->exc, unwind_stop, ibuf);
00131 #else
00132   /* Handle the compatibility stuff first.  Execute all handlers
00133      registered with the old method.  We don't execute them in order,
00134      instead, they will run first.  */
00135   struct _pthread_cleanup_buffer *oldp = ibuf->priv.data.cleanup;
00136   struct _pthread_cleanup_buffer *curp = THREAD_GETMEM (self, cleanup);
00137 
00138   if (curp != oldp)
00139     {
00140       do
00141        {
00142          /* Pointer to the next element.  */
00143          struct _pthread_cleanup_buffer *nextp = curp->__prev;
00144 
00145          /* Call the handler.  */
00146          curp->__routine (curp->__arg);
00147 
00148          /* To the next.  */
00149          curp = nextp;
00150        }
00151       while (curp != oldp);
00152 
00153       /* Mark the current element as handled.  */
00154       THREAD_SETMEM (self, cleanup, curp);
00155     }
00156 
00157   /* We simply jump to the registered setjmp buffer.  */
00158   __libc_unwind_longjmp ((struct __jmp_buf_tag *) ibuf->cancel_jmp_buf, 1);
00159 #endif
00160   /* NOTREACHED */
00161 
00162   /* We better do not get here.  */
00163   abort ();
00164 }
00165 hidden_def (__pthread_unwind)
00166 
00167 
00168 void
00169 __cleanup_fct_attribute __attribute ((noreturn))
00170 __pthread_unwind_next (__pthread_unwind_buf_t *buf)
00171 {
00172   struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf;
00173 
00174   __pthread_unwind ((__pthread_unwind_buf_t *) ibuf->priv.data.prev);
00175 }
00176 hidden_def (__pthread_unwind_next)