1  /* Copyright (C) 2021-2023 Free Software Foundation, Inc.
       2     Contributed by Oracle.
       3  
       4     This file is part of GNU Binutils.
       5  
       6     This program is free software; you can redistribute it and/or modify
       7     it under the terms of the GNU General Public License as published by
       8     the Free Software Foundation; either version 3, or (at your option)
       9     any later version.
      10  
      11     This program is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU General Public License for more details.
      15  
      16     You should have received a copy of the GNU General Public License
      17     along with this program; if not, write to the Free Software
      18     Foundation, 51 Franklin Street - Fifth Floor, Boston,
      19     MA 02110-1301, USA.  */
      20  
      21  #include "config.h"
      22  #include <pthread.h>
      23  
      24  #include "collector.h"
      25  #include "libcol_util.h"
      26  #include "tsd.h"
      27  #include "memmgr.h"
      28  
      29  /* TprintfT(<level>,...) definitions.  Adjust per module as needed */
      30  #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
      31  #define DBG_LT1 1 // for configuration details, warnings
      32  #define DBG_LT2 2
      33  #define DBG_LT3 3
      34  
      35  /*
      36   * Build our thread-specific-data support on pthread interfaces.
      37   */
      38  #define MAXNKEYS    64  /* hard-wired? really? well, it depends only on us and we have a sense for how many keys we will use */
      39  static pthread_key_t tsd_pkeys[MAXNKEYS];
      40  static size_t tsd_sizes[MAXNKEYS];
      41  static unsigned tsd_nkeys = 0;
      42  
      43  int
      44  __collector_tsd_init ()
      45  {
      46    return 0;
      47  }
      48  
      49  void
      50  __collector_tsd_fini ()
      51  {
      52    Tprintf (DBG_LT1, "tsd_fini()\n");
      53    while (tsd_nkeys)
      54      {
      55        tsd_nkeys--;
      56        pthread_key_delete (tsd_pkeys[tsd_nkeys]);
      57        tsd_sizes[tsd_nkeys] = 0; // should be unneeded
      58      }
      59  }
      60  
      61  int
      62  __collector_tsd_allocate ()
      63  {
      64    return 0;
      65  }
      66  
      67  void
      68  __collector_tsd_release () { }
      69  
      70  static void
      71  tsd_destructor (void *p)
      72  {
      73    if (p)
      74      __collector_freeCSize (__collector_heap, p, *((size_t *) p));
      75  }
      76  
      77  unsigned
      78  __collector_tsd_create_key (size_t sz, void (*init)(void*), void (*fini)(void*))
      79  {
      80    /*
      81     * We no longer support init and fini arguments (and weren't using them anyhow).
      82     * Our hard-wired MAXNKEYS presumably is considerably higher than the number of keys we use.
      83     */
      84    if (init || fini || (tsd_nkeys >= MAXNKEYS))
      85      return COLLECTOR_TSD_INVALID_KEY;
      86  
      87    /*
      88     * A pthread key has a value that is (void *).
      89     * We don't know where it is stored, and can access its value only through {get|set}specific.
      90     * But libcollector expects a pointer to memory that it can modify.
      91     * So we have to allocate that memory and store the pointer.
      92     *
      93     * For now, we just have to register a destructor that will free the memory
      94     * when the thread finishes.
      95     */
      96    if (pthread_key_create (&tsd_pkeys[tsd_nkeys], &tsd_destructor))
      97     return COLLECTOR_TSD_INVALID_KEY;
      98    tsd_sizes[tsd_nkeys] = sz;
      99    tsd_nkeys++;
     100    return (tsd_nkeys - 1);
     101  }
     102  
     103  void *
     104  __collector_tsd_get_by_key (unsigned key_index)
     105  {
     106    if (key_index == COLLECTOR_TSD_INVALID_KEY)
     107      return NULL;
     108    if (key_index < 0 || key_index >= tsd_nkeys)
     109      return NULL;
     110    pthread_key_t key = tsd_pkeys[key_index];
     111    size_t sz = tsd_sizes[key_index];
     112  
     113    /*
     114     * When we use __collector_freeCSize(), we need to know the
     115     * size that had been allocated.  So, stick a header to the
     116     * front of the allocation to hold the size.  The header could
     117     * just be sizeof(size_t), but pad it to preserve alignment for
     118     * the usable area.
     119     */
     120    size_t header = 8;
     121    void *value = pthread_getspecific (key);
     122  
     123    // check whether we have allocated the memory
     124    if (value == NULL)
     125      {
     126        // add room to record the size
     127        value = __collector_allocCSize (__collector_heap, sz + header, 0);
     128        if (value == NULL)
     129  	{
     130  	  // do we need to guard against trying to alloc each time?
     131  	  return NULL;
     132  	}
     133        // write the size of the allocation
     134        *((size_t *) value) = sz + header;
     135        CALL_UTIL (memset)(((char *) value) + header, 0, sz);
     136  
     137        // record the allocation for future retrieval
     138        if (pthread_setspecific (key, value))
     139  	return NULL;
     140      }
     141    // return the pointer, skipping the header
     142    return ((char *) value) +header;
     143  }
     144  
     145  void
     146  __collector_tsd_fork_child_cleanup ()
     147  {
     148    __collector_tsd_fini ();
     149  }