(root)/
glibc-2.38/
nptl/
unwind.c
       1  /* Copyright (C) 2003-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <setjmp.h>
      19  #include <stdio.h>
      20  #include <stdlib.h>
      21  #include <string.h>
      22  #include <unistd.h>
      23  #include "pthreadP.h"
      24  #include <libc-diag.h>
      25  #include <jmpbuf-unwind.h>
      26  #include <shlib-compat.h>
      27  
      28  #if _STACK_GROWS_DOWN
      29  # define FRAME_LEFT(frame, other, adj) \
      30    ((uintptr_t) frame - adj >= (uintptr_t) other - adj)
      31  #elif _STACK_GROWS_UP
      32  # define FRAME_LEFT(frame, other, adj) \
      33    ((uintptr_t) frame - adj <= (uintptr_t) other - adj)
      34  #else
      35  # error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
      36  #endif
      37  
      38  static _Unwind_Reason_Code
      39  unwind_stop (int version, _Unwind_Action actions,
      40  	     _Unwind_Exception_Class exc_class,
      41  	     struct _Unwind_Exception *exc_obj,
      42  	     struct _Unwind_Context *context, void *stop_parameter)
      43  {
      44    struct pthread_unwind_buf *buf = stop_parameter;
      45    struct pthread *self = THREAD_SELF;
      46    struct _pthread_cleanup_buffer *curp = THREAD_GETMEM (self, cleanup);
      47    int do_longjump = 0;
      48  
      49    /* Adjust all pointers used in comparisons, so that top of thread's
      50       stack is at the top of address space.  Without that, things break
      51       if stack is allocated above the main stack.  */
      52    uintptr_t adj = (uintptr_t) self->stackblock + self->stackblock_size;
      53  
      54    /* Do longjmp if we're at "end of stack", aka "end of unwind data".
      55       We assume there are only C frame without unwind data in between
      56       here and the jmp_buf target.  Otherwise simply note that the CFA
      57       of a function is NOT within it's stack frame; it's the SP of the
      58       previous frame.  */
      59    if ((actions & _UA_END_OF_STACK)
      60        || ! _JMPBUF_CFA_UNWINDS_ADJ (buf->cancel_jmp_buf[0].jmp_buf, context,
      61  				    adj))
      62      do_longjump = 1;
      63  
      64    if (__glibc_unlikely (curp != NULL))
      65      {
      66        /* Handle the compatibility stuff.  Execute all handlers
      67  	 registered with the old method which would be unwound by this
      68  	 step.  */
      69        struct _pthread_cleanup_buffer *oldp = buf->priv.data.cleanup;
      70        void *cfa = (void *) (_Unwind_Ptr) _Unwind_GetCFA (context);
      71  
      72        if (curp != oldp && (do_longjump || FRAME_LEFT (cfa, curp, adj)))
      73  	{
      74  	  do
      75  	    {
      76  	      /* Pointer to the next element.  */
      77  	      struct _pthread_cleanup_buffer *nextp = curp->__prev;
      78  
      79  	      /* Call the handler.  */
      80  	      curp->__routine (curp->__arg);
      81  
      82  	      /* To the next.  */
      83  	      curp = nextp;
      84  	    }
      85  	  while (curp != oldp
      86  		 && (do_longjump || FRAME_LEFT (cfa, curp, adj)));
      87  
      88  	  /* Mark the current element as handled.  */
      89  	  THREAD_SETMEM (self, cleanup, curp);
      90  	}
      91      }
      92  
      93    DIAG_PUSH_NEEDS_COMMENT;
      94  #if __GNUC_PREREQ (7, 0)
      95    /* This call results in a -Wstringop-overflow warning because struct
      96       pthread_unwind_buf is smaller than jmp_buf.  setjmp and longjmp
      97       do not use anything beyond the common prefix (they never access
      98       the saved signal mask), so that is a false positive.  */
      99    DIAG_IGNORE_NEEDS_COMMENT (11, "-Wstringop-overflow=");
     100  #endif
     101    if (do_longjump)
     102      __libc_unwind_longjmp ((struct __jmp_buf_tag *) buf->cancel_jmp_buf, 1);
     103    DIAG_POP_NEEDS_COMMENT;
     104  
     105    return _URC_NO_REASON;
     106  }
     107  
     108  
     109  static void
     110  unwind_cleanup (_Unwind_Reason_Code reason, struct _Unwind_Exception *exc)
     111  {
     112    /* When we get here a C++ catch block didn't rethrow the object.  We
     113       cannot handle this case and therefore abort.  */
     114    __libc_fatal ("FATAL: exception not rethrown\n");
     115  }
     116  
     117  
     118  void
     119  __cleanup_fct_attribute __attribute ((noreturn))
     120  __pthread_unwind (__pthread_unwind_buf_t *buf)
     121  {
     122    struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf;
     123    struct pthread *self = THREAD_SELF;
     124  
     125    /* This is not a catchable exception, so don't provide any details about
     126       the exception type.  We do need to initialize the field though.  */
     127    THREAD_SETMEM (self, exc.exception_class, 0);
     128    THREAD_SETMEM (self, exc.exception_cleanup, &unwind_cleanup);
     129  
     130    _Unwind_ForcedUnwind (&self->exc, unwind_stop, ibuf);
     131    /* NOTREACHED */
     132  
     133    /* We better do not get here.  */
     134    abort ();
     135  }
     136  libc_hidden_def (__pthread_unwind)
     137  
     138  void
     139  __cleanup_fct_attribute __attribute ((noreturn))
     140  ___pthread_unwind_next (__pthread_unwind_buf_t *buf)
     141  {
     142    struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf;
     143  
     144    __pthread_unwind ((__pthread_unwind_buf_t *) ibuf->priv.data.prev);
     145  }
     146  versioned_symbol (libc, ___pthread_unwind_next, __pthread_unwind_next,
     147  		  GLIBC_2_34);
     148  #if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_3_3, GLIBC_2_34)
     149  compat_symbol (libpthread, ___pthread_unwind_next, __pthread_unwind_next,
     150  	       GLIBC_2_3_3);
     151  #endif