(root)/
glibc-2.38/
elf/
dl-catch.c
       1  /* Exception handling in the dynamic linker.
       2     Copyright (C) 1995-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       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.
       9  
      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
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <libintl.h>
      20  #include <setjmp.h>
      21  #include <stdbool.h>
      22  #include <stdlib.h>
      23  #include <string.h>
      24  #include <unistd.h>
      25  #include <ldsodefs.h>
      26  #include <stdio.h>
      27  #include <tls.h>
      28  
      29  /* This structure communicates state between _dl_catch_error and
      30     _dl_signal_error.  */
      31  struct rtld_catch
      32    {
      33      struct dl_exception *exception; /* The exception data is stored there.  */
      34      volatile int *errcode;	/* Return value of _dl_signal_error.  */
      35      jmp_buf env;		/* longjmp here on error.  */
      36    };
      37  
      38  /* Multiple threads at once can use the `_dl_catch_error' function.
      39     The calls can come from `_dl_map_object_deps', `_dlerror_run', or
      40     from any of the libc functionality which loads dynamic objects
      41     (NSS, iconv).  Therefore we have to be prepared to save the state
      42     in thread-local memory.  We use THREAD_GETMEM and THREAD_SETMEM
      43     instead of ELF TLS because ELF TLS is not available in the dynamic
      44     loader.  Additionally, the exception handling mechanism must be
      45     usable before the TCB has been set up, which is why
      46     rtld_catch_notls is used if !__rtld_tls_init_tp_called.  This is
      47     not needed for static builds, where initialization completes before
      48     static dlopen etc. can be called.  */
      49  
      50  #if IS_IN (rtld)
      51  static struct rtld_catch *rtld_catch_notls;
      52  #endif
      53  
      54  static struct rtld_catch *
      55  get_catch (void)
      56  {
      57  #if IS_IN (rtld)
      58    if (!__rtld_tls_init_tp_called)
      59      return rtld_catch_notls;
      60    else
      61  #endif
      62      return THREAD_GETMEM (THREAD_SELF, rtld_catch);
      63  }
      64  
      65  static void
      66  set_catch (struct rtld_catch *catch)
      67  {
      68  #if IS_IN (rtld)
      69    if (!__rtld_tls_init_tp_called)
      70      rtld_catch_notls = catch;
      71    else
      72  #endif
      73      THREAD_SETMEM (THREAD_SELF, rtld_catch, catch);
      74  }
      75  
      76  /* Lossage while resolving the program's own symbols is always fatal.  */
      77  static void
      78  __attribute__ ((noreturn))
      79  fatal_error (int errcode, const char *objname, const char *occasion,
      80  	     const char *errstring)
      81  {
      82    char buffer[1024];
      83    _dl_fatal_printf ("%s: %s: %s%s%s%s%s\n",
      84  		    RTLD_PROGNAME,
      85  		    occasion ?: N_("error while loading shared libraries"),
      86  		    objname, *objname ? ": " : "",
      87  		    errstring, errcode ? ": " : "",
      88  		    (errcode
      89  		     ? __strerror_r (errcode, buffer, sizeof buffer)
      90  		     : ""));
      91  }
      92  
      93  void
      94  _dl_signal_exception (int errcode, struct dl_exception *exception,
      95  		      const char *occasion)
      96  {
      97    struct rtld_catch *lcatch = get_catch ();
      98    if (lcatch != NULL)
      99      {
     100        *lcatch->exception = *exception;
     101        *lcatch->errcode = errcode;
     102  
     103        /* We do not restore the signal mask because none was saved.  */
     104        __longjmp (lcatch->env[0].__jmpbuf, 1);
     105      }
     106    else
     107      fatal_error (errcode, exception->objname, occasion, exception->errstring);
     108  }
     109  rtld_hidden_def (_dl_signal_exception)
     110  
     111  void
     112  _dl_signal_error (int errcode, const char *objname, const char *occasion,
     113  		  const char *errstring)
     114  {
     115    struct rtld_catch *lcatch = get_catch ();
     116  
     117    if (! errstring)
     118      errstring = N_("DYNAMIC LINKER BUG!!!");
     119  
     120    if (lcatch != NULL)
     121      {
     122        _dl_exception_create (lcatch->exception, objname, errstring);
     123        *lcatch->errcode = errcode;
     124  
     125        /* We do not restore the signal mask because none was saved.  */
     126        __longjmp (lcatch->env[0].__jmpbuf, 1);
     127      }
     128    else
     129      fatal_error (errcode, objname, occasion, errstring);
     130  }
     131  rtld_hidden_def (_dl_signal_error)
     132  
     133  #if IS_IN (rtld)
     134  /* This points to a function which is called when a continuable error is
     135     received.  Unlike the handling of `catch' this function may return.
     136     The arguments will be the `errstring' and `objname'.
     137  
     138     Since this functionality is not used in normal programs (only in ld.so)
     139     we do not care about multi-threaded programs here.  We keep this as a
     140     global variable.  */
     141  static receiver_fct receiver;
     142  
     143  void
     144  _dl_signal_cexception (int errcode, struct dl_exception *exception,
     145  		       const char *occasion)
     146  {
     147    if (__builtin_expect (GLRO(dl_debug_mask)
     148  			& ~(DL_DEBUG_STATISTICS), 0))
     149      _dl_debug_printf ("%s: error: %s: %s (%s)\n",
     150  		      exception->objname, occasion,
     151  		      exception->errstring, receiver ? "continued" : "fatal");
     152  
     153    if (receiver)
     154      {
     155        /* We are inside _dl_receive_error.  Call the user supplied
     156  	 handler and resume the work.  The receiver will still be
     157  	 installed.  */
     158        (*receiver) (errcode, exception->objname, exception->errstring);
     159      }
     160    else
     161      _dl_signal_exception (errcode, exception, occasion);
     162  }
     163  
     164  void
     165  _dl_signal_cerror (int errcode, const char *objname, const char *occasion,
     166  		   const char *errstring)
     167  {
     168    if (__builtin_expect (GLRO(dl_debug_mask)
     169  			& ~(DL_DEBUG_STATISTICS), 0))
     170      _dl_debug_printf ("%s: error: %s: %s (%s)\n", objname, occasion,
     171  		      errstring, receiver ? "continued" : "fatal");
     172  
     173    if (receiver)
     174      {
     175        /* We are inside _dl_receive_error.  Call the user supplied
     176  	 handler and resume the work.  The receiver will still be
     177  	 installed.  */
     178        (*receiver) (errcode, objname, errstring);
     179      }
     180    else
     181      _dl_signal_error (errcode, objname, occasion, errstring);
     182  }
     183  
     184  void
     185  _dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args)
     186  {
     187    struct rtld_catch *old_catch = get_catch ();
     188    receiver_fct old_receiver = receiver;
     189  
     190    /* Set the new values.  */
     191    set_catch (NULL);
     192    receiver = fct;
     193  
     194    (*operate) (args);
     195  
     196    set_catch (old_catch);
     197    receiver = old_receiver;
     198  }
     199  #endif
     200  
     201  int
     202  _dl_catch_exception (struct dl_exception *exception,
     203  		     void (*operate) (void *), void *args)
     204  {
     205    /* If exception is NULL, temporarily disable exception handling.
     206       Exceptions during operate (args) are fatal.  */
     207    if (exception == NULL)
     208      {
     209        struct rtld_catch *old_catch = get_catch ();
     210        set_catch (NULL);
     211        operate (args);
     212        /* If we get here, the operation was successful.  */
     213        set_catch (old_catch);
     214        return 0;
     215      }
     216  
     217    /* We need not handle `receiver' since setting a `catch' is handled
     218       before it.  */
     219  
     220    /* Only this needs to be marked volatile, because it is the only local
     221       variable that gets changed between the setjmp invocation and the
     222       longjmp call.  All others are just set here (before setjmp) and read
     223       in _dl_signal_error (before longjmp).  */
     224    volatile int errcode;
     225  
     226    struct rtld_catch c;
     227    /* Don't use an initializer since we don't need to clear C.env.  */
     228    c.exception = exception;
     229    c.errcode = &errcode;
     230  
     231    struct rtld_catch *old = get_catch ();
     232    set_catch (&c);
     233  
     234    /* Do not save the signal mask.  */
     235    if (__builtin_expect (__sigsetjmp (c.env, 0), 0) == 0)
     236      {
     237        (*operate) (args);
     238        set_catch (old);
     239        *exception = (struct dl_exception) { NULL };
     240        return 0;
     241      }
     242  
     243    /* We get here only if we longjmp'd out of OPERATE.
     244       _dl_signal_exception has already stored values into
     245       *EXCEPTION.  */
     246    set_catch (old);
     247    return errcode;
     248  }
     249  rtld_hidden_def (_dl_catch_exception)
     250  
     251  int
     252  _dl_catch_error (const char **objname, const char **errstring,
     253  		 bool *mallocedp, void (*operate) (void *), void *args)
     254  {
     255    struct dl_exception exception;
     256    int errorcode = _dl_catch_exception (&exception, operate, args);
     257    *objname = exception.objname;
     258    *errstring = exception.errstring;
     259    *mallocedp = exception.message_buffer == exception.errstring;
     260    return errorcode;
     261  }