(root)/
gcc-13.2.0/
libgcc/
emutls.c
       1  /* TLS emulation.
       2     Copyright (C) 2006-2023 Free Software Foundation, Inc.
       3     Contributed by Jakub Jelinek <jakub@redhat.com>.
       4  
       5  This file is part of GCC.
       6  
       7  GCC is free software; you can redistribute it and/or modify it under
       8  the terms of the GNU General Public License as published by the Free
       9  Software Foundation; either version 3, or (at your option) any later
      10  version.
      11  
      12  GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      13  WARRANTY; without even the implied warranty of MERCHANTABILITY or
      14  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      15  for more details.
      16  
      17  Under Section 7 of GPL version 3, you are granted additional
      18  permissions described in the GCC Runtime Library Exception, version
      19  3.1, as published by the Free Software Foundation.
      20  
      21  You should have received a copy of the GNU General Public License and
      22  a copy of the GCC Runtime Library Exception along with this program;
      23  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      24  <http://www.gnu.org/licenses/>.  */
      25  
      26  #include "tconfig.h"
      27  #include "tsystem.h"
      28  #include "coretypes.h"
      29  #include "tm.h"
      30  #include "libgcc_tm.h"
      31  #include "gthr.h"
      32  
      33  typedef unsigned int word __attribute__((mode(word)));
      34  typedef unsigned int pointer __attribute__((mode(pointer)));
      35  
      36  struct __emutls_object
      37  {
      38    word size;
      39    word align;
      40    union {
      41      pointer offset;
      42      void *ptr;
      43    } loc;
      44    void *templ;
      45  };
      46  
      47  struct __emutls_array
      48  {
      49    pointer size;
      50    void **data[];
      51  };
      52  
      53  /* EMUTLS_ATTR is provided to allow targets to build the emulated tls
      54     routines as weak definitions, for example.
      55     If there is no definition, fall back to the default.  */
      56  #ifndef EMUTLS_ATTR
      57  #  define EMUTLS_ATTR
      58  #endif
      59  
      60  EMUTLS_ATTR
      61  void *__emutls_get_address (struct __emutls_object *);
      62  EMUTLS_ATTR
      63  void __emutls_register_common (struct __emutls_object *, word, word, void *);
      64  
      65  #ifdef __GTHREADS
      66  #ifdef __GTHREAD_MUTEX_INIT
      67  static __gthread_mutex_t emutls_mutex = __GTHREAD_MUTEX_INIT;
      68  #else
      69  static __gthread_mutex_t emutls_mutex;
      70  #endif
      71  static __gthread_key_t emutls_key;
      72  static pointer emutls_size;
      73  
      74  static void
      75  emutls_destroy (void *ptr)
      76  {
      77    struct __emutls_array *arr = ptr;
      78    pointer size = arr->size;
      79    pointer i;
      80  
      81    for (i = 0; i < size; ++i)
      82      {
      83        if (arr->data[i])
      84  	free (arr->data[i][-1]);
      85      }
      86  
      87    free (ptr);
      88  }
      89  
      90  static void
      91  emutls_init (void)
      92  {
      93  #ifndef __GTHREAD_MUTEX_INIT
      94    __GTHREAD_MUTEX_INIT_FUNCTION (&emutls_mutex);
      95  #endif
      96    if (__gthread_key_create (&emutls_key, emutls_destroy) != 0)
      97      abort ();
      98  }
      99  #endif
     100  
     101  static void *
     102  emutls_alloc (struct __emutls_object *obj)
     103  {
     104    void *ptr;
     105    void *ret;
     106  
     107    /* We could use here posix_memalign if available and adjust
     108       emutls_destroy accordingly.  */
     109    if (obj->align <= sizeof (void *))
     110      {
     111        ptr = malloc (obj->size + sizeof (void *));
     112        if (ptr == NULL)
     113  	abort ();
     114        ((void **) ptr)[0] = ptr;
     115        ret = ptr + sizeof (void *);
     116      }
     117    else
     118      {
     119        ptr = malloc (obj->size + sizeof (void *) + obj->align - 1);
     120        if (ptr == NULL)
     121  	abort ();
     122        ret = (void *) (((pointer) (ptr + sizeof (void *) + obj->align - 1))
     123  		      & ~(pointer)(obj->align - 1));
     124        ((void **) ret)[-1] = ptr;
     125      }
     126  
     127    if (obj->templ)
     128      memcpy (ret, obj->templ, obj->size);
     129    else
     130      memset (ret, 0, obj->size);
     131  
     132    return ret;
     133  }
     134  
     135  /* Despite applying the attribute to the declaration, in this case the mis-
     136     match between the builtin's declaration [void * (*)(void *)] and the
     137     implementation here, causes the decl. attributes to be discarded.  */
     138  
     139  EMUTLS_ATTR void *
     140  __emutls_get_address (struct __emutls_object *obj)
     141  {
     142    if (! __gthread_active_p ())
     143      {
     144        if (__builtin_expect (obj->loc.ptr == NULL, 0))
     145  	obj->loc.ptr = emutls_alloc (obj);
     146        return obj->loc.ptr;
     147      }
     148  
     149  #ifndef __GTHREADS
     150    abort ();
     151  #else
     152    pointer offset = __atomic_load_n (&obj->loc.offset, __ATOMIC_ACQUIRE);
     153  
     154    if (__builtin_expect (offset == 0, 0))
     155      {
     156        static __gthread_once_t once = __GTHREAD_ONCE_INIT;
     157        __gthread_once (&once, emutls_init);
     158        __gthread_mutex_lock (&emutls_mutex);
     159        offset = obj->loc.offset;
     160        if (offset == 0)
     161  	{
     162  	  offset = ++emutls_size;
     163  	  __atomic_store_n (&obj->loc.offset, offset, __ATOMIC_RELEASE);
     164  	}
     165        __gthread_mutex_unlock (&emutls_mutex);
     166      }
     167  
     168    struct __emutls_array *arr = __gthread_getspecific (emutls_key);
     169    if (__builtin_expect (arr == NULL, 0))
     170      {
     171        pointer size = offset + 32;
     172        arr = calloc (size + 1, sizeof (void *));
     173        if (arr == NULL)
     174  	abort ();
     175        arr->size = size;
     176        __gthread_setspecific (emutls_key, (void *) arr);
     177      }
     178    else if (__builtin_expect (offset > arr->size, 0))
     179      {
     180        pointer orig_size = arr->size;
     181        pointer size = orig_size * 2;
     182        if (offset > size)
     183  	size = offset + 32;
     184        arr = realloc (arr, (size + 1) * sizeof (void *));
     185        if (arr == NULL)
     186  	abort ();
     187        arr->size = size;
     188        memset (arr->data + orig_size, 0,
     189  	      (size - orig_size) * sizeof (void *));
     190        __gthread_setspecific (emutls_key, (void *) arr);
     191      }
     192  
     193    void *ret = arr->data[offset - 1];
     194    if (__builtin_expect (ret == NULL, 0))
     195      {
     196        ret = emutls_alloc (obj);
     197        arr->data[offset - 1] = ret;
     198      }
     199    return ret;
     200  #endif
     201  }
     202  
     203  EMUTLS_ATTR void
     204  __emutls_register_common (struct __emutls_object *obj,
     205  			  word size, word align, void *templ)
     206  {
     207    if (obj->size < size)
     208      {
     209        obj->size = size;
     210        obj->templ = NULL;
     211      }
     212    if (obj->align < align)
     213      obj->align = align;
     214    if (templ && size == obj->size)
     215      obj->templ = templ;
     216  }