xref: /dflybsd-src/lib/libc/gen/tls.c (revision 9e2ee207f665d385e3bc15d419383b3d061e7ab6)
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*9e2ee207SJoerg Sonnenberger  *	$DragonFly: src/lib/libc/gen/tls.c,v 1.5 2005/03/29 19:26:19 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>
38*9e2ee207SJoerg Sonnenberger 
39*9e2ee207SJoerg Sonnenberger #include <machine/tls.h>
40*9e2ee207SJoerg 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 
55bc633d63SMatthew Dillon struct tls_tcb *__libc_allocate_tls(struct tls_tcb *old_tcb, size_t tcbsize,
56bc633d63SMatthew Dillon 				    int flags);
57bc633d63SMatthew Dillon void __libc_free_tls(struct tls_tcb *tcb, size_t tcb_size);
58b00401f0SDavid Xu 
59*9e2ee207SJoerg Sonnenberger #if !defined(RTLD_STATIC_TLS_VARIANT_II)
60*9e2ee207SJoerg Sonnenberger #error "Unsupported TLS layout"
61b00401f0SDavid Xu #endif
62b00401f0SDavid Xu 
63b00401f0SDavid Xu #ifndef PIC
64b00401f0SDavid Xu 
65b00401f0SDavid Xu #define round(size, align) \
66b00401f0SDavid Xu 	(((size) + (align) - 1) & ~((align) - 1))
67b00401f0SDavid Xu 
68b00401f0SDavid Xu static size_t tls_static_space;
69b00401f0SDavid Xu static size_t tls_init_size;
70b00401f0SDavid Xu static void *tls_init;
71b00401f0SDavid Xu #endif
72b00401f0SDavid Xu 
73b00401f0SDavid Xu #ifdef __i386__
74b00401f0SDavid Xu 
7592df6c3eSDavid Xu /* GNU ABI */
76b00401f0SDavid Xu 
7792df6c3eSDavid Xu void *___libc_tls_get_addr(void *ti) __attribute__((__regparm__(1)));
7892df6c3eSDavid Xu 
79b00401f0SDavid Xu __attribute__((__regparm__(1)))
80b00401f0SDavid Xu void *
8192df6c3eSDavid Xu ___libc_tls_get_addr(void *ti __unused)
82b00401f0SDavid Xu {
83b00401f0SDavid Xu 	return (0);
84b00401f0SDavid Xu }
85b00401f0SDavid Xu 
86b00401f0SDavid Xu #endif
87b00401f0SDavid Xu 
8892df6c3eSDavid Xu void *__libc_tls_get_addr(void *ti);
89bc633d63SMatthew Dillon 
90b00401f0SDavid Xu void *
9192df6c3eSDavid Xu __libc_tls_get_addr(void *ti __unused)
92b00401f0SDavid Xu {
93b00401f0SDavid Xu 	return (0);
94b00401f0SDavid Xu }
95b00401f0SDavid Xu 
9692df6c3eSDavid Xu #ifndef PIC
9792df6c3eSDavid Xu 
98b00401f0SDavid Xu /*
99bc633d63SMatthew Dillon  * Free Static TLS
100b00401f0SDavid Xu  */
101b00401f0SDavid Xu void
102bc633d63SMatthew Dillon __libc_free_tls(struct tls_tcb *tcb, size_t tcb_size __unused)
103b00401f0SDavid Xu {
104bc633d63SMatthew Dillon 	size_t data_size;
105b00401f0SDavid Xu 
106*9e2ee207SJoerg Sonnenberger 	if (tcb->tcb_dtv)
107*9e2ee207SJoerg Sonnenberger 		free(tcb->tcb_dtv);
108bc633d63SMatthew Dillon 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
109bc633d63SMatthew Dillon 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
110bc633d63SMatthew Dillon 	free((char *)tcb - data_size);
111b00401f0SDavid Xu }
112b00401f0SDavid Xu 
113b00401f0SDavid Xu /*
114bc633d63SMatthew Dillon  * Allocate Static TLS.
115b00401f0SDavid Xu  */
116bc633d63SMatthew Dillon struct tls_tcb *
117bc633d63SMatthew Dillon __libc_allocate_tls(struct tls_tcb *old_tcb, size_t tcb_size, int flags)
118b00401f0SDavid Xu {
119bc633d63SMatthew Dillon 	size_t data_size;
120bc633d63SMatthew Dillon 	struct tls_tcb *tcb;
121b00401f0SDavid Xu 	Elf_Addr *dtv;
122b00401f0SDavid Xu 
123bc633d63SMatthew Dillon 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
124bc633d63SMatthew Dillon 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
125bc633d63SMatthew Dillon 	tcb = malloc(data_size + tcb_size);
126bc633d63SMatthew Dillon 	tcb = (struct tls_tcb *)((char *)tcb + data_size);
127b00401f0SDavid Xu 	dtv = malloc(3 * sizeof(Elf_Addr));
128*9e2ee207SJoerg Sonnenberger 
129*9e2ee207SJoerg Sonnenberger #ifdef RTLD_TCB_HAS_SELF_POINTER
130*9e2ee207SJoerg Sonnenberger 	tcb->tcb_self = tcb;
131*9e2ee207SJoerg Sonnenberger #endif
132*9e2ee207SJoerg Sonnenberger 	tcb->tcb_dtv = dtv;
133*9e2ee207SJoerg Sonnenberger 	tcb->tcb_pthread = NULL;
134b00401f0SDavid Xu 
135b00401f0SDavid Xu 	dtv[0] = 1;
136b00401f0SDavid Xu 	dtv[1] = 1;
137bc633d63SMatthew Dillon 	dtv[2] = (Elf_Addr)((char *)tcb - tls_static_space);
138bc633d63SMatthew Dillon 
139bc633d63SMatthew Dillon 	if (old_tcb) {
140b00401f0SDavid Xu 		/*
141b00401f0SDavid Xu 		 * Copy the static TLS block over whole.
142b00401f0SDavid Xu 		 */
143bc633d63SMatthew Dillon 		memcpy((char *)tcb - tls_static_space,
144bc633d63SMatthew Dillon 			(char *)old_tcb - tls_static_space,
145b00401f0SDavid Xu 			tls_static_space);
146b00401f0SDavid Xu 
147b00401f0SDavid Xu 		/*
148b00401f0SDavid Xu 		 * We assume that this block was the one we created with
149b00401f0SDavid Xu 		 * allocate_initial_tls().
150b00401f0SDavid Xu 		 */
151bc633d63SMatthew Dillon 		_rtld_free_tls(old_tcb, sizeof(struct tls_tcb));
152b00401f0SDavid Xu 	} else {
153bc633d63SMatthew Dillon 		memcpy((char *)tcb - tls_static_space,
154b00401f0SDavid Xu 			tls_init, tls_init_size);
155bc633d63SMatthew Dillon 		memset((char *)tcb - tls_static_space + tls_init_size,
156b00401f0SDavid Xu 			0, tls_static_space - tls_init_size);
157b00401f0SDavid Xu 	}
158bc633d63SMatthew Dillon 	return (tcb);
159b00401f0SDavid Xu }
160b00401f0SDavid Xu 
16192df6c3eSDavid Xu #else
16292df6c3eSDavid Xu 
163bc633d63SMatthew Dillon struct tls_tcb *
164bc633d63SMatthew Dillon __libc_allocate_tls(struct tls_tcb *old_tls __unused, size_t tcb_size __unused,
165bc633d63SMatthew Dillon 			int flags __unused)
16692df6c3eSDavid Xu {
16792df6c3eSDavid Xu 	return (0);
16892df6c3eSDavid Xu }
16992df6c3eSDavid Xu 
17092df6c3eSDavid Xu void
171bc633d63SMatthew Dillon __libc_free_tls(struct tls_tcb *tcb __unused, size_t tcb_size __unused)
17292df6c3eSDavid Xu {
17392df6c3eSDavid Xu }
17492df6c3eSDavid Xu 
17592df6c3eSDavid Xu #endif /* PIC */
17692df6c3eSDavid Xu 
177b00401f0SDavid Xu extern char **environ;
178b00401f0SDavid Xu 
179b00401f0SDavid Xu void
180b00401f0SDavid Xu _init_tls()
181b00401f0SDavid Xu {
182b00401f0SDavid Xu #ifndef PIC
183b00401f0SDavid Xu 	Elf_Addr *sp;
184b00401f0SDavid Xu 	Elf_Auxinfo *aux, *auxp;
185b00401f0SDavid Xu 	Elf_Phdr *phdr;
186b00401f0SDavid Xu 	size_t phent, phnum;
187b00401f0SDavid Xu 	int i;
188bc633d63SMatthew Dillon 	struct tls_tcb *tcb;
189b00401f0SDavid Xu 
190b00401f0SDavid Xu 	sp = (Elf_Addr *) environ;
191b00401f0SDavid Xu 	while (*sp++ != 0)
192b00401f0SDavid Xu 		;
193b00401f0SDavid Xu 	aux = (Elf_Auxinfo *) sp;
194b00401f0SDavid Xu 	phdr = 0;
195b00401f0SDavid Xu 	phent = phnum = 0;
196b00401f0SDavid Xu 	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
197b00401f0SDavid Xu 		switch (auxp->a_type) {
198b00401f0SDavid Xu 		case AT_PHDR:
199b00401f0SDavid Xu 			phdr = auxp->a_un.a_ptr;
200b00401f0SDavid Xu 			break;
201b00401f0SDavid Xu 
202b00401f0SDavid Xu 		case AT_PHENT:
203b00401f0SDavid Xu 			phent = auxp->a_un.a_val;
204b00401f0SDavid Xu 			break;
205b00401f0SDavid Xu 
206b00401f0SDavid Xu 		case AT_PHNUM:
207b00401f0SDavid Xu 			phnum = auxp->a_un.a_val;
208b00401f0SDavid Xu 			break;
209b00401f0SDavid Xu 		}
210b00401f0SDavid Xu 	}
211b00401f0SDavid Xu 	if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0)
212b00401f0SDavid Xu 		return;
213b00401f0SDavid Xu 
214b00401f0SDavid Xu 	for (i = 0; (unsigned)i < phnum; i++) {
215b00401f0SDavid Xu 		if (phdr[i].p_type == PT_TLS) {
216b00401f0SDavid Xu 			tls_static_space = round(phdr[i].p_memsz,
217b00401f0SDavid Xu 			    phdr[i].p_align);
218b00401f0SDavid Xu 			tls_init_size = phdr[i].p_filesz;
219b00401f0SDavid Xu 			tls_init = (void*) phdr[i].p_vaddr;
220b00401f0SDavid Xu 		}
221b00401f0SDavid Xu 	}
222b00401f0SDavid Xu 
223bc633d63SMatthew Dillon 	if (tls_static_space) {
224bc633d63SMatthew Dillon 		tcb = _rtld_allocate_tls(NULL, sizeof(struct tls_tcb), 0);
225*9e2ee207SJoerg Sonnenberger 		tls_set_tcb(tcb);
226bc633d63SMatthew Dillon 	}
227b00401f0SDavid Xu #endif
228b00401f0SDavid Xu }
229