tls.c revision 161800
1133064Sdfr/*-
2133064Sdfr * Copyright (c) 2004 Doug Rabson
3133064Sdfr * All rights reserved.
4133064Sdfr *
5133064Sdfr * Redistribution and use in source and binary forms, with or without
6133064Sdfr * modification, are permitted provided that the following conditions
7133064Sdfr * are met:
8133064Sdfr * 1. Redistributions of source code must retain the above copyright
9133064Sdfr *    notice, this list of conditions and the following disclaimer.
10133064Sdfr * 2. Redistributions in binary form must reproduce the above copyright
11133064Sdfr *    notice, this list of conditions and the following disclaimer in the
12133064Sdfr *    documentation and/or other materials provided with the distribution.
13133064Sdfr *
14133064Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15133064Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16133064Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17133064Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18133064Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19133064Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20133064Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21133064Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22133064Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23133064Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24133064Sdfr * SUCH DAMAGE.
25133064Sdfr *
26133064Sdfr *	$FreeBSD: head/lib/libc/gen/tls.c 161800 2006-09-01 06:13:16Z marcel $
27133064Sdfr */
28133064Sdfr
29133064Sdfr/*
30133064Sdfr * Define stubs for TLS internals so that programs and libraries can
31133064Sdfr * link. These functions will be replaced by functional versions at
32133064Sdfr * runtime from ld-elf.so.1.
33133064Sdfr */
34133064Sdfr
35143921Sdavidxu#include <sys/cdefs.h>
36133754Sdfr#include <stdlib.h>
37133754Sdfr#include <string.h>
38133754Sdfr#include <elf.h>
39133754Sdfr#include <assert.h>
40143921Sdavidxu
41133754Sdfr#include "libc_private.h"
42133064Sdfr
43143921Sdavidxu__weak_reference(__libc_allocate_tls, _rtld_allocate_tls);
44143921Sdavidxu__weak_reference(__libc_free_tls, _rtld_free_tls);
45143921Sdavidxu
46143921Sdavidxu#ifdef __i386__
47143921Sdavidxu
48143921Sdavidxu__weak_reference(___libc_tls_get_addr, ___tls_get_addr);
49143921Sdavidxu__attribute__((__regparm__(1))) void * ___libc_tls_get_addr(void *);
50143921Sdavidxu
51143921Sdavidxu#endif
52143921Sdavidxu
53143921Sdavidxuvoid * __libc_tls_get_addr(void *);
54143921Sdavidxu__weak_reference(__libc_tls_get_addr, __tls_get_addr);
55143921Sdavidxu
56143921Sdavidxuvoid *_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign);
57143921Sdavidxuvoid _rtld_free_tls(void *tls, size_t tcbsize, size_t tcbalign);
58143921Sdavidxuvoid *__libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign);
59143921Sdavidxuvoid __libc_free_tls(void *tls, size_t tcbsize, size_t tcbalign);
60143921Sdavidxu
61161800Smarcel#if defined(__ia64__) || defined(__powerpc__)
62133754Sdfr#define TLS_VARIANT_I
63133754Sdfr#endif
64135686Scognet#if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \
65135686Scognet    defined(__arm__)
66133754Sdfr#define TLS_VARIANT_II
67133754Sdfr#endif
68133754Sdfr
69133754Sdfr#ifndef PIC
70133754Sdfr
71133754Sdfr#define round(size, align) \
72133754Sdfr	(((size) + (align) - 1) & ~((align) - 1))
73133754Sdfr
74133754Sdfrstatic size_t tls_static_space;
75133754Sdfrstatic size_t tls_init_size;
76133754Sdfrstatic void *tls_init;
77133754Sdfr#endif
78133754Sdfr
79133064Sdfr#ifdef __i386__
80133064Sdfr
81143921Sdavidxu/* GNU ABI */
82133064Sdfr
83133064Sdfr__attribute__((__regparm__(1)))
84133064Sdfrvoid *
85143921Sdavidxu___libc_tls_get_addr(void *ti __unused)
86133064Sdfr{
87133064Sdfr	return (0);
88133064Sdfr}
89133064Sdfr
90133064Sdfr#endif
91133064Sdfr
92133064Sdfrvoid *
93143921Sdavidxu__libc_tls_get_addr(void *ti __unused)
94133064Sdfr{
95133064Sdfr	return (0);
96133064Sdfr}
97133064Sdfr
98143921Sdavidxu#ifndef PIC
99143921Sdavidxu
100133754Sdfr#ifdef TLS_VARIANT_I
101133754Sdfr
102161800Smarcel#define	TLS_TCB_SIZE	(2 * sizeof(void *))
103161800Smarcel
104142560Sdavidxu/*
105142959Sdavidxu * Free Static TLS using the Variant I method.
106142560Sdavidxu */
107133754Sdfrvoid
108161800Smarcel__libc_free_tls(void *tcb, size_t tcbsize, size_t tcbalign __unused)
109133754Sdfr{
110161800Smarcel	Elf_Addr *dtv;
111161800Smarcel	Elf_Addr **tls;
112133754Sdfr
113161800Smarcel	tls = (Elf_Addr **)((Elf_Addr)tcb + tcbsize - TLS_TCB_SIZE);
114161800Smarcel	dtv = tls[0];
115133754Sdfr	free(dtv);
116161800Smarcel	free(tcb);
117133754Sdfr}
118133754Sdfr
119133754Sdfr/*
120133754Sdfr * Allocate Static TLS using the Variant I method.
121133754Sdfr */
122133064Sdfrvoid *
123161800Smarcel__libc_allocate_tls(void *oldtcb, size_t tcbsize, size_t tcbalign __unused)
124133064Sdfr{
125133754Sdfr	Elf_Addr *dtv;
126161800Smarcel	Elf_Addr **tls;
127161800Smarcel	char *tcb;
128133754Sdfr
129161800Smarcel	if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE)
130161800Smarcel		return (oldtcb);
131133754Sdfr
132161800Smarcel	tcb = calloc(1, tls_static_space + tcbsize);
133161800Smarcel	tls = (Elf_Addr **)(tcb + tcbsize - TLS_TCB_SIZE);
134133754Sdfr
135161800Smarcel	if (oldtcb != NULL) {
136161800Smarcel		memcpy(tls, oldtcb, tls_static_space + TLS_TCB_SIZE);
137161800Smarcel		free(oldtcb);
138133754Sdfr
139161800Smarcel		/* Adjust the DTV. */
140161800Smarcel		dtv = tls[0];
141161800Smarcel		dtv[2] = (Elf_Addr)tls + TLS_TCB_SIZE;
142161800Smarcel	} else {
143161800Smarcel		dtv = malloc(3 * sizeof(Elf_Addr));
144161800Smarcel		tls[0] = dtv;
145161800Smarcel		dtv[0] = 1;
146161800Smarcel		dtv[1] = 1;
147161800Smarcel		dtv[2] = (Elf_Addr)tls + TLS_TCB_SIZE;
148133754Sdfr
149161800Smarcel		if (tls_init_size > 0)
150161800Smarcel		    memcpy((void*)dtv[2], tls_init, tls_init_size);
151161800Smarcel		if (tls_static_space > tls_init_size)
152161800Smarcel		    memset((void*)(dtv[2] + tls_init_size), 0,
153161800Smarcel		      tls_static_space - tls_init_size);
154133754Sdfr	}
155133754Sdfr
156161800Smarcel	return(tcb);
157133064Sdfr}
158133064Sdfr
159133754Sdfr#endif
160133754Sdfr
161133754Sdfr#ifdef TLS_VARIANT_II
162133754Sdfr
163161800Smarcel#define	TLS_TCB_SIZE	(3 * sizeof(Elf_Addr))
164161800Smarcel
165133754Sdfr/*
166133754Sdfr * Free Static TLS using the Variant II method.
167133754Sdfr */
168133064Sdfrvoid
169143921Sdavidxu__libc_free_tls(void *tcb, size_t tcbsize __unused, size_t tcbalign)
170133064Sdfr{
171133754Sdfr	size_t size;
172133754Sdfr	Elf_Addr* dtv;
173133754Sdfr	Elf_Addr tlsstart, tlsend;
174133754Sdfr
175133754Sdfr	/*
176133754Sdfr	 * Figure out the size of the initial TLS block so that we can
177133754Sdfr	 * find stuff which ___tls_get_addr() allocated dynamically.
178133754Sdfr	 */
179133754Sdfr	size = round(tls_static_space, tcbalign);
180133754Sdfr
181133754Sdfr	dtv = ((Elf_Addr**)tcb)[1];
182133754Sdfr	tlsend = (Elf_Addr) tcb;
183133754Sdfr	tlsstart = tlsend - size;
184133754Sdfr	free((void*) tlsstart);
185133754Sdfr	free(dtv);
186133064Sdfr}
187133754Sdfr
188133754Sdfr/*
189133754Sdfr * Allocate Static TLS using the Variant II method.
190133754Sdfr */
191133754Sdfrvoid *
192143921Sdavidxu__libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
193133754Sdfr{
194133754Sdfr	size_t size;
195133754Sdfr	char *tls;
196133754Sdfr	Elf_Addr *dtv;
197133754Sdfr	Elf_Addr segbase, oldsegbase;
198133754Sdfr
199133754Sdfr	size = round(tls_static_space, tcbalign);
200133754Sdfr
201133754Sdfr	assert(tcbsize >= 2*sizeof(Elf_Addr));
202157198Sdavidxu	tls = calloc(1, size + tcbsize);
203133754Sdfr	dtv = malloc(3 * sizeof(Elf_Addr));
204133754Sdfr
205133754Sdfr	segbase = (Elf_Addr)(tls + size);
206133754Sdfr	((Elf_Addr*)segbase)[0] = segbase;
207133754Sdfr	((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv;
208133754Sdfr
209133754Sdfr	dtv[0] = 1;
210133754Sdfr	dtv[1] = 1;
211133754Sdfr	dtv[2] = segbase - tls_static_space;
212133754Sdfr
213133754Sdfr	if (oldtls) {
214133754Sdfr		/*
215133754Sdfr		 * Copy the static TLS block over whole.
216133754Sdfr		 */
217133754Sdfr		oldsegbase = (Elf_Addr) oldtls;
218133754Sdfr		memcpy((void *)(segbase - tls_static_space),
219133754Sdfr		    (const void *)(oldsegbase - tls_static_space),
220133754Sdfr		    tls_static_space);
221133754Sdfr
222133754Sdfr		/*
223133754Sdfr		 * We assume that this block was the one we created with
224133754Sdfr		 * allocate_initial_tls().
225133754Sdfr		 */
226133754Sdfr		_rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
227133754Sdfr	} else {
228133754Sdfr		memcpy((void *)(segbase - tls_static_space),
229133754Sdfr		    tls_init, tls_init_size);
230133754Sdfr		memset((void *)(segbase - tls_static_space + tls_init_size),
231133754Sdfr		    0, tls_static_space - tls_init_size);
232133754Sdfr	}
233133754Sdfr
234133754Sdfr	return (void*) segbase;
235143921Sdavidxu}
236143921Sdavidxu
237143921Sdavidxu#endif /* TLS_VARIANT_II */
238143921Sdavidxu
239133754Sdfr#else
240143921Sdavidxu
241143921Sdavidxuvoid *
242143921Sdavidxu__libc_allocate_tls(void *oldtls __unused, size_t tcbsize __unused,
243143921Sdavidxu	size_t tcbalign __unused)
244143921Sdavidxu{
245133754Sdfr	return (0);
246133754Sdfr}
247133754Sdfr
248143921Sdavidxuvoid
249143921Sdavidxu__libc_free_tls(void *tcb __unused, size_t tcbsize __unused,
250143921Sdavidxu	size_t tcbalign __unused)
251143921Sdavidxu{
252143921Sdavidxu}
253133754Sdfr
254143921Sdavidxu#endif /* PIC */
255143921Sdavidxu
256143921Sdavidxuextern char **environ;
257143921Sdavidxu
258133754Sdfrvoid
259133754Sdfr_init_tls()
260133754Sdfr{
261133754Sdfr#ifndef PIC
262133754Sdfr	Elf_Addr *sp;
263133754Sdfr	Elf_Auxinfo *aux, *auxp;
264133754Sdfr	Elf_Phdr *phdr;
265133754Sdfr	size_t phent, phnum;
266133754Sdfr	int i;
267133949Sdfr	void *tls;
268133754Sdfr
269133754Sdfr	sp = (Elf_Addr *) environ;
270133754Sdfr	while (*sp++ != 0)
271133754Sdfr		;
272133754Sdfr	aux = (Elf_Auxinfo *) sp;
273133754Sdfr	phdr = 0;
274133754Sdfr	phent = phnum = 0;
275133754Sdfr	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
276133754Sdfr		switch (auxp->a_type) {
277133754Sdfr		case AT_PHDR:
278133754Sdfr			phdr = auxp->a_un.a_ptr;
279133754Sdfr			break;
280133754Sdfr
281133754Sdfr		case AT_PHENT:
282133754Sdfr			phent = auxp->a_un.a_val;
283133754Sdfr			break;
284133754Sdfr
285133754Sdfr		case AT_PHNUM:
286133754Sdfr			phnum = auxp->a_un.a_val;
287133754Sdfr			break;
288133754Sdfr		}
289133754Sdfr	}
290133754Sdfr	if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0)
291133754Sdfr		return;
292133754Sdfr
293143921Sdavidxu	for (i = 0; (unsigned) i < phnum; i++) {
294133754Sdfr		if (phdr[i].p_type == PT_TLS) {
295133754Sdfr			tls_static_space = round(phdr[i].p_memsz,
296133754Sdfr			    phdr[i].p_align);
297133754Sdfr			tls_init_size = phdr[i].p_filesz;
298133754Sdfr			tls_init = (void*) phdr[i].p_vaddr;
299133754Sdfr		}
300133754Sdfr	}
301133754Sdfr
302161800Smarcel	tls = _rtld_allocate_tls(NULL, TLS_TCB_SIZE, 1);
303133754Sdfr
304133949Sdfr	_set_tp(tls);
305133754Sdfr#endif
306133754Sdfr}
307