xref: /freebsd/lib/libc/gen/tls.c (revision d0e79aa362ed3a077477d26af7a40f64f4516e90)
10e1c7d0fSDoug Rabson /*-
20e1c7d0fSDoug Rabson  * Copyright (c) 2004 Doug Rabson
30e1c7d0fSDoug Rabson  * All rights reserved.
40e1c7d0fSDoug Rabson  *
50e1c7d0fSDoug Rabson  * Redistribution and use in source and binary forms, with or without
60e1c7d0fSDoug Rabson  * modification, are permitted provided that the following conditions
70e1c7d0fSDoug Rabson  * are met:
80e1c7d0fSDoug Rabson  * 1. Redistributions of source code must retain the above copyright
90e1c7d0fSDoug Rabson  *    notice, this list of conditions and the following disclaimer.
100e1c7d0fSDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
110e1c7d0fSDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
120e1c7d0fSDoug Rabson  *    documentation and/or other materials provided with the distribution.
130e1c7d0fSDoug Rabson  *
140e1c7d0fSDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
150e1c7d0fSDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
160e1c7d0fSDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
170e1c7d0fSDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
180e1c7d0fSDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
190e1c7d0fSDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
200e1c7d0fSDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
210e1c7d0fSDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
220e1c7d0fSDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
230e1c7d0fSDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
240e1c7d0fSDoug Rabson  * SUCH DAMAGE.
250e1c7d0fSDoug Rabson  *
260e1c7d0fSDoug Rabson  *	$FreeBSD$
270e1c7d0fSDoug Rabson  */
280e1c7d0fSDoug Rabson 
290e1c7d0fSDoug Rabson /*
300e1c7d0fSDoug Rabson  * Define stubs for TLS internals so that programs and libraries can
310e1c7d0fSDoug Rabson  * link. These functions will be replaced by functional versions at
320e1c7d0fSDoug Rabson  * runtime from ld-elf.so.1.
330e1c7d0fSDoug Rabson  */
340e1c7d0fSDoug Rabson 
3526896bdaSDavid Xu #include <sys/cdefs.h>
36ccd13c49SDoug Rabson #include <stdlib.h>
37ccd13c49SDoug Rabson #include <string.h>
38ccd13c49SDoug Rabson #include <elf.h>
3926896bdaSDavid Xu 
40ccd13c49SDoug Rabson #include "libc_private.h"
41ccd13c49SDoug Rabson 
42a4bd5210SJason Evans /* Provided by jemalloc to avoid bootstrapping issues. */
43*d0e79aa3SJason Evans void	*__je_bootstrap_malloc(size_t size);
44*d0e79aa3SJason Evans void	*__je_bootstrap_calloc(size_t num, size_t size);
45*d0e79aa3SJason Evans void	__je_bootstrap_free(void *ptr);
46a4bd5210SJason Evans 
4726896bdaSDavid Xu __weak_reference(__libc_allocate_tls, _rtld_allocate_tls);
4826896bdaSDavid Xu __weak_reference(__libc_free_tls, _rtld_free_tls);
4926896bdaSDavid Xu 
5026896bdaSDavid Xu #ifdef __i386__
5126896bdaSDavid Xu 
5226896bdaSDavid Xu __weak_reference(___libc_tls_get_addr, ___tls_get_addr);
5326896bdaSDavid Xu __attribute__((__regparm__(1))) void * ___libc_tls_get_addr(void *);
5426896bdaSDavid Xu 
5526896bdaSDavid Xu #endif
5626896bdaSDavid Xu 
5726896bdaSDavid Xu void * __libc_tls_get_addr(void *);
5826896bdaSDavid Xu __weak_reference(__libc_tls_get_addr, __tls_get_addr);
5926896bdaSDavid Xu 
6026896bdaSDavid Xu void *_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign);
6126896bdaSDavid Xu void _rtld_free_tls(void *tls, size_t tcbsize, size_t tcbalign);
6226896bdaSDavid Xu void *__libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign);
6326896bdaSDavid Xu void __libc_free_tls(void *tls, size_t tcbsize, size_t tcbalign);
6426896bdaSDavid Xu 
65e7d939bdSMarcel Moolenaar #if defined(__amd64__)
66b84c7a79SKip Macy #define TLS_TCB_ALIGN 16
67b84c7a79SKip Macy #elif defined(__powerpc__) || defined(__i386__) || defined(__arm__) || \
68d422e6f9SAndrew Turner     defined(__sparc64__) || defined(__mips__) || defined(__aarch64__)
69b84c7a79SKip Macy #define TLS_TCB_ALIGN sizeof(void *)
70b84c7a79SKip Macy #else
71b84c7a79SKip Macy #error TLS_TCB_ALIGN undefined for target architecture
72b84c7a79SKip Macy #endif
73b84c7a79SKip Macy 
74d422e6f9SAndrew Turner #if defined(__arm__) || defined(__mips__) || defined(__powerpc__) || \
75d422e6f9SAndrew Turner     defined(__aarch64__)
76ccd13c49SDoug Rabson #define TLS_VARIANT_I
77ccd13c49SDoug Rabson #endif
7854da2fb8SOleksandr Tymoshenko #if defined(__i386__) || defined(__amd64__) || defined(__sparc64__)
79ccd13c49SDoug Rabson #define TLS_VARIANT_II
80ccd13c49SDoug Rabson #endif
81ccd13c49SDoug Rabson 
82294246bbSEd Maste #ifndef PIC
83ccd13c49SDoug Rabson 
84ccd13c49SDoug Rabson #define round(size, align) \
85ccd13c49SDoug Rabson 	(((size) + (align) - 1) & ~((align) - 1))
86ccd13c49SDoug Rabson 
87ccd13c49SDoug Rabson static size_t tls_static_space;
88ccd13c49SDoug Rabson static size_t tls_init_size;
89ccd13c49SDoug Rabson static void *tls_init;
90ccd13c49SDoug Rabson #endif
910e1c7d0fSDoug Rabson 
920e1c7d0fSDoug Rabson #ifdef __i386__
930e1c7d0fSDoug Rabson 
9426896bdaSDavid Xu /* GNU ABI */
950e1c7d0fSDoug Rabson 
960e1c7d0fSDoug Rabson __attribute__((__regparm__(1)))
970e1c7d0fSDoug Rabson void *
9826896bdaSDavid Xu ___libc_tls_get_addr(void *ti __unused)
990e1c7d0fSDoug Rabson {
1000e1c7d0fSDoug Rabson 	return (0);
1010e1c7d0fSDoug Rabson }
1020e1c7d0fSDoug Rabson 
1030e1c7d0fSDoug Rabson #endif
1040e1c7d0fSDoug Rabson 
1050e1c7d0fSDoug Rabson void *
10626896bdaSDavid Xu __libc_tls_get_addr(void *ti __unused)
1070e1c7d0fSDoug Rabson {
1080e1c7d0fSDoug Rabson 	return (0);
1090e1c7d0fSDoug Rabson }
1100e1c7d0fSDoug Rabson 
111294246bbSEd Maste #ifndef PIC
11226896bdaSDavid Xu 
113ccd13c49SDoug Rabson #ifdef TLS_VARIANT_I
114ccd13c49SDoug Rabson 
1153614156cSMarcel Moolenaar #define	TLS_TCB_SIZE	(2 * sizeof(void *))
1163614156cSMarcel Moolenaar 
11717ceb495SDavid Xu /*
11896a93293SDavid Xu  * Free Static TLS using the Variant I method.
11917ceb495SDavid Xu  */
120ccd13c49SDoug Rabson void
1213614156cSMarcel Moolenaar __libc_free_tls(void *tcb, size_t tcbsize, size_t tcbalign __unused)
122ccd13c49SDoug Rabson {
123ccd13c49SDoug Rabson 	Elf_Addr *dtv;
1243614156cSMarcel Moolenaar 	Elf_Addr **tls;
125ccd13c49SDoug Rabson 
1263614156cSMarcel Moolenaar 	tls = (Elf_Addr **)((Elf_Addr)tcb + tcbsize - TLS_TCB_SIZE);
1273614156cSMarcel Moolenaar 	dtv = tls[0];
128*d0e79aa3SJason Evans 	__je_bootstrap_free(dtv);
129*d0e79aa3SJason Evans 	__je_bootstrap_free(tcb);
130ccd13c49SDoug Rabson }
131ccd13c49SDoug Rabson 
132ccd13c49SDoug Rabson /*
133ccd13c49SDoug Rabson  * Allocate Static TLS using the Variant I method.
134ccd13c49SDoug Rabson  */
1350e1c7d0fSDoug Rabson void *
1363614156cSMarcel Moolenaar __libc_allocate_tls(void *oldtcb, size_t tcbsize, size_t tcbalign __unused)
1370e1c7d0fSDoug Rabson {
138ccd13c49SDoug Rabson 	Elf_Addr *dtv;
1393614156cSMarcel Moolenaar 	Elf_Addr **tls;
1403614156cSMarcel Moolenaar 	char *tcb;
141ccd13c49SDoug Rabson 
1423614156cSMarcel Moolenaar 	if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE)
1433614156cSMarcel Moolenaar 		return (oldtcb);
144ccd13c49SDoug Rabson 
145*d0e79aa3SJason Evans 	tcb = __je_bootstrap_calloc(1, tls_static_space + tcbsize - TLS_TCB_SIZE);
1463614156cSMarcel Moolenaar 	tls = (Elf_Addr **)(tcb + tcbsize - TLS_TCB_SIZE);
1473614156cSMarcel Moolenaar 
1483614156cSMarcel Moolenaar 	if (oldtcb != NULL) {
14994368521SMarcel Moolenaar 		memcpy(tls, oldtcb, tls_static_space);
150*d0e79aa3SJason Evans 		__je_bootstrap_free(oldtcb);
1513614156cSMarcel Moolenaar 
1523614156cSMarcel Moolenaar 		/* Adjust the DTV. */
1533614156cSMarcel Moolenaar 		dtv = tls[0];
1543614156cSMarcel Moolenaar 		dtv[2] = (Elf_Addr)tls + TLS_TCB_SIZE;
1553614156cSMarcel Moolenaar 	} else {
156*d0e79aa3SJason Evans 		dtv = __je_bootstrap_malloc(3 * sizeof(Elf_Addr));
1573614156cSMarcel Moolenaar 		tls[0] = dtv;
158ccd13c49SDoug Rabson 		dtv[0] = 1;
159ccd13c49SDoug Rabson 		dtv[1] = 1;
1603614156cSMarcel Moolenaar 		dtv[2] = (Elf_Addr)tls + TLS_TCB_SIZE;
161ccd13c49SDoug Rabson 
1623614156cSMarcel Moolenaar 		if (tls_init_size > 0)
1633614156cSMarcel Moolenaar 			memcpy((void*)dtv[2], tls_init, tls_init_size);
1643614156cSMarcel Moolenaar 		if (tls_static_space > tls_init_size)
1653614156cSMarcel Moolenaar 			memset((void*)(dtv[2] + tls_init_size), 0,
1663614156cSMarcel Moolenaar 			    tls_static_space - tls_init_size);
1670e1c7d0fSDoug Rabson 	}
1680e1c7d0fSDoug Rabson 
1693614156cSMarcel Moolenaar 	return(tcb);
170ccd13c49SDoug Rabson }
171ccd13c49SDoug Rabson 
172ccd13c49SDoug Rabson #endif
173ccd13c49SDoug Rabson 
174ccd13c49SDoug Rabson #ifdef TLS_VARIANT_II
175ccd13c49SDoug Rabson 
1763614156cSMarcel Moolenaar #define	TLS_TCB_SIZE	(3 * sizeof(Elf_Addr))
1773614156cSMarcel Moolenaar 
178ccd13c49SDoug Rabson /*
179ccd13c49SDoug Rabson  * Free Static TLS using the Variant II method.
180ccd13c49SDoug Rabson  */
1810e1c7d0fSDoug Rabson void
18226896bdaSDavid Xu __libc_free_tls(void *tcb, size_t tcbsize __unused, size_t tcbalign)
1830e1c7d0fSDoug Rabson {
184ccd13c49SDoug Rabson 	size_t size;
185ccd13c49SDoug Rabson 	Elf_Addr* dtv;
186ccd13c49SDoug Rabson 	Elf_Addr tlsstart, tlsend;
187ccd13c49SDoug Rabson 
188ccd13c49SDoug Rabson 	/*
189ccd13c49SDoug Rabson 	 * Figure out the size of the initial TLS block so that we can
190ccd13c49SDoug Rabson 	 * find stuff which ___tls_get_addr() allocated dynamically.
191ccd13c49SDoug Rabson 	 */
192ccd13c49SDoug Rabson 	size = round(tls_static_space, tcbalign);
193ccd13c49SDoug Rabson 
194ccd13c49SDoug Rabson 	dtv = ((Elf_Addr**)tcb)[1];
195ccd13c49SDoug Rabson 	tlsend = (Elf_Addr) tcb;
196ccd13c49SDoug Rabson 	tlsstart = tlsend - size;
197*d0e79aa3SJason Evans 	__je_bootstrap_free((void*) tlsstart);
198*d0e79aa3SJason Evans 	__je_bootstrap_free(dtv);
199ccd13c49SDoug Rabson }
200ccd13c49SDoug Rabson 
201ccd13c49SDoug Rabson /*
202ccd13c49SDoug Rabson  * Allocate Static TLS using the Variant II method.
203ccd13c49SDoug Rabson  */
204ccd13c49SDoug Rabson void *
20526896bdaSDavid Xu __libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
206ccd13c49SDoug Rabson {
207ccd13c49SDoug Rabson 	size_t size;
208ccd13c49SDoug Rabson 	char *tls;
209ccd13c49SDoug Rabson 	Elf_Addr *dtv;
210ccd13c49SDoug Rabson 	Elf_Addr segbase, oldsegbase;
211ccd13c49SDoug Rabson 
212ccd13c49SDoug Rabson 	size = round(tls_static_space, tcbalign);
213ccd13c49SDoug Rabson 
2140031cdf4STim Kientzle 	if (tcbsize < 2 * sizeof(Elf_Addr))
2150031cdf4STim Kientzle 		tcbsize = 2 * sizeof(Elf_Addr);
216*d0e79aa3SJason Evans 	tls = __je_bootstrap_calloc(1, size + tcbsize);
217*d0e79aa3SJason Evans 	dtv = __je_bootstrap_malloc(3 * sizeof(Elf_Addr));
218ccd13c49SDoug Rabson 
219ccd13c49SDoug Rabson 	segbase = (Elf_Addr)(tls + size);
220ccd13c49SDoug Rabson 	((Elf_Addr*)segbase)[0] = segbase;
221ccd13c49SDoug Rabson 	((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv;
222ccd13c49SDoug Rabson 
223ccd13c49SDoug Rabson 	dtv[0] = 1;
224ccd13c49SDoug Rabson 	dtv[1] = 1;
225ccd13c49SDoug Rabson 	dtv[2] = segbase - tls_static_space;
226ccd13c49SDoug Rabson 
227ccd13c49SDoug Rabson 	if (oldtls) {
228ccd13c49SDoug Rabson 		/*
229ccd13c49SDoug Rabson 		 * Copy the static TLS block over whole.
230ccd13c49SDoug Rabson 		 */
231ccd13c49SDoug Rabson 		oldsegbase = (Elf_Addr) oldtls;
232ccd13c49SDoug Rabson 		memcpy((void *)(segbase - tls_static_space),
233ccd13c49SDoug Rabson 		    (const void *)(oldsegbase - tls_static_space),
234ccd13c49SDoug Rabson 		    tls_static_space);
235ccd13c49SDoug Rabson 
236ccd13c49SDoug Rabson 		/*
237ccd13c49SDoug Rabson 		 * We assume that this block was the one we created with
238ccd13c49SDoug Rabson 		 * allocate_initial_tls().
239ccd13c49SDoug Rabson 		 */
240ccd13c49SDoug Rabson 		_rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
241ccd13c49SDoug Rabson 	} else {
242ccd13c49SDoug Rabson 		memcpy((void *)(segbase - tls_static_space),
243ccd13c49SDoug Rabson 		    tls_init, tls_init_size);
244ccd13c49SDoug Rabson 		memset((void *)(segbase - tls_static_space + tls_init_size),
245ccd13c49SDoug Rabson 		    0, tls_static_space - tls_init_size);
246ccd13c49SDoug Rabson 	}
247ccd13c49SDoug Rabson 
248ccd13c49SDoug Rabson 	return (void*) segbase;
249ccd13c49SDoug Rabson }
250ccd13c49SDoug Rabson 
25126896bdaSDavid Xu #endif /* TLS_VARIANT_II */
25226896bdaSDavid Xu 
25326896bdaSDavid Xu #else
25426896bdaSDavid Xu 
25526896bdaSDavid Xu void *
25626896bdaSDavid Xu __libc_allocate_tls(void *oldtls __unused, size_t tcbsize __unused,
25726896bdaSDavid Xu 	size_t tcbalign __unused)
25826896bdaSDavid Xu {
25926896bdaSDavid Xu 	return (0);
26026896bdaSDavid Xu }
26126896bdaSDavid Xu 
26226896bdaSDavid Xu void
26326896bdaSDavid Xu __libc_free_tls(void *tcb __unused, size_t tcbsize __unused,
26426896bdaSDavid Xu 	size_t tcbalign __unused)
26526896bdaSDavid Xu {
26626896bdaSDavid Xu }
26726896bdaSDavid Xu 
268294246bbSEd Maste #endif /* PIC */
26926896bdaSDavid Xu 
27026896bdaSDavid Xu extern char **environ;
271ccd13c49SDoug Rabson 
272ccd13c49SDoug Rabson void
273ccd13c49SDoug Rabson _init_tls()
274ccd13c49SDoug Rabson {
275294246bbSEd Maste #ifndef PIC
276ccd13c49SDoug Rabson 	Elf_Addr *sp;
277ccd13c49SDoug Rabson 	Elf_Auxinfo *aux, *auxp;
278ccd13c49SDoug Rabson 	Elf_Phdr *phdr;
279ccd13c49SDoug Rabson 	size_t phent, phnum;
280ccd13c49SDoug Rabson 	int i;
2810e7e4e5fSDoug Rabson 	void *tls;
282ccd13c49SDoug Rabson 
283ccd13c49SDoug Rabson 	sp = (Elf_Addr *) environ;
284ccd13c49SDoug Rabson 	while (*sp++ != 0)
285ccd13c49SDoug Rabson 		;
286ccd13c49SDoug Rabson 	aux = (Elf_Auxinfo *) sp;
287ccd13c49SDoug Rabson 	phdr = 0;
288ccd13c49SDoug Rabson 	phent = phnum = 0;
289ccd13c49SDoug Rabson 	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
290ccd13c49SDoug Rabson 		switch (auxp->a_type) {
291ccd13c49SDoug Rabson 		case AT_PHDR:
292ccd13c49SDoug Rabson 			phdr = auxp->a_un.a_ptr;
293ccd13c49SDoug Rabson 			break;
294ccd13c49SDoug Rabson 
295ccd13c49SDoug Rabson 		case AT_PHENT:
296ccd13c49SDoug Rabson 			phent = auxp->a_un.a_val;
297ccd13c49SDoug Rabson 			break;
298ccd13c49SDoug Rabson 
299ccd13c49SDoug Rabson 		case AT_PHNUM:
300ccd13c49SDoug Rabson 			phnum = auxp->a_un.a_val;
301ccd13c49SDoug Rabson 			break;
302ccd13c49SDoug Rabson 		}
303ccd13c49SDoug Rabson 	}
304ccd13c49SDoug Rabson 	if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0)
305ccd13c49SDoug Rabson 		return;
306ccd13c49SDoug Rabson 
30726896bdaSDavid Xu 	for (i = 0; (unsigned) i < phnum; i++) {
308ccd13c49SDoug Rabson 		if (phdr[i].p_type == PT_TLS) {
309ccd13c49SDoug Rabson 			tls_static_space = round(phdr[i].p_memsz,
310ccd13c49SDoug Rabson 			    phdr[i].p_align);
311ccd13c49SDoug Rabson 			tls_init_size = phdr[i].p_filesz;
312ccd13c49SDoug Rabson 			tls_init = (void*) phdr[i].p_vaddr;
313ccd13c49SDoug Rabson 		}
314ccd13c49SDoug Rabson 	}
315ccd13c49SDoug Rabson 
31654da2fb8SOleksandr Tymoshenko #ifdef TLS_VARIANT_I
31754da2fb8SOleksandr Tymoshenko 	/*
31854da2fb8SOleksandr Tymoshenko 	 * tls_static_space should include space for TLS structure
31954da2fb8SOleksandr Tymoshenko 	 */
32054da2fb8SOleksandr Tymoshenko 	tls_static_space += TLS_TCB_SIZE;
32154da2fb8SOleksandr Tymoshenko #endif
32254da2fb8SOleksandr Tymoshenko 
323b84c7a79SKip Macy 	tls = _rtld_allocate_tls(NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN);
324ccd13c49SDoug Rabson 
325ccd13c49SDoug Rabson 	_set_tp(tls);
326ccd13c49SDoug Rabson #endif
3270e1c7d0fSDoug Rabson }
328