Merge pull request #4248 from Unity-Technologies/boehm-gc-alloc-fixed
[mono.git] / mono / utils / mono-tls.c
1 /*
2  * mono-tls.c: Low-level TLS support
3  *
4  * Thread local variables that are accessed both from native and managed code
5  * are defined here and should be accessed only through this APIs
6  *
7  * Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
8  */
9
10 #include <mono/utils/mach-support.h>
11
12 #include "mono-tls.h"
13
14 /*
15  * On all platforms we should be able to use either __thread or pthread/TlsGetValue.
16  * Certain platforms will support fast tls only when using one of the thread local
17  * storage backends. By default this is __thread if we have HAVE_KW_THREAD defined.
18  *
19  * By default all platforms will call into these native getters whenever they need
20  * to get a tls value. On certain platforms we can try to be faster than this and
21  * avoid the call. We call this fast tls and each platform defines its own way to
22  * achieve this. For this, a platform has to define MONO_ARCH_HAVE_INLINED_TLS,
23  * and provide alternative getters/setters for a MonoTlsKey. In order to have fast
24  * getter/setters, the platform has to declare a way to fetch an internal offset
25  * (MONO_THREAD_VAR_OFFSET) which is stored here, and in the arch specific file
26  * probe the system to see if we can use the offset initialized here. If these
27  * run-time checks don't succeed we just use the fallbacks.
28  *
29  * In case we would wish to provide fast inlined tls for aot code, we would need
30  * to be sure that, at run-time, these two platform checks would never fail
31  * otherwise the tls getter/setters that we emitted would not work. Normally,
32  * there is little incentive to support this since tls access is most common in
33  * wrappers and managed allocators, both of which are not aot-ed by default.
34  * So far, we never supported inlined fast tls on full-aot systems.
35  */
36
37 #ifdef USE_KW_THREAD
38
39 /* tls attribute */
40 #if HAVE_TLS_MODEL_ATTR
41
42 #if defined(__PIC__) && !defined(PIC)
43 /*
44  * Must be compiling -fPIE, for executables.  Build PIC
45  * but with initial-exec.
46  * http://bugs.gentoo.org/show_bug.cgi?id=165547
47  */
48 #define PIC
49 #define PIC_INITIAL_EXEC
50 #endif
51
52 /*
53  * Define this if you want a faster libmono, which cannot be loaded dynamically as a
54  * module.
55  */
56 //#define PIC_INITIAL_EXEC
57
58 #if defined(PIC)
59
60 #ifdef PIC_INITIAL_EXEC
61 #define MONO_TLS_FAST __attribute__ ((__tls_model__("initial-exec")))
62 #else
63 #if defined (__powerpc__)
64 /* local dynamic requires a call to __tls_get_addr to look up the
65    TLS block address via the Dynamic Thread Vector. In this case Thread
66    Pointer relative offsets can't be used as this modules TLS was
67    allocated separately (none contiguoiusly) from the initial TLS
68    block.
69
70    For now we will disable this. */
71 #define MONO_TLS_FAST
72 #else
73 #define MONO_TLS_FAST __attribute__ ((__tls_model__("local-dynamic")))
74 #endif
75 #endif
76
77 #else
78
79 #define MONO_TLS_FAST __attribute__ ((__tls_model__("local-exec")))
80
81 #endif
82
83 #else
84 #define MONO_TLS_FAST
85 #endif
86
87 /* Runtime offset detection */
88 #if defined(TARGET_AMD64) && !defined(TARGET_MACH) && !defined(HOST_WIN32) /* __thread likely not tested on mac/win */
89
90 #if defined(PIC)
91 // This only works if libmono is linked into the application
92 #define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;  __asm ("movq " #var "@GOTTPOFF(%%rip), %0" : "=r" (foo)); offset = foo; } while (0)
93 #else
94 #define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;  __asm ("movq $" #var "@TPOFF, %0" : "=r" (foo)); offset = foo; } while (0)
95 #endif
96
97 #elif defined(TARGET_X86) && !defined(TARGET_MACH) && !defined(HOST_WIN32) && defined(__GNUC__)
98
99 #if defined(PIC)
100 #define MONO_THREAD_VAR_OFFSET(var,offset) do { int tmp; __asm ("call 1f; 1: popl %0; addl $_GLOBAL_OFFSET_TABLE_+[.-1b], %0; movl " #var "@gotntpoff(%0), %1" : "=r" (tmp), "=r" (offset)); } while (0)
101 #else
102 #define MONO_THREAD_VAR_OFFSET(var,offset) __asm ("movl $" #var "@ntpoff, %0" : "=r" (offset))
103 #endif
104
105 #elif defined(TARGET_ARM64) && !defined(PIC)
106
107 #define MONO_THREAD_VAR_OFFSET(var,offset) \
108         __asm ( "mov %0, #0\n add %0, %0, #:tprel_hi12:" #var "\n add %0, %0, #:tprel_lo12_nc:" #var "\n" \
109                 : "=r" (offset))
110
111 #elif defined(TARGET_ARM) && defined(__ARM_EABI__) && !defined(PIC)
112
113 #define MONO_THREAD_VAR_OFFSET(var,offset) __asm ("     ldr     %0, 1f; b 2f; 1: .word " #var "(tpoff); 2:" : "=r" (offset))
114
115 #elif defined(TARGET_S390X)
116 # if defined(__PIC__)
117 #  if !defined(__PIE__)
118 // This only works if libmono is linked into the application
119 #   define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;                                 \
120                                                 __asm__ ("basr  %%r1,0\n\t"                     \
121                                                          "j     0f\n\t"                         \
122                                                          ".quad " #var "@TLSGD\n"               \
123                                                          "0:\n\t"                               \
124                                                          "lg    %%r2,4(%%r1)\n\t"               \
125                                                          "brasl %%r14,__tls_get_offset@PLT:tls_gdcall:"#var"\n\t" \
126                                                          "lgr   %0,%%r2\n\t"                    \
127                                                         : "=r" (foo) :                          \
128                                                         : "1", "2", "14", "cc");                \
129                                                 offset = foo; } while (0)
130 #  elif __PIE__ == 1
131 #   define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;                                         \
132                                                 __asm__ ("lg    %0," #var "@GOTNTPOFF(%%r12)\n\t"       \
133                                                          : "=r" (foo));                                 \
134                                                 offset = foo; } while (0)
135 #  elif __PIE__ == 2
136 #   define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;                                 \
137                                                 __asm__ ("larl  %%r1," #var "@INDNTPOFF\n\t"    \
138                                                          "lg    %0,0(%%r1)\n\t"                 \
139                                                          : "=r" (foo) :                         \
140                                                          : "1", "cc");                          \
141                                                 offset = foo; } while (0)
142 #  endif
143 # else
144 #  define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;                          \
145                                                 __asm__ ("basr  %%r1,0\n\t"             \
146                                                          "j     0f\n\t"                 \
147                                                          ".quad " #var "@NTPOFF\n"      \
148                                                          "0:\n\t"                       \
149                                                          "lg    %0,4(%%r1)\n\t"         \
150                                                         : "=r" (foo) : : "1");          \
151                                                 offset = foo; } while (0)
152 # endif
153 #else
154
155 #define MONO_THREAD_VAR_OFFSET(var,offset) (offset) = -1
156
157 #endif
158
159 /* Tls variables for each MonoTlsKey */
160
161 static __thread gpointer mono_tls_thread MONO_TLS_FAST;
162 static __thread gpointer mono_tls_jit_tls MONO_TLS_FAST;
163 static __thread gpointer mono_tls_domain MONO_TLS_FAST;
164 static __thread gpointer mono_tls_lmf MONO_TLS_FAST;
165 static __thread gpointer mono_tls_sgen_thread_info MONO_TLS_FAST;
166 static __thread gpointer mono_tls_lmf_addr MONO_TLS_FAST;
167
168 #else
169
170 #if defined(TARGET_AMD64) && (defined(TARGET_MACH) || defined(HOST_WIN32))
171 #define MONO_THREAD_VAR_OFFSET(key,offset) (offset) = (gint32)key
172 #elif defined(TARGET_X86) && (defined(TARGET_MACH) || defined(HOST_WIN32))
173 #define MONO_THREAD_VAR_OFFSET(key,offset) (offset) = (gint32)key
174 #else
175 #define MONO_THREAD_VAR_OFFSET(var,offset) (offset) = -1
176 #endif
177
178 static MonoNativeTlsKey mono_tls_key_thread;
179 static MonoNativeTlsKey mono_tls_key_jit_tls;
180 static MonoNativeTlsKey mono_tls_key_domain;
181 static MonoNativeTlsKey mono_tls_key_lmf;
182 static MonoNativeTlsKey mono_tls_key_sgen_thread_info;
183 static MonoNativeTlsKey mono_tls_key_lmf_addr;
184
185 #endif
186
187 static gint32 tls_offsets [TLS_KEY_NUM];
188
189 #ifdef USE_KW_THREAD
190 #define MONO_TLS_GET_VALUE(tls_var,tls_key) (tls_var)
191 #define MONO_TLS_SET_VALUE(tls_var,tls_key,value) (tls_var = value)
192 #else
193 #define MONO_TLS_GET_VALUE(tls_var,tls_key) (mono_native_tls_get_value (tls_key))
194 #define MONO_TLS_SET_VALUE(tls_var,tls_key,value) (mono_native_tls_set_value (tls_key, value))
195 #endif
196
197 void
198 mono_tls_init_gc_keys (void)
199 {
200 #ifdef USE_KW_THREAD
201         MONO_THREAD_VAR_OFFSET (mono_tls_sgen_thread_info, tls_offsets [TLS_KEY_SGEN_THREAD_INFO]);
202 #else
203         mono_native_tls_alloc (&mono_tls_key_sgen_thread_info, NULL);
204         MONO_THREAD_VAR_OFFSET (mono_tls_key_sgen_thread_info, tls_offsets [TLS_KEY_SGEN_THREAD_INFO]);
205 #endif
206 }
207
208 void
209 mono_tls_init_runtime_keys (void)
210 {
211 #ifdef USE_KW_THREAD
212         MONO_THREAD_VAR_OFFSET (mono_tls_thread, tls_offsets [TLS_KEY_THREAD]);
213         MONO_THREAD_VAR_OFFSET (mono_tls_jit_tls, tls_offsets [TLS_KEY_JIT_TLS]);
214         MONO_THREAD_VAR_OFFSET (mono_tls_domain, tls_offsets [TLS_KEY_DOMAIN]);
215         MONO_THREAD_VAR_OFFSET (mono_tls_lmf, tls_offsets [TLS_KEY_LMF]);
216         MONO_THREAD_VAR_OFFSET (mono_tls_lmf_addr, tls_offsets [TLS_KEY_LMF_ADDR]);
217 #else
218         mono_native_tls_alloc (&mono_tls_key_thread, NULL);
219         MONO_THREAD_VAR_OFFSET (mono_tls_key_thread, tls_offsets [TLS_KEY_THREAD]);
220         mono_native_tls_alloc (&mono_tls_key_jit_tls, NULL);
221         MONO_THREAD_VAR_OFFSET (mono_tls_key_jit_tls, tls_offsets [TLS_KEY_JIT_TLS]);
222         mono_native_tls_alloc (&mono_tls_key_domain, NULL);
223         MONO_THREAD_VAR_OFFSET (mono_tls_key_domain, tls_offsets [TLS_KEY_DOMAIN]);
224         mono_native_tls_alloc (&mono_tls_key_lmf, NULL);
225         MONO_THREAD_VAR_OFFSET (mono_tls_key_lmf, tls_offsets [TLS_KEY_LMF]);
226         mono_native_tls_alloc (&mono_tls_key_lmf_addr, NULL);
227         MONO_THREAD_VAR_OFFSET (mono_tls_key_lmf_addr, tls_offsets [TLS_KEY_LMF_ADDR]);
228 #endif
229 }
230
231 void
232 mono_tls_free_keys (void)
233 {
234 #ifndef USE_KW_THREAD
235         mono_native_tls_free (mono_tls_key_thread);
236         mono_native_tls_free (mono_tls_key_jit_tls);
237         mono_native_tls_free (mono_tls_key_domain);
238         mono_native_tls_free (mono_tls_key_lmf);
239         mono_native_tls_free (mono_tls_key_sgen_thread_info);
240         mono_native_tls_free (mono_tls_key_lmf_addr);
241 #endif
242 }
243
244
245 /*
246  * Gets the tls offset associated with the key. This offset is set at key
247  * initialization (at runtime). Certain targets can implement computing
248  * this offset and using it at runtime for fast inlined tls access.
249  */
250 gint32
251 mono_tls_get_tls_offset (MonoTlsKey key)
252 {
253         g_assert (tls_offsets [key]);
254         return tls_offsets [key];
255 }
256
257 /*
258  * Returns the getter (gpointer (*)(void)) for the mono tls key.
259  * Managed code will always get the value by calling this getter.
260  */
261 gpointer
262 mono_tls_get_tls_getter (MonoTlsKey key, gboolean name)
263 {
264         switch (key) {
265         case TLS_KEY_THREAD:
266                 return name ? (gpointer)"mono_tls_get_thread" : (gpointer)mono_tls_get_thread;
267         case TLS_KEY_JIT_TLS:
268                 return name ? (gpointer)"mono_tls_get_jit_tls" : (gpointer)mono_tls_get_jit_tls;
269         case TLS_KEY_DOMAIN:
270                 return name ? (gpointer)"mono_tls_get_domain" : (gpointer)mono_tls_get_domain;
271         case TLS_KEY_LMF:
272                 return name ? (gpointer)"mono_tls_get_lmf" : (gpointer)mono_tls_get_lmf;
273         case TLS_KEY_SGEN_THREAD_INFO:
274                 return name ? (gpointer)"mono_tls_get_sgen_thread_info" : (gpointer)mono_tls_get_sgen_thread_info;
275         case TLS_KEY_LMF_ADDR:
276                 return name ? (gpointer)"mono_tls_get_lmf_addr" : (gpointer)mono_tls_get_lmf_addr;
277         }
278         g_assert_not_reached ();
279         return NULL;
280 }
281
282 /* Returns the setter (void (*)(gpointer)) for the mono tls key */
283 gpointer
284 mono_tls_get_tls_setter (MonoTlsKey key, gboolean name)
285 {
286         switch (key) {
287         case TLS_KEY_THREAD:
288                 return name ? (gpointer)"mono_tls_set_thread" : (gpointer)mono_tls_set_thread;
289         case TLS_KEY_JIT_TLS:
290                 return name ? (gpointer)"mono_tls_set_jit_tls" : (gpointer)mono_tls_set_jit_tls;
291         case TLS_KEY_DOMAIN:
292                 return name ? (gpointer)"mono_tls_set_domain" : (gpointer)mono_tls_set_domain;
293         case TLS_KEY_LMF:
294                 return name ? (gpointer)"mono_tls_set_lmf" : (gpointer)mono_tls_set_lmf;
295         case TLS_KEY_SGEN_THREAD_INFO:
296                 return name ? (gpointer)"mono_tls_set_sgen_thread_info" : (gpointer)mono_tls_set_sgen_thread_info;
297         case TLS_KEY_LMF_ADDR:
298                 return name ? (gpointer)"mono_tls_set_lmf_addr" : (gpointer)mono_tls_set_lmf_addr;
299         }
300         g_assert_not_reached ();
301         return NULL;
302 }
303
304 gpointer
305 mono_tls_get_tls_addr (MonoTlsKey key)
306 {
307 #ifdef HAVE_GET_TLS_ADDR
308         if (key == TLS_KEY_LMF) {
309 #if defined(USE_KW_THREAD)
310                 return &mono_tls_lmf;
311 #elif defined(TARGET_MACH)
312                 return mono_mach_get_tls_address_from_thread (pthread_self (), mono_tls_key_lmf);
313 #endif
314         }
315 #endif
316         /* Implement if we ever need for other targets/keys */
317         g_assert_not_reached ();
318         return NULL;
319 }
320
321 /* Getters for each tls key */
322 gpointer mono_tls_get_thread (void)
323 {
324         return MONO_TLS_GET_VALUE (mono_tls_thread, mono_tls_key_thread);
325 }
326
327 gpointer mono_tls_get_jit_tls (void)
328 {
329         return MONO_TLS_GET_VALUE (mono_tls_jit_tls, mono_tls_key_jit_tls);
330 }
331
332 gpointer mono_tls_get_domain (void)
333 {
334         return MONO_TLS_GET_VALUE (mono_tls_domain, mono_tls_key_domain);
335 }
336
337 gpointer mono_tls_get_lmf (void)
338 {
339         return MONO_TLS_GET_VALUE (mono_tls_lmf, mono_tls_key_lmf);
340 }
341
342 gpointer mono_tls_get_sgen_thread_info (void)
343 {
344         return MONO_TLS_GET_VALUE (mono_tls_sgen_thread_info, mono_tls_key_sgen_thread_info);
345 }
346
347 gpointer mono_tls_get_lmf_addr (void)
348 {
349         return MONO_TLS_GET_VALUE (mono_tls_lmf_addr, mono_tls_key_lmf_addr);
350 }
351
352 /* Setters for each tls key */
353 void mono_tls_set_thread (gpointer value)
354 {
355         MONO_TLS_SET_VALUE (mono_tls_thread, mono_tls_key_thread, value);
356 }
357
358 void mono_tls_set_jit_tls (gpointer value)
359 {
360         MONO_TLS_SET_VALUE (mono_tls_jit_tls, mono_tls_key_jit_tls, value);
361 }
362
363 void mono_tls_set_domain (gpointer value)
364 {
365         MONO_TLS_SET_VALUE (mono_tls_domain, mono_tls_key_domain, value);
366 }
367
368 void mono_tls_set_lmf (gpointer value)
369 {
370         MONO_TLS_SET_VALUE (mono_tls_lmf, mono_tls_key_lmf, value);
371 }
372
373 void mono_tls_set_sgen_thread_info (gpointer value)
374 {
375         MONO_TLS_SET_VALUE (mono_tls_sgen_thread_info, mono_tls_key_sgen_thread_info, value);
376 }
377
378 void mono_tls_set_lmf_addr (gpointer value)
379 {
380         MONO_TLS_SET_VALUE (mono_tls_lmf_addr, mono_tls_key_lmf_addr, value);
381 }