xref: /netbsd-src/external/gpl3/gcc.old/dist/libgcc/emutls.c (revision 70f7362772ba52b749c976fb5e86e39a8b2c9afc)
1 /* TLS emulation.
2    Copyright (C) 2006-2020 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 (void *);
62 EMUTLS_ATTR
63 void __emutls_register_common (void *, 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 (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 
205 EMUTLS_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