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