1/* TLS emulation. 2 Copyright (C) 2006-2015 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 53void *__emutls_get_address (struct __emutls_object *); 54void __emutls_register_common (struct __emutls_object *, word, word, void *); 55 56#ifdef __GTHREADS 57#ifdef __GTHREAD_MUTEX_INIT 58static __gthread_mutex_t emutls_mutex = __GTHREAD_MUTEX_INIT; 59#else 60static __gthread_mutex_t emutls_mutex; 61#endif 62static __gthread_key_t emutls_key; 63static pointer emutls_size; 64 65static void 66emutls_destroy (void *ptr) 67{ 68 struct __emutls_array *arr = ptr; 69 pointer size = arr->size; 70 pointer i; 71 72 for (i = 0; i < size; ++i) 73 { 74 if (arr->data[i]) 75 free (arr->data[i][-1]); 76 } 77 78 free (ptr); 79} 80 81static void 82emutls_init (void) 83{ 84#ifndef __GTHREAD_MUTEX_INIT 85 __GTHREAD_MUTEX_INIT_FUNCTION (&emutls_mutex); 86#endif 87 if (__gthread_key_create (&emutls_key, emutls_destroy) != 0) 88 abort (); 89} 90#endif 91 92static void * 93emutls_alloc (struct __emutls_object *obj) 94{ 95 void *ptr; 96 void *ret; 97 98 /* We could use here posix_memalign if available and adjust 99 emutls_destroy accordingly. */ 100 if (obj->align <= sizeof (void *)) 101 { 102 ptr = malloc (obj->size + sizeof (void *)); 103 if (ptr == NULL) 104 abort (); 105 ((void **) ptr)[0] = ptr; 106 ret = ptr + sizeof (void *); 107 } 108 else 109 { 110 ptr = malloc (obj->size + sizeof (void *) + obj->align - 1); 111 if (ptr == NULL) 112 abort (); 113 ret = (void *) (((pointer) (ptr + sizeof (void *) + obj->align - 1)) 114 & ~(pointer)(obj->align - 1)); 115 ((void **) ret)[-1] = ptr; 116 } 117 118 if (obj->templ) 119 memcpy (ret, obj->templ, obj->size); 120 else 121 memset (ret, 0, obj->size); 122 123 return ret; 124} 125 126void * 127__emutls_get_address (struct __emutls_object *obj) 128{ 129 if (! __gthread_active_p ()) 130 { 131 if (__builtin_expect (obj->loc.ptr == NULL, 0)) 132 obj->loc.ptr = emutls_alloc (obj); 133 return obj->loc.ptr; 134 } 135 136#ifndef __GTHREADS 137 abort (); 138#else 139 pointer offset = __atomic_load_n (&obj->loc.offset, __ATOMIC_ACQUIRE); 140 141 if (__builtin_expect (offset == 0, 0)) 142 { 143 static __gthread_once_t once = __GTHREAD_ONCE_INIT; 144 __gthread_once (&once, emutls_init); 145 __gthread_mutex_lock (&emutls_mutex); 146 offset = obj->loc.offset; 147 if (offset == 0) 148 { 149 offset = ++emutls_size; 150 __atomic_store_n (&obj->loc.offset, offset, __ATOMIC_RELEASE); 151 } 152 __gthread_mutex_unlock (&emutls_mutex); 153 } 154 155 struct __emutls_array *arr = __gthread_getspecific (emutls_key); 156 if (__builtin_expect (arr == NULL, 0)) 157 { 158 pointer size = offset + 32; 159 arr = calloc (size + 1, sizeof (void *)); 160 if (arr == NULL) 161 abort (); 162 arr->size = size; 163 __gthread_setspecific (emutls_key, (void *) arr); 164 } 165 else if (__builtin_expect (offset > arr->size, 0)) 166 { 167 pointer orig_size = arr->size; 168 pointer size = orig_size * 2; 169 if (offset > size) 170 size = offset + 32; 171 arr = realloc (arr, (size + 1) * sizeof (void *)); 172 if (arr == NULL) 173 abort (); 174 arr->size = size; 175 memset (arr->data + orig_size, 0, 176 (size - orig_size) * sizeof (void *)); 177 __gthread_setspecific (emutls_key, (void *) arr); 178 } 179 180 void *ret = arr->data[offset - 1]; 181 if (__builtin_expect (ret == NULL, 0)) 182 { 183 ret = emutls_alloc (obj); 184 arr->data[offset - 1] = ret; 185 } 186 return ret; 187#endif 188} 189 190void 191__emutls_register_common (struct __emutls_object *obj, 192 word size, word align, void *templ) 193{ 194 if (obj->size < size) 195 { 196 obj->size = size; 197 obj->templ = NULL; 198 } 199 if (obj->align < align) 200 obj->align = align; 201 if (templ && size == obj->size) 202 obj->templ = templ; 203} 204