111
|
1 /* TLS emulation.
|
131
|
2 Copyright (C) 2006-2018 Free Software Foundation, Inc.
|
111
|
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 void *__emutls_get_address (struct __emutls_object *);
|
|
54 void __emutls_register_common (struct __emutls_object *, word, word, void *);
|
|
55
|
|
56 #ifdef __GTHREADS
|
|
57 #ifdef __GTHREAD_MUTEX_INIT
|
|
58 static __gthread_mutex_t emutls_mutex = __GTHREAD_MUTEX_INIT;
|
|
59 #else
|
|
60 static __gthread_mutex_t emutls_mutex;
|
|
61 #endif
|
|
62 static __gthread_key_t emutls_key;
|
|
63 static pointer emutls_size;
|
|
64
|
|
65 static void
|
|
66 emutls_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
|
|
81 static void
|
|
82 emutls_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
|
|
92 static void *
|
|
93 emutls_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
|
|
126 void *
|
|
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
|
|
190 void
|
|
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 }
|