xref: /dflybsd-src/lib/libc/gen/tls.c (revision 678e8cc6002ecd3a79cdae95ac8b454437219fc5)
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 
3592df6c3eSDavid Xu #include <sys/cdefs.h>
36bc633d63SMatthew Dillon #include <sys/tls.h>
379e2ee207SJoerg Sonnenberger 
389e2ee207SJoerg Sonnenberger #include <machine/tls.h>
399e2ee207SJoerg Sonnenberger 
40b00401f0SDavid Xu #include <stdlib.h>
41b00401f0SDavid Xu #include <string.h>
42b00401f0SDavid Xu #include <elf.h>
43b00401f0SDavid Xu #include <assert.h>
44a1eee96aSMatthew Dillon #include <errno.h>
45a1eee96aSMatthew Dillon #include <unistd.h>
4692df6c3eSDavid Xu 
47b00401f0SDavid Xu #include "libc_private.h"
48b00401f0SDavid Xu 
4992df6c3eSDavid Xu __weak_reference(__libc_allocate_tls, _rtld_allocate_tls);
5092df6c3eSDavid Xu __weak_reference(__libc_free_tls, _rtld_free_tls);
51a1eee96aSMatthew Dillon __weak_reference(__libc_call_init, _rtld_call_init);
5292df6c3eSDavid Xu #ifdef __i386__
5392df6c3eSDavid Xu __weak_reference(___libc_tls_get_addr, ___tls_get_addr);
5492df6c3eSDavid Xu #endif
5592df6c3eSDavid Xu __weak_reference(__libc_tls_get_addr, __tls_get_addr);
56a1eee96aSMatthew Dillon __weak_reference(__libc_tls_get_addr_tcb, __tls_get_addr_tcb);
57a1eee96aSMatthew Dillon __weak_reference(_libc_init_tls, _init_tls);
5892df6c3eSDavid Xu 
59a1eee96aSMatthew Dillon struct tls_tcb *__libc_allocate_tls(void);
60f20fd431SJoerg Sonnenberger void __libc_free_tls(struct tls_tcb *tcb);
61b00401f0SDavid Xu 
629e2ee207SJoerg Sonnenberger #if !defined(RTLD_STATIC_TLS_VARIANT_II)
639e2ee207SJoerg Sonnenberger #error "Unsupported TLS layout"
64b00401f0SDavid Xu #endif
65b00401f0SDavid Xu 
66b00401f0SDavid Xu #ifndef PIC
67b00401f0SDavid Xu 
68b00401f0SDavid Xu #define round(size, align) \
69b00401f0SDavid Xu 	(((size) + (align) - 1) & ~((align) - 1))
70b00401f0SDavid Xu 
71b00401f0SDavid Xu static size_t tls_static_space;
72b00401f0SDavid Xu static size_t tls_init_size;
73b00401f0SDavid Xu static void *tls_init;
74a1eee96aSMatthew Dillon static struct tls_tcb *initial_tcb;
75b00401f0SDavid Xu #endif
76b00401f0SDavid Xu 
77b00401f0SDavid Xu #ifdef __i386__
78b00401f0SDavid Xu 
7992df6c3eSDavid Xu /* GNU ABI */
80b00401f0SDavid Xu 
8192df6c3eSDavid Xu void *___libc_tls_get_addr(void *ti) __attribute__((__regparm__(1)));
8292df6c3eSDavid Xu 
83b00401f0SDavid Xu __attribute__((__regparm__(1)))
84b00401f0SDavid Xu void *
8592df6c3eSDavid Xu ___libc_tls_get_addr(void *ti __unused)
86b00401f0SDavid Xu {
87b00401f0SDavid Xu 	return (0);
88b00401f0SDavid Xu }
89b00401f0SDavid Xu 
90b00401f0SDavid Xu #endif
91b00401f0SDavid Xu 
9292df6c3eSDavid Xu void *__libc_tls_get_addr(void *ti);
93a1eee96aSMatthew Dillon void *__libc_tls_get_addr_tcb(struct tls_tcb *, void *);
94bc633d63SMatthew Dillon 
95b00401f0SDavid Xu void *
9692df6c3eSDavid Xu __libc_tls_get_addr(void *ti __unused)
97b00401f0SDavid Xu {
98a1eee96aSMatthew Dillon 	return (NULL);
99a1eee96aSMatthew Dillon }
100a1eee96aSMatthew Dillon 
101a1eee96aSMatthew Dillon void *
102a1eee96aSMatthew Dillon __libc_tls_get_addr_tcb(struct tls_tcb *tcb __unused, void *got_ptr __unused)
103a1eee96aSMatthew Dillon {
104a1eee96aSMatthew Dillon 	return (NULL);
105b00401f0SDavid Xu }
106b00401f0SDavid Xu 
10792df6c3eSDavid Xu #ifndef PIC
10892df6c3eSDavid Xu 
109b00401f0SDavid Xu /*
110a1eee96aSMatthew Dillon  * Free Static TLS, weakly bound to _rtld_free_tls()
111b00401f0SDavid Xu  */
112b00401f0SDavid Xu void
113f20fd431SJoerg Sonnenberger __libc_free_tls(struct tls_tcb *tcb)
114b00401f0SDavid Xu {
115bc633d63SMatthew Dillon 	size_t data_size;
116b00401f0SDavid Xu 
117bc633d63SMatthew Dillon 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
118bc633d63SMatthew Dillon 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
119a1eee96aSMatthew Dillon 
120a1eee96aSMatthew Dillon 	if (tcb == initial_tcb) {
121a1eee96aSMatthew Dillon 		/* initial_tcb was allocated with sbrk(), cannot call free() */
122a1eee96aSMatthew Dillon 	} else {
123bc633d63SMatthew Dillon 		free((char *)tcb - data_size);
124b00401f0SDavid Xu 	}
125a1eee96aSMatthew Dillon }
126b00401f0SDavid Xu 
127b00401f0SDavid Xu /*
128a1eee96aSMatthew Dillon  * Allocate Static TLS, weakly bound to _rtld_allocate_tls()
129a378ce7dSJoerg Sonnenberger  *
130a1eee96aSMatthew Dillon  * NOTE!  There is a chicken-and-egg problem here because no TLS exists
131a1eee96aSMatthew Dillon  * on the first call into this function.
132b00401f0SDavid Xu  */
133bc633d63SMatthew Dillon struct tls_tcb *
134a1eee96aSMatthew Dillon __libc_allocate_tls(void)
135b00401f0SDavid Xu {
136bc633d63SMatthew Dillon 	size_t data_size;
137bc633d63SMatthew Dillon 	struct tls_tcb *tcb;
138b00401f0SDavid Xu 	Elf_Addr *dtv;
139b00401f0SDavid Xu 
140bc633d63SMatthew Dillon 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
141bc633d63SMatthew Dillon 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
142a378ce7dSJoerg Sonnenberger 
143a1eee96aSMatthew Dillon 	/*
144a1eee96aSMatthew Dillon 	 * Allocate space.  malloc() may require a working TLS segment
145a1eee96aSMatthew Dillon 	 * so we use sbrk() for main's TLS.
146a1eee96aSMatthew Dillon 	 */
147a1eee96aSMatthew Dillon 	if (initial_tcb == NULL)
14897c6bef2SMatthew Dillon 		tcb = sbrk(data_size + sizeof(*tcb) + 3 * sizeof(*dtv));
149a1eee96aSMatthew Dillon 	else
15097c6bef2SMatthew Dillon 		tcb = malloc(data_size + sizeof(*tcb) + 3 * sizeof(*dtv));
1519e2ee207SJoerg Sonnenberger 
152a1eee96aSMatthew Dillon 	tcb = (struct tls_tcb *)((char *)tcb + data_size);
153a1eee96aSMatthew Dillon 	dtv = (Elf_Addr *)(tcb + 1);
154a1eee96aSMatthew Dillon 
155a1eee96aSMatthew Dillon 	memset(tcb, 0, sizeof(*tcb));
1569e2ee207SJoerg Sonnenberger #ifdef RTLD_TCB_HAS_SELF_POINTER
1579e2ee207SJoerg Sonnenberger 	tcb->tcb_self = tcb;
1589e2ee207SJoerg Sonnenberger #endif
1599e2ee207SJoerg Sonnenberger 	tcb->tcb_dtv = dtv;
160b00401f0SDavid Xu 
161a1eee96aSMatthew Dillon 	/*
162a1eee96aSMatthew Dillon 	 * Dummy-up the module array.  A static binary has only one.  This
163a1eee96aSMatthew Dillon 	 * allows us to support the generic __tls_get_addr compiler ABI
164a1eee96aSMatthew Dillon 	 * function.  However, if there is no RTLD linked in, nothing in
165a1eee96aSMatthew Dillon 	 * the program should ever call __tls_get_addr (and our version
166a1eee96aSMatthew Dillon 	 * of it doesn't do anything).
167a1eee96aSMatthew Dillon 	 */
168b00401f0SDavid Xu 	dtv[0] = 1;
169b00401f0SDavid Xu 	dtv[1] = 1;
170bc633d63SMatthew Dillon 	dtv[2] = (Elf_Addr)((char *)tcb - tls_static_space);
171bc633d63SMatthew Dillon 
172bc633d63SMatthew Dillon 	memcpy((char *)tcb - tls_static_space,
173b00401f0SDavid Xu 		tls_init, tls_init_size);
174bc633d63SMatthew Dillon 	memset((char *)tcb - tls_static_space + tls_init_size,
175b00401f0SDavid Xu 		0, tls_static_space - tls_init_size);
176a378ce7dSJoerg Sonnenberger 
177a1eee96aSMatthew Dillon 	/*
178a1eee96aSMatthew Dillon 	 * Activate the initial TCB
179a1eee96aSMatthew Dillon 	 */
180a1eee96aSMatthew Dillon 	if (initial_tcb == NULL) {
181a1eee96aSMatthew Dillon 		initial_tcb = tcb;
182a378ce7dSJoerg Sonnenberger 		tls_set_tcb(tcb);
183a1eee96aSMatthew Dillon 	}
184a1eee96aSMatthew Dillon 	return (tcb);
185b00401f0SDavid Xu }
186b00401f0SDavid Xu 
18792df6c3eSDavid Xu #else
18892df6c3eSDavid Xu 
189bc633d63SMatthew Dillon struct tls_tcb *
190a1eee96aSMatthew Dillon __libc_allocate_tls(void)
19192df6c3eSDavid Xu {
192a1eee96aSMatthew Dillon 	return (NULL);
19392df6c3eSDavid Xu }
19492df6c3eSDavid Xu 
19592df6c3eSDavid Xu void
196f20fd431SJoerg Sonnenberger __libc_free_tls(struct tls_tcb *tcb __unused)
19792df6c3eSDavid Xu {
19892df6c3eSDavid Xu }
19992df6c3eSDavid Xu 
20092df6c3eSDavid Xu #endif /* PIC */
20192df6c3eSDavid Xu 
202a1eee96aSMatthew Dillon void
203a1eee96aSMatthew Dillon __libc_call_init(void)
204a1eee96aSMatthew Dillon {
205a1eee96aSMatthew Dillon }
206a1eee96aSMatthew Dillon 
207b00401f0SDavid Xu extern char **environ;
208b00401f0SDavid Xu 
209a1eee96aSMatthew Dillon struct tls_tcb *
210a1eee96aSMatthew Dillon _libc_init_tls(void)
211b00401f0SDavid Xu {
212a1eee96aSMatthew Dillon 	struct tls_tcb *tcb;
213a1eee96aSMatthew Dillon 
214b00401f0SDavid Xu #ifndef PIC
215a1eee96aSMatthew Dillon 	/*
216a1eee96aSMatthew Dillon 	 * If this is a static binary there is no RTLD and so we have not
217a1eee96aSMatthew Dillon 	 * yet calculated the static space requirement.  Do so now.
218a1eee96aSMatthew Dillon 	 */
219b00401f0SDavid Xu 	Elf_Addr *sp;
220b00401f0SDavid Xu 	Elf_Auxinfo *aux, *auxp;
221b00401f0SDavid Xu 	Elf_Phdr *phdr;
222b00401f0SDavid Xu 	size_t phent, phnum;
223b00401f0SDavid Xu 	int i;
224b00401f0SDavid Xu 
225b00401f0SDavid Xu 	sp = (Elf_Addr *) environ;
226b00401f0SDavid Xu 	while (*sp++ != 0)
227b00401f0SDavid Xu 		;
228b00401f0SDavid Xu 	aux = (Elf_Auxinfo *) sp;
229*678e8cc6SSascha Wildner 	phdr = NULL;
230b00401f0SDavid Xu 	phent = phnum = 0;
231b00401f0SDavid Xu 	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
232b00401f0SDavid Xu 		switch (auxp->a_type) {
233b00401f0SDavid Xu 		case AT_PHDR:
234b00401f0SDavid Xu 			phdr = auxp->a_un.a_ptr;
235b00401f0SDavid Xu 			break;
236b00401f0SDavid Xu 
237b00401f0SDavid Xu 		case AT_PHENT:
238b00401f0SDavid Xu 			phent = auxp->a_un.a_val;
239b00401f0SDavid Xu 			break;
240b00401f0SDavid Xu 
241b00401f0SDavid Xu 		case AT_PHNUM:
242b00401f0SDavid Xu 			phnum = auxp->a_un.a_val;
243b00401f0SDavid Xu 			break;
244b00401f0SDavid Xu 		}
245b00401f0SDavid Xu 	}
246*678e8cc6SSascha Wildner 	if (phdr == NULL || phent != sizeof(Elf_Phdr) || phnum == 0)
247b0e25abdSSascha Wildner 		return(NULL);
248b00401f0SDavid Xu 
249b00401f0SDavid Xu 	for (i = 0; (unsigned)i < phnum; i++) {
250b00401f0SDavid Xu 		if (phdr[i].p_type == PT_TLS) {
251b00401f0SDavid Xu 			tls_static_space = round(phdr[i].p_memsz,
252b00401f0SDavid Xu 			    phdr[i].p_align);
253b00401f0SDavid Xu 			tls_init_size = phdr[i].p_filesz;
254b00401f0SDavid Xu 			tls_init = (void*) phdr[i].p_vaddr;
255b00401f0SDavid Xu 		}
256b00401f0SDavid Xu 	}
257b00401f0SDavid Xu #endif
258a1eee96aSMatthew Dillon 
259a1eee96aSMatthew Dillon 	/*
260a1eee96aSMatthew Dillon 	 * Allocate the initial TLS segment.  The TLS has not been set up
261a1eee96aSMatthew Dillon 	 * yet for either the static or dynamic linked case (RTLD no longer
262a1eee96aSMatthew Dillon 	 * sets up an initial TLS segment for us).
263a1eee96aSMatthew Dillon 	 */
264a1eee96aSMatthew Dillon 	tcb = _libc_allocate_tls();
265a1eee96aSMatthew Dillon 	tls_set_tcb(tcb);
266a1eee96aSMatthew Dillon 	return(tcb);
267b00401f0SDavid Xu }
268a1eee96aSMatthew Dillon 
269a1eee96aSMatthew Dillon /*
270a1eee96aSMatthew Dillon  * Allocate a standard TLS.  This function is called by libc and by
271a1eee96aSMatthew Dillon  * thread libraries to create a new TCB with libc-related fields properly
272a1eee96aSMatthew Dillon  * initialized (whereas _rtld_allocate_tls() is unable to completely set
273a1eee96aSMatthew Dillon  * up the TCB).
274a1eee96aSMatthew Dillon  *
275a1eee96aSMatthew Dillon  * Note that this is different from __libc_allocate_tls which is the
276a1eee96aSMatthew Dillon  * weakly bound symbol that handles the case where _rtld_allocate_tls
277a1eee96aSMatthew Dillon  * does not exist.
278a1eee96aSMatthew Dillon  */
279a1eee96aSMatthew Dillon struct tls_tcb *
280a1eee96aSMatthew Dillon _libc_allocate_tls(void)
281a1eee96aSMatthew Dillon {
282a1eee96aSMatthew Dillon 	struct tls_tcb *tcb;
283a1eee96aSMatthew Dillon 
284a1eee96aSMatthew Dillon 	tcb = _rtld_allocate_tls();
285a1eee96aSMatthew Dillon 
286a1eee96aSMatthew Dillon #if 0
287a1eee96aSMatthew Dillon #if defined(__thread)
288a1eee96aSMatthew Dillon 	/* non-TLS libc */
289a1eee96aSMatthew Dillon 	tcb->tcb_errno_p = &errno;
290a1eee96aSMatthew Dillon #elif defined(PIC)
291a1eee96aSMatthew Dillon 	/* TLS libc dynamically linked */
292a1eee96aSMatthew Dillon 	tcb->tcb_errno_p = __tls_get_addr_tcb(tcb, __get_errno_GOT_ptr());
293a1eee96aSMatthew Dillon #else
294a1eee96aSMatthew Dillon 	/* TLS libc (threaded or unthreaded) */
295a1eee96aSMatthew Dillon 	tcb->tcb_errno_p = (void *)((char *)tcb + __get_errno_GS_offset());
296a1eee96aSMatthew Dillon #endif
297a1eee96aSMatthew Dillon #endif
298a1eee96aSMatthew Dillon 	return(tcb);
299a1eee96aSMatthew Dillon }
300a1eee96aSMatthew Dillon 
301