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