xref: /dflybsd-src/lib/libc/gen/tls.c (revision a378ce7d5ac32e9f2633d2c798abaeda34b8b7f3)
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 $
27*a378ce7dSJoerg Sonnenberger  *	$DragonFly: src/lib/libc/gen/tls.c,v 1.8 2005/04/29 22:00:20 joerg Exp $
28b00401f0SDavid Xu  */
29b00401f0SDavid Xu 
30b00401f0SDavid Xu /*
31b00401f0SDavid Xu  * Define stubs for TLS internals so that programs and libraries can
32b00401f0SDavid Xu  * link. These functions will be replaced by functional versions at
33b00401f0SDavid Xu  * runtime from ld-elf.so.1.
34b00401f0SDavid Xu  */
35b00401f0SDavid Xu 
3692df6c3eSDavid Xu #include <sys/cdefs.h>
37bc633d63SMatthew Dillon #include <sys/tls.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>
4592df6c3eSDavid Xu 
46b00401f0SDavid Xu #include "libc_private.h"
47b00401f0SDavid Xu 
4892df6c3eSDavid Xu __weak_reference(__libc_allocate_tls, _rtld_allocate_tls);
4992df6c3eSDavid Xu __weak_reference(__libc_free_tls, _rtld_free_tls);
5092df6c3eSDavid Xu #ifdef __i386__
5192df6c3eSDavid Xu __weak_reference(___libc_tls_get_addr, ___tls_get_addr);
5292df6c3eSDavid Xu #endif
5392df6c3eSDavid Xu __weak_reference(__libc_tls_get_addr, __tls_get_addr);
5492df6c3eSDavid Xu 
55f20fd431SJoerg Sonnenberger struct tls_tcb *__libc_allocate_tls(struct tls_tcb *old_tcb);
56f20fd431SJoerg Sonnenberger void __libc_free_tls(struct tls_tcb *tcb);
57b00401f0SDavid Xu 
589e2ee207SJoerg Sonnenberger #if !defined(RTLD_STATIC_TLS_VARIANT_II)
599e2ee207SJoerg Sonnenberger #error "Unsupported TLS layout"
60b00401f0SDavid Xu #endif
61b00401f0SDavid Xu 
62b00401f0SDavid Xu #ifndef PIC
63b00401f0SDavid Xu 
64b00401f0SDavid Xu #define round(size, align) \
65b00401f0SDavid Xu 	(((size) + (align) - 1) & ~((align) - 1))
66b00401f0SDavid Xu 
67b00401f0SDavid Xu static size_t tls_static_space;
68b00401f0SDavid Xu static size_t tls_init_size;
69b00401f0SDavid Xu static void *tls_init;
70b00401f0SDavid Xu #endif
71b00401f0SDavid Xu 
72b00401f0SDavid Xu #ifdef __i386__
73b00401f0SDavid Xu 
7492df6c3eSDavid Xu /* GNU ABI */
75b00401f0SDavid Xu 
7692df6c3eSDavid Xu void *___libc_tls_get_addr(void *ti) __attribute__((__regparm__(1)));
7792df6c3eSDavid Xu 
78b00401f0SDavid Xu __attribute__((__regparm__(1)))
79b00401f0SDavid Xu void *
8092df6c3eSDavid Xu ___libc_tls_get_addr(void *ti __unused)
81b00401f0SDavid Xu {
82b00401f0SDavid Xu 	return (0);
83b00401f0SDavid Xu }
84b00401f0SDavid Xu 
85b00401f0SDavid Xu #endif
86b00401f0SDavid Xu 
8792df6c3eSDavid Xu void *__libc_tls_get_addr(void *ti);
88bc633d63SMatthew Dillon 
89b00401f0SDavid Xu void *
9092df6c3eSDavid Xu __libc_tls_get_addr(void *ti __unused)
91b00401f0SDavid Xu {
92b00401f0SDavid Xu 	return (0);
93b00401f0SDavid Xu }
94b00401f0SDavid Xu 
9592df6c3eSDavid Xu #ifndef PIC
9692df6c3eSDavid Xu 
97b00401f0SDavid Xu /*
98bc633d63SMatthew Dillon  * Free Static TLS
99b00401f0SDavid Xu  */
100b00401f0SDavid Xu void
101f20fd431SJoerg Sonnenberger __libc_free_tls(struct tls_tcb *tcb)
102b00401f0SDavid Xu {
103bc633d63SMatthew Dillon 	size_t data_size;
104b00401f0SDavid Xu 
1059e2ee207SJoerg Sonnenberger 	if (tcb->tcb_dtv)
1069e2ee207SJoerg Sonnenberger 		free(tcb->tcb_dtv);
107bc633d63SMatthew Dillon 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
108bc633d63SMatthew Dillon 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
109bc633d63SMatthew Dillon 	free((char *)tcb - data_size);
110b00401f0SDavid Xu }
111b00401f0SDavid Xu 
112b00401f0SDavid Xu /*
113bc633d63SMatthew Dillon  * Allocate Static TLS.
114*a378ce7dSJoerg Sonnenberger  *
115*a378ce7dSJoerg Sonnenberger  * !!!WARNING!!! The first run for a static binary is done without valid
116*a378ce7dSJoerg Sonnenberger  * TLS storage.  It must not call any system calls which want to change
117*a378ce7dSJoerg Sonnenberger  * errno.
118b00401f0SDavid Xu  */
119bc633d63SMatthew Dillon struct tls_tcb *
120f20fd431SJoerg Sonnenberger __libc_allocate_tls(struct tls_tcb *old_tcb)
121b00401f0SDavid Xu {
122*a378ce7dSJoerg Sonnenberger 	static int first_run = 0;
123bc633d63SMatthew Dillon 	size_t data_size;
124bc633d63SMatthew Dillon 	struct tls_tcb *tcb;
125b00401f0SDavid Xu 	Elf_Addr *dtv;
126b00401f0SDavid Xu 
127*a378ce7dSJoerg Sonnenberger retry:
128bc633d63SMatthew Dillon 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
129bc633d63SMatthew Dillon 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
130*a378ce7dSJoerg Sonnenberger 
131*a378ce7dSJoerg Sonnenberger 	if (first_run)
132f20fd431SJoerg Sonnenberger 		tcb = malloc(data_size + sizeof(*tcb));
133*a378ce7dSJoerg Sonnenberger 	else
134*a378ce7dSJoerg Sonnenberger 		tcb = alloca(data_size + sizeof(*tcb));
135bc633d63SMatthew Dillon 	tcb = (struct tls_tcb *)((char *)tcb + data_size);
136*a378ce7dSJoerg Sonnenberger 	if (first_run)
137b00401f0SDavid Xu 		dtv = malloc(3 * sizeof(Elf_Addr));
138*a378ce7dSJoerg Sonnenberger 	else
139*a378ce7dSJoerg Sonnenberger 		dtv = alloca(3 * sizeof(Elf_Addr));
1409e2ee207SJoerg Sonnenberger 
1419e2ee207SJoerg Sonnenberger #ifdef RTLD_TCB_HAS_SELF_POINTER
1429e2ee207SJoerg Sonnenberger 	tcb->tcb_self = tcb;
1439e2ee207SJoerg Sonnenberger #endif
1449e2ee207SJoerg Sonnenberger 	tcb->tcb_dtv = dtv;
1459e2ee207SJoerg Sonnenberger 	tcb->tcb_pthread = NULL;
146b00401f0SDavid Xu 
147b00401f0SDavid Xu 	dtv[0] = 1;
148b00401f0SDavid Xu 	dtv[1] = 1;
149bc633d63SMatthew Dillon 	dtv[2] = (Elf_Addr)((char *)tcb - tls_static_space);
150bc633d63SMatthew Dillon 
151bc633d63SMatthew Dillon 	if (old_tcb) {
152b00401f0SDavid Xu 		/*
153b00401f0SDavid Xu 		 * Copy the static TLS block over whole.
154b00401f0SDavid Xu 		 */
155bc633d63SMatthew Dillon 		memcpy((char *)tcb - tls_static_space,
156bc633d63SMatthew Dillon 			(char *)old_tcb - tls_static_space,
157b00401f0SDavid Xu 			tls_static_space);
158b00401f0SDavid Xu 
159b00401f0SDavid Xu 		/*
160b00401f0SDavid Xu 		 * We assume that this block was the one we created with
161b00401f0SDavid Xu 		 * allocate_initial_tls().
162b00401f0SDavid Xu 		 */
163f20fd431SJoerg Sonnenberger 		_rtld_free_tls(old_tcb);
164b00401f0SDavid Xu 	} else {
165bc633d63SMatthew Dillon 		memcpy((char *)tcb - tls_static_space,
166b00401f0SDavid Xu 			tls_init, tls_init_size);
167bc633d63SMatthew Dillon 		memset((char *)tcb - tls_static_space + tls_init_size,
168b00401f0SDavid Xu 			0, tls_static_space - tls_init_size);
169b00401f0SDavid Xu 	}
170*a378ce7dSJoerg Sonnenberger 
171*a378ce7dSJoerg Sonnenberger 	if (first_run)
172bc633d63SMatthew Dillon 		return (tcb);
173*a378ce7dSJoerg Sonnenberger 
174*a378ce7dSJoerg Sonnenberger 	tls_set_tcb(tcb);
175*a378ce7dSJoerg Sonnenberger 	first_run = 1;
176*a378ce7dSJoerg Sonnenberger 	goto retry;
177b00401f0SDavid Xu }
178b00401f0SDavid Xu 
17992df6c3eSDavid Xu #else
18092df6c3eSDavid Xu 
181bc633d63SMatthew Dillon struct tls_tcb *
182f20fd431SJoerg Sonnenberger __libc_allocate_tls(struct tls_tcb *old_tls __unused)
18392df6c3eSDavid Xu {
18492df6c3eSDavid Xu 	return (0);
18592df6c3eSDavid Xu }
18692df6c3eSDavid Xu 
18792df6c3eSDavid Xu void
188f20fd431SJoerg Sonnenberger __libc_free_tls(struct tls_tcb *tcb __unused)
18992df6c3eSDavid Xu {
19092df6c3eSDavid Xu }
19192df6c3eSDavid Xu 
19292df6c3eSDavid Xu #endif /* PIC */
19392df6c3eSDavid Xu 
194b00401f0SDavid Xu extern char **environ;
195b00401f0SDavid Xu 
196b00401f0SDavid Xu void
197b00401f0SDavid Xu _init_tls()
198b00401f0SDavid Xu {
199b00401f0SDavid Xu #ifndef PIC
200b00401f0SDavid Xu 	Elf_Addr *sp;
201b00401f0SDavid Xu 	Elf_Auxinfo *aux, *auxp;
202b00401f0SDavid Xu 	Elf_Phdr *phdr;
203b00401f0SDavid Xu 	size_t phent, phnum;
204b00401f0SDavid Xu 	int i;
205bc633d63SMatthew Dillon 	struct tls_tcb *tcb;
206b00401f0SDavid Xu 
207b00401f0SDavid Xu 	sp = (Elf_Addr *) environ;
208b00401f0SDavid Xu 	while (*sp++ != 0)
209b00401f0SDavid Xu 		;
210b00401f0SDavid Xu 	aux = (Elf_Auxinfo *) sp;
211b00401f0SDavid Xu 	phdr = 0;
212b00401f0SDavid Xu 	phent = phnum = 0;
213b00401f0SDavid Xu 	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
214b00401f0SDavid Xu 		switch (auxp->a_type) {
215b00401f0SDavid Xu 		case AT_PHDR:
216b00401f0SDavid Xu 			phdr = auxp->a_un.a_ptr;
217b00401f0SDavid Xu 			break;
218b00401f0SDavid Xu 
219b00401f0SDavid Xu 		case AT_PHENT:
220b00401f0SDavid Xu 			phent = auxp->a_un.a_val;
221b00401f0SDavid Xu 			break;
222b00401f0SDavid Xu 
223b00401f0SDavid Xu 		case AT_PHNUM:
224b00401f0SDavid Xu 			phnum = auxp->a_un.a_val;
225b00401f0SDavid Xu 			break;
226b00401f0SDavid Xu 		}
227b00401f0SDavid Xu 	}
228b00401f0SDavid Xu 	if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0)
229b00401f0SDavid Xu 		return;
230b00401f0SDavid Xu 
231b00401f0SDavid Xu 	for (i = 0; (unsigned)i < phnum; i++) {
232b00401f0SDavid Xu 		if (phdr[i].p_type == PT_TLS) {
233b00401f0SDavid Xu 			tls_static_space = round(phdr[i].p_memsz,
234b00401f0SDavid Xu 			    phdr[i].p_align);
235b00401f0SDavid Xu 			tls_init_size = phdr[i].p_filesz;
236b00401f0SDavid Xu 			tls_init = (void*) phdr[i].p_vaddr;
237b00401f0SDavid Xu 		}
238b00401f0SDavid Xu 	}
239b00401f0SDavid Xu 
240f20fd431SJoerg Sonnenberger 	tcb = _rtld_allocate_tls(NULL);
2419e2ee207SJoerg Sonnenberger 	tls_set_tcb(tcb);
242b00401f0SDavid Xu #endif
243b00401f0SDavid Xu }
244