xref: /dflybsd-src/lib/libc/gen/tls.c (revision ccd5664d71c00ee493975d76c9e8d0921109397d)
1b00401f0SDavid Xu /*-
2b00401f0SDavid Xu  * Copyright (c) 2004 Doug Rabson
3b00401f0SDavid Xu  * All rights reserved.
4b00401f0SDavid Xu  *
5b00401f0SDavid Xu  * Redistribution and use in source and binary forms, with or without
6b00401f0SDavid Xu  * modification, are permitted provided that the following conditions
7b00401f0SDavid Xu  * are met:
8b00401f0SDavid Xu  * 1. Redistributions of source code must retain the above copyright
9b00401f0SDavid Xu  *    notice, this list of conditions and the following disclaimer.
10b00401f0SDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
11b00401f0SDavid Xu  *    notice, this list of conditions and the following disclaimer in the
12b00401f0SDavid Xu  *    documentation and/or other materials provided with the distribution.
13b00401f0SDavid Xu  *
14b00401f0SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15b00401f0SDavid Xu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16b00401f0SDavid Xu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17b00401f0SDavid Xu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18b00401f0SDavid Xu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19b00401f0SDavid Xu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20b00401f0SDavid Xu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21b00401f0SDavid Xu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22b00401f0SDavid Xu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23b00401f0SDavid Xu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24b00401f0SDavid Xu  * SUCH DAMAGE.
25b00401f0SDavid Xu  *
26b00401f0SDavid Xu  *	$FreeBSD: src/lib/libc/gen/tls.c,v 1.7 2005/03/01 23:42:00 davidxu Exp $
27b00401f0SDavid Xu  */
28b00401f0SDavid Xu 
29b00401f0SDavid Xu /*
30b00401f0SDavid Xu  * Define stubs for TLS internals so that programs and libraries can
31b00401f0SDavid Xu  * link. These functions will be replaced by functional versions at
32b00401f0SDavid Xu  * runtime from ld-elf.so.1.
33b00401f0SDavid Xu  */
34b00401f0SDavid Xu 
35965b839fSSascha Wildner #include <sys/param.h>
36bc633d63SMatthew Dillon #include <sys/tls.h>
37dc676eaeSMatthew Dillon #include <sys/mman.h>
389e2ee207SJoerg Sonnenberger 
399e2ee207SJoerg Sonnenberger #include <machine/tls.h>
409e2ee207SJoerg Sonnenberger 
41b00401f0SDavid Xu #include <stdlib.h>
42b00401f0SDavid Xu #include <string.h>
43b00401f0SDavid Xu #include <elf.h>
44b00401f0SDavid Xu #include <assert.h>
45a1eee96aSMatthew Dillon #include <errno.h>
46a1eee96aSMatthew Dillon #include <unistd.h>
4792df6c3eSDavid Xu 
48b00401f0SDavid Xu #include "libc_private.h"
49b00401f0SDavid Xu 
50a1eee96aSMatthew Dillon struct tls_tcb *__libc_allocate_tls(void);
51f20fd431SJoerg Sonnenberger void __libc_free_tls(struct tls_tcb *tcb);
52*a32e3ba6SSascha Wildner void __libc_call_init(void);
53b00401f0SDavid Xu 
549e2ee207SJoerg Sonnenberger #if !defined(RTLD_STATIC_TLS_VARIANT_II)
559e2ee207SJoerg Sonnenberger #error "Unsupported TLS layout"
56b00401f0SDavid Xu #endif
57b00401f0SDavid Xu 
58b00401f0SDavid Xu #ifndef PIC
59b00401f0SDavid Xu 
60b00401f0SDavid Xu static size_t tls_static_space;
61b00401f0SDavid Xu static size_t tls_init_size;
62b00401f0SDavid Xu static void *tls_init;
63a1eee96aSMatthew Dillon static struct tls_tcb *initial_tcb;
64b00401f0SDavid Xu #endif
65b00401f0SDavid Xu 
6692df6c3eSDavid Xu void *__libc_tls_get_addr(void *ti);
67a1eee96aSMatthew Dillon void *__libc_tls_get_addr_tcb(struct tls_tcb *, void *);
68bc633d63SMatthew Dillon 
69b00401f0SDavid Xu void *
__libc_tls_get_addr(void * ti __unused)7092df6c3eSDavid Xu __libc_tls_get_addr(void *ti __unused)
71b00401f0SDavid Xu {
72a1eee96aSMatthew Dillon 	return (NULL);
73a1eee96aSMatthew Dillon }
74a1eee96aSMatthew Dillon 
75a1eee96aSMatthew Dillon void *
__libc_tls_get_addr_tcb(struct tls_tcb * tcb __unused,void * got_ptr __unused)76a1eee96aSMatthew Dillon __libc_tls_get_addr_tcb(struct tls_tcb *tcb __unused, void *got_ptr __unused)
77a1eee96aSMatthew Dillon {
78a1eee96aSMatthew Dillon 	return (NULL);
79b00401f0SDavid Xu }
80b00401f0SDavid Xu 
8192df6c3eSDavid Xu #ifndef PIC
8292df6c3eSDavid Xu 
83b00401f0SDavid Xu /*
84a1eee96aSMatthew Dillon  * Free Static TLS, weakly bound to _rtld_free_tls()
85b00401f0SDavid Xu  */
86b00401f0SDavid Xu void
__libc_free_tls(struct tls_tcb * tcb)87f20fd431SJoerg Sonnenberger __libc_free_tls(struct tls_tcb *tcb)
88b00401f0SDavid Xu {
89bc633d63SMatthew Dillon 	size_t data_size;
90b00401f0SDavid Xu 
91bc633d63SMatthew Dillon 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
92bc633d63SMatthew Dillon 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
93a1eee96aSMatthew Dillon 
94a1eee96aSMatthew Dillon 	if (tcb == initial_tcb) {
95dc676eaeSMatthew Dillon 		/* initial_tcb was allocated with mmap(), cannot call free() */
96a1eee96aSMatthew Dillon 	} else {
97bc633d63SMatthew Dillon 		free((char *)tcb - data_size);
98b00401f0SDavid Xu 	}
99a1eee96aSMatthew Dillon }
100b00401f0SDavid Xu 
101b00401f0SDavid Xu /*
102a1eee96aSMatthew Dillon  * Allocate Static TLS, weakly bound to _rtld_allocate_tls()
103a378ce7dSJoerg Sonnenberger  *
104a1eee96aSMatthew Dillon  * NOTE!  There is a chicken-and-egg problem here because no TLS exists
105a1eee96aSMatthew Dillon  * on the first call into this function.
106b00401f0SDavid Xu  */
107bc633d63SMatthew Dillon struct tls_tcb *
__libc_allocate_tls(void)108a1eee96aSMatthew Dillon __libc_allocate_tls(void)
109b00401f0SDavid Xu {
110bc633d63SMatthew Dillon 	size_t data_size;
111bc633d63SMatthew Dillon 	struct tls_tcb *tcb;
112b00401f0SDavid Xu 	Elf_Addr *dtv;
113b00401f0SDavid Xu 
114bc633d63SMatthew Dillon 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
115bc633d63SMatthew Dillon 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
116a378ce7dSJoerg Sonnenberger 
117a1eee96aSMatthew Dillon 	/*
118a1eee96aSMatthew Dillon 	 * Allocate space.  malloc() may require a working TLS segment
119dc676eaeSMatthew Dillon 	 * so we use sbrk() for main's TLS.  Oops, but in order to be
120dc676eaeSMatthew Dillon 	 * compatible with older kernels we cannot use sbrk() because it
121dc676eaeSMatthew Dillon 	 * will generate an errno (which needs the TLS), so use mmap().
122a1eee96aSMatthew Dillon 	 */
123dc676eaeSMatthew Dillon 	if (initial_tcb == NULL) {
124dc676eaeSMatthew Dillon 		size_t bytes;
125dc676eaeSMatthew Dillon 
126dc676eaeSMatthew Dillon 		bytes = data_size + sizeof(*tcb) + 3 * sizeof(*dtv);
127dc676eaeSMatthew Dillon 		bytes = (bytes + PAGE_MASK) & ~(size_t)PAGE_MASK;
128dc676eaeSMatthew Dillon 		tcb = mmap((void *)1, bytes,
129dc676eaeSMatthew Dillon 			   PROT_READ | PROT_WRITE,
130dc676eaeSMatthew Dillon 			   MAP_PRIVATE | MAP_ANON,
131dc676eaeSMatthew Dillon 			   -1, 0);
132dc676eaeSMatthew Dillon 	} else {
13397c6bef2SMatthew Dillon 		tcb = malloc(data_size + sizeof(*tcb) + 3 * sizeof(*dtv));
134dc676eaeSMatthew Dillon 	}
1359e2ee207SJoerg Sonnenberger 
136a1eee96aSMatthew Dillon 	tcb = (struct tls_tcb *)((char *)tcb + data_size);
137a1eee96aSMatthew Dillon 	dtv = (Elf_Addr *)(tcb + 1);
138a1eee96aSMatthew Dillon 
139a1eee96aSMatthew Dillon 	memset(tcb, 0, sizeof(*tcb));
1409e2ee207SJoerg Sonnenberger #ifdef RTLD_TCB_HAS_SELF_POINTER
1419e2ee207SJoerg Sonnenberger 	tcb->tcb_self = tcb;
1429e2ee207SJoerg Sonnenberger #endif
1439e2ee207SJoerg Sonnenberger 	tcb->tcb_dtv = dtv;
144b00401f0SDavid Xu 
145a1eee96aSMatthew Dillon 	/*
146a1eee96aSMatthew Dillon 	 * Dummy-up the module array.  A static binary has only one.  This
147a1eee96aSMatthew Dillon 	 * allows us to support the generic __tls_get_addr compiler ABI
148a1eee96aSMatthew Dillon 	 * function.  However, if there is no RTLD linked in, nothing in
149a1eee96aSMatthew Dillon 	 * the program should ever call __tls_get_addr (and our version
150a1eee96aSMatthew Dillon 	 * of it doesn't do anything).
151a1eee96aSMatthew Dillon 	 */
152b00401f0SDavid Xu 	dtv[0] = 1;
153b00401f0SDavid Xu 	dtv[1] = 1;
154bc633d63SMatthew Dillon 	dtv[2] = (Elf_Addr)((char *)tcb - tls_static_space);
155bc633d63SMatthew Dillon 
156bc633d63SMatthew Dillon 	memcpy((char *)tcb - tls_static_space,
157b00401f0SDavid Xu 		tls_init, tls_init_size);
158bc633d63SMatthew Dillon 	memset((char *)tcb - tls_static_space + tls_init_size,
159b00401f0SDavid Xu 		0, tls_static_space - tls_init_size);
160a378ce7dSJoerg Sonnenberger 
161a1eee96aSMatthew Dillon 	/*
162a1eee96aSMatthew Dillon 	 * Activate the initial TCB
163a1eee96aSMatthew Dillon 	 */
164a1eee96aSMatthew Dillon 	if (initial_tcb == NULL) {
165a1eee96aSMatthew Dillon 		initial_tcb = tcb;
166a378ce7dSJoerg Sonnenberger 		tls_set_tcb(tcb);
167a1eee96aSMatthew Dillon 	}
168a1eee96aSMatthew Dillon 	return (tcb);
169b00401f0SDavid Xu }
170b00401f0SDavid Xu 
17192df6c3eSDavid Xu #else
17292df6c3eSDavid Xu 
173bc633d63SMatthew Dillon struct tls_tcb *
__libc_allocate_tls(void)174a1eee96aSMatthew Dillon __libc_allocate_tls(void)
17592df6c3eSDavid Xu {
176a1eee96aSMatthew Dillon 	return (NULL);
17792df6c3eSDavid Xu }
17892df6c3eSDavid Xu 
17992df6c3eSDavid Xu void
__libc_free_tls(struct tls_tcb * tcb __unused)180f20fd431SJoerg Sonnenberger __libc_free_tls(struct tls_tcb *tcb __unused)
18192df6c3eSDavid Xu {
18292df6c3eSDavid Xu }
18392df6c3eSDavid Xu 
18492df6c3eSDavid Xu #endif /* PIC */
18592df6c3eSDavid Xu 
186a1eee96aSMatthew Dillon void
__libc_call_init(void)187a1eee96aSMatthew Dillon __libc_call_init(void)
188a1eee96aSMatthew Dillon {
189a1eee96aSMatthew Dillon }
190a1eee96aSMatthew Dillon 
191b00401f0SDavid Xu extern char **environ;
192b00401f0SDavid Xu 
193a1eee96aSMatthew Dillon struct tls_tcb *
_libc_init_tls(void)194a1eee96aSMatthew Dillon _libc_init_tls(void)
195b00401f0SDavid Xu {
196a1eee96aSMatthew Dillon 	struct tls_tcb *tcb;
197a1eee96aSMatthew Dillon 
198b00401f0SDavid Xu #ifndef PIC
199a1eee96aSMatthew Dillon 	/*
200a1eee96aSMatthew Dillon 	 * If this is a static binary there is no RTLD and so we have not
201a1eee96aSMatthew Dillon 	 * yet calculated the static space requirement.  Do so now.
202a1eee96aSMatthew Dillon 	 */
203b00401f0SDavid Xu 	Elf_Addr *sp;
204b00401f0SDavid Xu 	Elf_Auxinfo *aux, *auxp;
205b00401f0SDavid Xu 	Elf_Phdr *phdr;
206b00401f0SDavid Xu 	size_t phent, phnum;
207b00401f0SDavid Xu 	int i;
208b00401f0SDavid Xu 
209b00401f0SDavid Xu 	sp = (Elf_Addr *) environ;
210b00401f0SDavid Xu 	while (*sp++ != 0)
211b00401f0SDavid Xu 		;
212b00401f0SDavid Xu 	aux = (Elf_Auxinfo *) sp;
213678e8cc6SSascha Wildner 	phdr = NULL;
214b00401f0SDavid Xu 	phent = phnum = 0;
215b00401f0SDavid Xu 	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
216b00401f0SDavid Xu 		switch (auxp->a_type) {
217b00401f0SDavid Xu 		case AT_PHDR:
218b00401f0SDavid Xu 			phdr = auxp->a_un.a_ptr;
219b00401f0SDavid Xu 			break;
220b00401f0SDavid Xu 
221b00401f0SDavid Xu 		case AT_PHENT:
222b00401f0SDavid Xu 			phent = auxp->a_un.a_val;
223b00401f0SDavid Xu 			break;
224b00401f0SDavid Xu 
225b00401f0SDavid Xu 		case AT_PHNUM:
226b00401f0SDavid Xu 			phnum = auxp->a_un.a_val;
227b00401f0SDavid Xu 			break;
228b00401f0SDavid Xu 		}
229b00401f0SDavid Xu 	}
230678e8cc6SSascha Wildner 	if (phdr == NULL || phent != sizeof(Elf_Phdr) || phnum == 0)
231b0e25abdSSascha Wildner 		return(NULL);
232b00401f0SDavid Xu 
233b00401f0SDavid Xu 	for (i = 0; (unsigned)i < phnum; i++) {
234b00401f0SDavid Xu 		if (phdr[i].p_type == PT_TLS) {
235965b839fSSascha Wildner 			tls_static_space = roundup2(phdr[i].p_memsz,
236b00401f0SDavid Xu 			    phdr[i].p_align);
237b00401f0SDavid Xu 			tls_init_size = phdr[i].p_filesz;
238b00401f0SDavid Xu 			tls_init = (void*) phdr[i].p_vaddr;
239b00401f0SDavid Xu 		}
240b00401f0SDavid Xu 	}
241b00401f0SDavid Xu #endif
242a1eee96aSMatthew Dillon 
243a1eee96aSMatthew Dillon 	/*
244a1eee96aSMatthew Dillon 	 * Allocate the initial TLS segment.  The TLS has not been set up
245a1eee96aSMatthew Dillon 	 * yet for either the static or dynamic linked case (RTLD no longer
246a1eee96aSMatthew Dillon 	 * sets up an initial TLS segment for us).
247a1eee96aSMatthew Dillon 	 */
248a1eee96aSMatthew Dillon 	tcb = _libc_allocate_tls();
249a1eee96aSMatthew Dillon 	tls_set_tcb(tcb);
250a1eee96aSMatthew Dillon 	return(tcb);
251b00401f0SDavid Xu }
252a1eee96aSMatthew Dillon 
253a1eee96aSMatthew Dillon /*
254a1eee96aSMatthew Dillon  * Allocate a standard TLS.  This function is called by libc and by
255a1eee96aSMatthew Dillon  * thread libraries to create a new TCB with libc-related fields properly
256a1eee96aSMatthew Dillon  * initialized (whereas _rtld_allocate_tls() is unable to completely set
257a1eee96aSMatthew Dillon  * up the TCB).
258a1eee96aSMatthew Dillon  *
259a1eee96aSMatthew Dillon  * Note that this is different from __libc_allocate_tls which is the
260a1eee96aSMatthew Dillon  * weakly bound symbol that handles the case where _rtld_allocate_tls
261a1eee96aSMatthew Dillon  * does not exist.
262a1eee96aSMatthew Dillon  */
263a1eee96aSMatthew Dillon struct tls_tcb *
_libc_allocate_tls(void)264a1eee96aSMatthew Dillon _libc_allocate_tls(void)
265a1eee96aSMatthew Dillon {
266a1eee96aSMatthew Dillon 	struct tls_tcb *tcb;
267a1eee96aSMatthew Dillon 
268a1eee96aSMatthew Dillon 	tcb = _rtld_allocate_tls();
269a1eee96aSMatthew Dillon 	return(tcb);
270a1eee96aSMatthew Dillon }
271a1eee96aSMatthew Dillon 
272f8406b33Szrj __weak_reference(__libc_allocate_tls, _rtld_allocate_tls);
273f8406b33Szrj __weak_reference(__libc_free_tls, _rtld_free_tls);
274f8406b33Szrj __weak_reference(__libc_call_init, _rtld_call_init);
275f8406b33Szrj __weak_reference(__libc_tls_get_addr, __tls_get_addr);
276f8406b33Szrj __weak_reference(__libc_tls_get_addr_tcb, __tls_get_addr_tcb);
277f8406b33Szrj __weak_reference(_libc_init_tls, _init_tls);
278