1/* Copyright (C) 2021-2024 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 */ 39static pthread_key_t tsd_pkeys[MAXNKEYS]; 40static size_t tsd_sizes[MAXNKEYS]; 41static unsigned tsd_nkeys = 0; 42 43int 44__collector_tsd_init () 45{ 46 return 0; 47} 48 49void 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 61int 62__collector_tsd_allocate () 63{ 64 return 0; 65} 66 67void 68__collector_tsd_release () { } 69 70static void 71tsd_destructor (void *p) 72{ 73 if (p) 74 __collector_freeCSize (__collector_heap, p, *((size_t *) p)); 75} 76 77unsigned 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 103void * 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 145void 146__collector_tsd_fork_child_cleanup () 147{ 148 __collector_tsd_fini (); 149} 150