|  | /* Copyright (C) 2021 Free Software Foundation, Inc. | 
|  | Contributed by Oracle. | 
|  |  | 
|  | This file is part of GNU Binutils. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3, or (at your option) | 
|  | any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program; if not, write to the Free Software | 
|  | Foundation, 51 Franklin Street - Fifth Floor, Boston, | 
|  | MA 02110-1301, USA.  */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include <pthread.h> | 
|  |  | 
|  | #include "collector.h" | 
|  | #include "libcol_util.h" | 
|  | #include "tsd.h" | 
|  | #include "memmgr.h" | 
|  |  | 
|  | /* TprintfT(<level>,...) definitions.  Adjust per module as needed */ | 
|  | #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings | 
|  | #define DBG_LT1 1 // for configuration details, warnings | 
|  | #define DBG_LT2 2 | 
|  | #define DBG_LT3 3 | 
|  |  | 
|  | /* | 
|  | * Build our thread-specific-data support on pthread interfaces. | 
|  | */ | 
|  | #define MAXNKEYS    64  /* hard-wired? really? well, it depends only on us and we have a sense for how many keys we will use */ | 
|  | static pthread_key_t tsd_pkeys[MAXNKEYS]; | 
|  | static size_t tsd_sizes[MAXNKEYS]; | 
|  | static unsigned tsd_nkeys = 0; | 
|  |  | 
|  | int | 
|  | __collector_tsd_init () | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | __collector_tsd_fini () | 
|  | { | 
|  | Tprintf (DBG_LT1, "tsd_fini()\n"); | 
|  | while (tsd_nkeys) | 
|  | { | 
|  | tsd_nkeys--; | 
|  | pthread_key_delete (tsd_pkeys[tsd_nkeys]); | 
|  | tsd_sizes[tsd_nkeys] = 0; // should be unneeded | 
|  | } | 
|  | } | 
|  |  | 
|  | int | 
|  | __collector_tsd_allocate () | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | __collector_tsd_release () { } | 
|  |  | 
|  | static void | 
|  | tsd_destructor (void *p) | 
|  | { | 
|  | if (p) | 
|  | __collector_freeCSize (__collector_heap, p, *((size_t *) p)); | 
|  | } | 
|  |  | 
|  | unsigned | 
|  | __collector_tsd_create_key (size_t sz, void (*init)(void*), void (*fini)(void*)) | 
|  | { | 
|  | /* | 
|  | * We no longer support init and fini arguments (and weren't using them anyhow). | 
|  | * Our hard-wired MAXNKEYS presumably is considerably higher than the number of keys we use. | 
|  | */ | 
|  | if (init || fini || (tsd_nkeys >= MAXNKEYS)) | 
|  | return COLLECTOR_TSD_INVALID_KEY; | 
|  |  | 
|  | /* | 
|  | * A pthread key has a value that is (void *). | 
|  | * We don't know where it is stored, and can access its value only through {get|set}specific. | 
|  | * But libcollector expects a pointer to memory that it can modify. | 
|  | * So we have to allocate that memory and store the pointer. | 
|  | * | 
|  | * For now, we just have to register a destructor that will free the memory | 
|  | * when the thread finishes. | 
|  | */ | 
|  | if (pthread_key_create (&tsd_pkeys[tsd_nkeys], &tsd_destructor)) | 
|  | return COLLECTOR_TSD_INVALID_KEY; | 
|  | tsd_sizes[tsd_nkeys] = sz; | 
|  | tsd_nkeys++; | 
|  | return (tsd_nkeys - 1); | 
|  | } | 
|  |  | 
|  | void * | 
|  | __collector_tsd_get_by_key (unsigned key_index) | 
|  | { | 
|  | if (key_index == COLLECTOR_TSD_INVALID_KEY) | 
|  | return NULL; | 
|  | if (key_index < 0 || key_index >= tsd_nkeys) | 
|  | return NULL; | 
|  | pthread_key_t key = tsd_pkeys[key_index]; | 
|  | size_t sz = tsd_sizes[key_index]; | 
|  |  | 
|  | /* | 
|  | * When we use __collector_freeCSize(), we need to know the | 
|  | * size that had been allocated.  So, stick a header to the | 
|  | * front of the allocation to hold the size.  The header could | 
|  | * just be sizeof(size_t), but pad it to preserve alignment for | 
|  | * the usable area. | 
|  | */ | 
|  | size_t header = 8; | 
|  | void *value = pthread_getspecific (key); | 
|  |  | 
|  | // check whether we have allocated the memory | 
|  | if (value == NULL) | 
|  | { | 
|  | // add room to record the size | 
|  | value = __collector_allocCSize (__collector_heap, sz + header, 0); | 
|  | if (value == NULL) | 
|  | { | 
|  | // do we need to guard against trying to alloc each time? | 
|  | return NULL; | 
|  | } | 
|  | // write the size of the allocation | 
|  | *((size_t *) value) = sz + header; | 
|  | CALL_UTIL (memset)(((char *) value) + header, 0, sz); | 
|  |  | 
|  | // record the allocation for future retrieval | 
|  | if (pthread_setspecific (key, value)) | 
|  | return NULL; | 
|  | } | 
|  | // return the pointer, skipping the header | 
|  | return ((char *) value) +header; | 
|  | } | 
|  |  | 
|  | void | 
|  | __collector_tsd_fork_child_cleanup () | 
|  | { | 
|  | __collector_tsd_fini (); | 
|  | } |