xref: /freebsd/lib/libc/gen/tls.c (revision e4e9813eb92cd7c4d4b819a8fbed5cbd3d92f5d8)
1 /*-
2  * Copyright (c) 2004 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$FreeBSD$
27  */
28 
29 /*
30  * Define stubs for TLS internals so that programs and libraries can
31  * link. These functions will be replaced by functional versions at
32  * runtime from ld-elf.so.1.
33  */
34 
35 #include <sys/cdefs.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <elf.h>
39 #include <assert.h>
40 
41 #include "libc_private.h"
42 
43 __weak_reference(__libc_allocate_tls, _rtld_allocate_tls);
44 __weak_reference(__libc_free_tls, _rtld_free_tls);
45 
46 #ifdef __i386__
47 
48 __weak_reference(___libc_tls_get_addr, ___tls_get_addr);
49 __attribute__((__regparm__(1))) void * ___libc_tls_get_addr(void *);
50 
51 #endif
52 
53 void * __libc_tls_get_addr(void *);
54 __weak_reference(__libc_tls_get_addr, __tls_get_addr);
55 
56 void *_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign);
57 void _rtld_free_tls(void *tls, size_t tcbsize, size_t tcbalign);
58 void *__libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign);
59 void __libc_free_tls(void *tls, size_t tcbsize, size_t tcbalign);
60 
61 #if defined(__ia64__) || defined(__alpha__) || defined(__powerpc__)
62 #define TLS_VARIANT_I
63 #endif
64 #if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \
65     defined(__arm__)
66 #define TLS_VARIANT_II
67 #endif
68 
69 #ifndef PIC
70 
71 #define round(size, align) \
72 	(((size) + (align) - 1) & ~((align) - 1))
73 
74 static size_t tls_static_space;
75 static size_t tls_init_size;
76 #ifdef TLS_VARIANT_I
77 static size_t tls_init_offset;
78 #endif
79 static void *tls_init;
80 #endif
81 
82 #ifdef __i386__
83 
84 /* GNU ABI */
85 
86 __attribute__((__regparm__(1)))
87 void *
88 ___libc_tls_get_addr(void *ti __unused)
89 {
90 	return (0);
91 }
92 
93 #endif
94 
95 void *
96 __libc_tls_get_addr(void *ti __unused)
97 {
98 	return (0);
99 }
100 
101 #ifndef PIC
102 
103 #ifdef TLS_VARIANT_I
104 
105 /*
106  * Free Static TLS using the Variant I method.
107  */
108 void
109 __libc_free_tls(void *tls, size_t tcbsize __unused, size_t tcbalign __unused)
110 {
111 	Elf_Addr* dtv;
112 
113 	dtv = ((Elf_Addr**)tls)[0];
114 	free(tls);
115 	free(dtv);
116 }
117 
118 /*
119  * Allocate Static TLS using the Variant I method.
120  */
121 void *
122 __libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign __unused)
123 {
124 	size_t size;
125 	char *tls;
126 	Elf_Addr *dtv;
127 
128 	size = tls_static_space;
129 	if (size < tcbsize)
130 		size = tcbsize;
131 
132 	tls = calloc(1, size);
133 	dtv = malloc(3 * sizeof(Elf_Addr));
134 
135 	*(Elf_Addr **) tls = dtv;
136 
137 	dtv[0] = 1;
138 	dtv[1] = 1;
139 	dtv[2] = (Elf_Addr)(tls + tls_init_offset);
140 	if (oldtls) {
141 		/*
142 		 * Copy the static TLS block over whole.
143 		 */
144 		memcpy(tls + tls_init_offset,
145 		    (char *)oldtls + tls_init_offset,
146 		    tls_static_space - tls_init_offset);
147 
148 		/*
149 		 * We assume that this block was the one we created with
150 		 * allocate_initial_tls().
151 		 */
152 		_rtld_free_tls(oldtls, 2 * sizeof(Elf_Addr), sizeof(Elf_Addr));
153 	} else {
154 		memcpy(tls + tls_init_offset, tls_init, tls_init_size);
155 		memset(tls + tls_init_offset + tls_init_size,
156 		    0, tls_static_space - tls_init_size);
157 	}
158 
159 	return tls;
160 }
161 
162 #endif
163 
164 #ifdef TLS_VARIANT_II
165 
166 /*
167  * Free Static TLS using the Variant II method.
168  */
169 void
170 __libc_free_tls(void *tcb, size_t tcbsize __unused, size_t tcbalign)
171 {
172 	size_t size;
173 	Elf_Addr* dtv;
174 	Elf_Addr tlsstart, tlsend;
175 
176 	/*
177 	 * Figure out the size of the initial TLS block so that we can
178 	 * find stuff which ___tls_get_addr() allocated dynamically.
179 	 */
180 	size = round(tls_static_space, tcbalign);
181 
182 	dtv = ((Elf_Addr**)tcb)[1];
183 	tlsend = (Elf_Addr) tcb;
184 	tlsstart = tlsend - size;
185 	free((void*) tlsstart);
186 	free(dtv);
187 }
188 
189 /*
190  * Allocate Static TLS using the Variant II method.
191  */
192 void *
193 __libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
194 {
195 	size_t size;
196 	char *tls;
197 	Elf_Addr *dtv;
198 	Elf_Addr segbase, oldsegbase;
199 
200 	size = round(tls_static_space, tcbalign);
201 
202 	assert(tcbsize >= 2*sizeof(Elf_Addr));
203 	tls = calloc(1, size + tcbsize);
204 	dtv = malloc(3 * sizeof(Elf_Addr));
205 
206 	segbase = (Elf_Addr)(tls + size);
207 	((Elf_Addr*)segbase)[0] = segbase;
208 	((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv;
209 
210 	dtv[0] = 1;
211 	dtv[1] = 1;
212 	dtv[2] = segbase - tls_static_space;
213 
214 	if (oldtls) {
215 		/*
216 		 * Copy the static TLS block over whole.
217 		 */
218 		oldsegbase = (Elf_Addr) oldtls;
219 		memcpy((void *)(segbase - tls_static_space),
220 		    (const void *)(oldsegbase - tls_static_space),
221 		    tls_static_space);
222 
223 		/*
224 		 * We assume that this block was the one we created with
225 		 * allocate_initial_tls().
226 		 */
227 		_rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
228 	} else {
229 		memcpy((void *)(segbase - tls_static_space),
230 		    tls_init, tls_init_size);
231 		memset((void *)(segbase - tls_static_space + tls_init_size),
232 		    0, tls_static_space - tls_init_size);
233 	}
234 
235 	return (void*) segbase;
236 }
237 
238 #endif /* TLS_VARIANT_II */
239 
240 #else
241 
242 void *
243 __libc_allocate_tls(void *oldtls __unused, size_t tcbsize __unused,
244 	size_t tcbalign __unused)
245 {
246 	return (0);
247 }
248 
249 void
250 __libc_free_tls(void *tcb __unused, size_t tcbsize __unused,
251 	size_t tcbalign __unused)
252 {
253 }
254 
255 #endif /* PIC */
256 
257 extern char **environ;
258 
259 void
260 _init_tls()
261 {
262 #ifndef PIC
263 	Elf_Addr *sp;
264 	Elf_Auxinfo *aux, *auxp;
265 	Elf_Phdr *phdr;
266 	size_t phent, phnum;
267 	int i;
268 	void *tls;
269 
270 	sp = (Elf_Addr *) environ;
271 	while (*sp++ != 0)
272 		;
273 	aux = (Elf_Auxinfo *) sp;
274 	phdr = 0;
275 	phent = phnum = 0;
276 	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
277 		switch (auxp->a_type) {
278 		case AT_PHDR:
279 			phdr = auxp->a_un.a_ptr;
280 			break;
281 
282 		case AT_PHENT:
283 			phent = auxp->a_un.a_val;
284 			break;
285 
286 		case AT_PHNUM:
287 			phnum = auxp->a_un.a_val;
288 			break;
289 		}
290 	}
291 	if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0)
292 		return;
293 
294 	for (i = 0; (unsigned) i < phnum; i++) {
295 		if (phdr[i].p_type == PT_TLS) {
296 #ifdef TLS_VARIANT_I
297 			tls_static_space = round(2*sizeof(Elf_Addr),
298 			    phdr[i].p_align) + phdr[i].p_memsz;
299 			tls_init_offset = round(2*sizeof(Elf_Addr),
300 			    phdr[i].p_align);
301 #else
302 			tls_static_space = round(phdr[i].p_memsz,
303 			    phdr[i].p_align);
304 #endif
305 			tls_init_size = phdr[i].p_filesz;
306 			tls_init = (void*) phdr[i].p_vaddr;
307 		}
308 	}
309 
310 	tls = _rtld_allocate_tls(NULL, 3*sizeof(Elf_Addr),
311 	    sizeof(Elf_Addr));
312 
313 	_set_tp(tls);
314 #endif
315 }
316