(root)/
glibc-2.38/
stdlib/
tst-tls-atexit.c
       1  /* Verify that DSO is unloaded only if its TLS objects are destroyed.
       2     Copyright (C) 2013-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  /* For the default case, i.e. NO_DELETE not defined, the test dynamically loads
      20     a DSO and spawns a thread that subsequently calls into the DSO to register a
      21     destructor for an object in the DSO and then calls dlclose on the handle for
      22     the DSO.  When the thread exits, the DSO should not be unloaded or else the
      23     destructor called during thread exit will crash.  Further in the main
      24     thread, the DSO is opened and closed again, at which point the DSO should be
      25     unloaded.
      26  
      27     When NO_DELETE is defined, the DSO is loaded twice, once with just RTLD_LAZY
      28     flag and the second time with the RTLD_NODELETE flag set.  The thread is
      29     spawned, destructor registered and then thread exits without closing the
      30     DSO.  In the main thread, the first handle is then closed, followed by the
      31     second handle.  In the end, the DSO should remain loaded due to the
      32     RTLD_NODELETE flag being set in the second dlopen call.  */
      33  
      34  #include <pthread.h>
      35  #include <stdio.h>
      36  #include <unistd.h>
      37  #include <string.h>
      38  #include <errno.h>
      39  #include <link.h>
      40  #include <stdbool.h>
      41  #include <support/xdlfcn.h>
      42  
      43  #ifndef NO_DELETE
      44  # define LOADED_IS_GOOD false
      45  #endif
      46  
      47  #ifndef H2_RTLD_FLAGS
      48  # define H2_RTLD_FLAGS (RTLD_LAZY)
      49  #endif
      50  
      51  #define DSO_NAME "$ORIGIN/tst-tls-atexit-lib.so"
      52  
      53  /* Walk through the map in the _r_debug structure to see if our lib is still
      54     loaded.  */
      55  static bool
      56  is_loaded (void)
      57  {
      58    struct link_map *lm = (struct link_map *) _r_debug.r_map;
      59  
      60    for (; lm; lm = lm->l_next)
      61      if (lm->l_type == lt_loaded && lm->l_name
      62  	&& strcmp (basename (DSO_NAME), basename (lm->l_name)) == 0)
      63        {
      64  	printf ("%s is still loaded\n", lm->l_name);
      65  	return true;
      66        }
      67    return false;
      68  }
      69  
      70  /* Accept a valid handle returned by DLOPEN, load the reg_dtor symbol to
      71     register a destructor and then call dlclose on the handle.  The dlclose
      72     should not unload the DSO since the destructor has not been called yet.  */
      73  static void *
      74  reg_dtor_and_close (void *h)
      75  {
      76    void (*reg_dtor) (void) = (void (*) (void)) xdlsym (h, "reg_dtor");
      77  
      78    reg_dtor ();
      79  
      80  #ifndef NO_DELETE
      81    xdlclose (h);
      82  #endif
      83  
      84    return NULL;
      85  }
      86  
      87  static int
      88  spawn_thread (void *h)
      89  {
      90    pthread_t t;
      91    int ret;
      92    void *thr_ret;
      93  
      94    if ((ret = pthread_create (&t, NULL, reg_dtor_and_close, h)) != 0)
      95      {
      96        printf ("pthread_create failed: %s\n", strerror (ret));
      97        return 1;
      98      }
      99  
     100    if ((ret = pthread_join (t, &thr_ret)) != 0)
     101      {
     102        printf ("pthread_join failed: %s\n", strerror (ret));
     103        return 1;
     104      }
     105  
     106    if (thr_ret != NULL)
     107      return 1;
     108  
     109    return 0;
     110  }
     111  
     112  static int
     113  do_test (void)
     114  {
     115    /* Load the DSO.  */
     116    void *h1 = xdlopen (DSO_NAME, RTLD_LAZY);
     117  
     118  #ifndef NO_DELETE
     119    if (spawn_thread (h1) != 0)
     120      return 1;
     121  #endif
     122  
     123    void *h2 = xdlopen (DSO_NAME, H2_RTLD_FLAGS);
     124  
     125  #ifdef NO_DELETE
     126    if (spawn_thread (h1) != 0)
     127      return 1;
     128  
     129    xdlclose (h1);
     130  #endif
     131    xdlclose (h2);
     132  
     133    /* Check link maps to ensure that the DSO has unloaded.  In the normal case,
     134       the DSO should be unloaded if there are no uses.  However, if one of the
     135       dlopen calls were with RTLD_NODELETE, the DSO should remain loaded.  */
     136    return is_loaded () == LOADED_IS_GOOD ? 0 : 1;
     137  }
     138  
     139  #define TEST_FUNCTION do_test ()
     140  #include "../test-skeleton.c"