1/* TLS emulation. 2 Copyright (C) 2006-2020 Free Software Foundation, Inc. 3 Contributed by Jakub Jelinek <jakub@redhat.com>. 4 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 3, or (at your option) any later 10version. 11 12GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17Under Section 7 of GPL version 3, you are granted additional 18permissions described in the GCC Runtime Library Exception, version 193.1, as published by the Free Software Foundation. 20 21You should have received a copy of the GNU General Public License and 22a copy of the GCC Runtime Library Exception along with this program; 23see 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 33typedef unsigned int word __attribute__((mode(word))); 34typedef unsigned int pointer __attribute__((mode(pointer))); 35 36struct __emutls_object 37{ 38 word size; 39 word align; 40 union { 41 pointer offset; 42 void *ptr; 43 } loc; 44 void *templ; 45}; 46 47struct __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 60EMUTLS_ATTR 61void *__emutls_get_address (void *); 62EMUTLS_ATTR 63void __emutls_register_common (void *, word, word, void *); 64 65#ifdef __GTHREADS 66#ifdef __GTHREAD_MUTEX_INIT 67static __gthread_mutex_t emutls_mutex = __GTHREAD_MUTEX_INIT; 68#else 69static __gthread_mutex_t emutls_mutex; 70#endif 71static __gthread_key_t emutls_key; 72static pointer emutls_size; 73 74static void 75emutls_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 90static void 91emutls_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 101static void * 102emutls_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 139EMUTLS_ATTR void * 140__emutls_get_address (void *vobj) 141{ 142 struct __emutls_object *obj = vobj; 143 144 if (! __gthread_active_p ()) 145 { 146 if (__builtin_expect (obj->loc.ptr == NULL, 0)) 147 obj->loc.ptr = emutls_alloc (obj); 148 return obj->loc.ptr; 149 } 150 151#ifndef __GTHREADS 152 abort (); 153#else 154 pointer offset = __atomic_load_n (&obj->loc.offset, __ATOMIC_ACQUIRE); 155 156 if (__builtin_expect (offset == 0, 0)) 157 { 158 static __gthread_once_t once = __GTHREAD_ONCE_INIT; 159 __gthread_once (&once, emutls_init); 160 __gthread_mutex_lock (&emutls_mutex); 161 offset = obj->loc.offset; 162 if (offset == 0) 163 { 164 offset = ++emutls_size; 165 __atomic_store_n (&obj->loc.offset, offset, __ATOMIC_RELEASE); 166 } 167 __gthread_mutex_unlock (&emutls_mutex); 168 } 169 170 struct __emutls_array *arr = __gthread_getspecific (emutls_key); 171 if (__builtin_expect (arr == NULL, 0)) 172 { 173 pointer size = offset + 32; 174 arr = calloc (size + 1, sizeof (void *)); 175 if (arr == NULL) 176 abort (); 177 arr->size = size; 178 __gthread_setspecific (emutls_key, (void *) arr); 179 } 180 else if (__builtin_expect (offset > arr->size, 0)) 181 { 182 pointer orig_size = arr->size; 183 pointer size = orig_size * 2; 184 if (offset > size) 185 size = offset + 32; 186 arr = realloc (arr, (size + 1) * sizeof (void *)); 187 if (arr == NULL) 188 abort (); 189 arr->size = size; 190 memset (arr->data + orig_size, 0, 191 (size - orig_size) * sizeof (void *)); 192 __gthread_setspecific (emutls_key, (void *) arr); 193 } 194 195 void *ret = arr->data[offset - 1]; 196 if (__builtin_expect (ret == NULL, 0)) 197 { 198 ret = emutls_alloc (obj); 199 arr->data[offset - 1] = ret; 200 } 201 return ret; 202#endif 203} 204 205EMUTLS_ATTR void 206__emutls_register_common (void *vobj, 207 word size, word align, void *templ) 208{ 209 struct __emutls_object *obj = vobj; 210 211 if (obj->size < size) 212 { 213 obj->size = size; 214 obj->templ = NULL; 215 } 216 if (obj->align < align) 217 obj->align = align; 218 if (templ && size == obj->size) 219 obj->templ = templ; 220} 221