tls.c revision 203946
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 203946 2010-02-16 02:22:59Z 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>
39143921Sdavidxu
40133754Sdfr#include "libc_private.h"
41133064Sdfr
42143921Sdavidxu__weak_reference(__libc_allocate_tls, _rtld_allocate_tls);
43143921Sdavidxu__weak_reference(__libc_free_tls, _rtld_free_tls);
44143921Sdavidxu
45143921Sdavidxu#ifdef __i386__
46143921Sdavidxu
47143921Sdavidxu__weak_reference(___libc_tls_get_addr, ___tls_get_addr);
48143921Sdavidxu__attribute__((__regparm__(1))) void * ___libc_tls_get_addr(void *);
49143921Sdavidxu
50143921Sdavidxu#endif
51143921Sdavidxu
52143921Sdavidxuvoid * __libc_tls_get_addr(void *);
53143921Sdavidxu__weak_reference(__libc_tls_get_addr, __tls_get_addr);
54143921Sdavidxu
55143921Sdavidxuvoid *_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign);
56143921Sdavidxuvoid _rtld_free_tls(void *tls, size_t tcbsize, size_t tcbalign);
57143921Sdavidxuvoid *__libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign);
58143921Sdavidxuvoid __libc_free_tls(void *tls, size_t tcbsize, size_t tcbalign);
59143921Sdavidxu
60163118Skmacy#if defined(__ia64__) || defined(__amd64__)
61163118Skmacy#define TLS_TCB_ALIGN 16
62163118Skmacy#elif defined(__powerpc__) || defined(__i386__) || defined(__arm__) || \
63178684Sgonzo    defined(__sparc64__) || defined(__mips__)
64163118Skmacy#define TLS_TCB_ALIGN sizeof(void *)
65163118Skmacy#else
66163118Skmacy#error TLS_TCB_ALIGN undefined for target architecture
67163118Skmacy#endif
68163118Skmacy
69161800Smarcel#if defined(__ia64__) || defined(__powerpc__)
70133754Sdfr#define TLS_VARIANT_I
71133754Sdfr#endif
72135686Scognet#if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \
73178684Sgonzo    defined(__arm__) || defined(__mips__)
74133754Sdfr#define TLS_VARIANT_II
75133754Sdfr#endif
76133754Sdfr
77133754Sdfr#ifndef PIC
78133754Sdfr
79133754Sdfr#define round(size, align) \
80133754Sdfr	(((size) + (align) - 1) & ~((align) - 1))
81133754Sdfr
82133754Sdfrstatic size_t tls_static_space;
83133754Sdfrstatic size_t tls_init_size;
84133754Sdfrstatic void *tls_init;
85133754Sdfr#endif
86133754Sdfr
87133064Sdfr#ifdef __i386__
88133064Sdfr
89143921Sdavidxu/* GNU ABI */
90133064Sdfr
91133064Sdfr__attribute__((__regparm__(1)))
92133064Sdfrvoid *
93143921Sdavidxu___libc_tls_get_addr(void *ti __unused)
94133064Sdfr{
95133064Sdfr	return (0);
96133064Sdfr}
97133064Sdfr
98133064Sdfr#endif
99133064Sdfr
100133064Sdfrvoid *
101143921Sdavidxu__libc_tls_get_addr(void *ti __unused)
102133064Sdfr{
103133064Sdfr	return (0);
104133064Sdfr}
105133064Sdfr
106143921Sdavidxu#ifndef PIC
107143921Sdavidxu
108133754Sdfr#ifdef TLS_VARIANT_I
109133754Sdfr
110161800Smarcel#define	TLS_TCB_SIZE	(2 * sizeof(void *))
111161800Smarcel
112142560Sdavidxu/*
113142959Sdavidxu * Free Static TLS using the Variant I method.
114142560Sdavidxu */
115133754Sdfrvoid
116161800Smarcel__libc_free_tls(void *tcb, size_t tcbsize, size_t tcbalign __unused)
117133754Sdfr{
118161800Smarcel	Elf_Addr *dtv;
119161800Smarcel	Elf_Addr **tls;
120133754Sdfr
121161800Smarcel	tls = (Elf_Addr **)((Elf_Addr)tcb + tcbsize - TLS_TCB_SIZE);
122161800Smarcel	dtv = tls[0];
123133754Sdfr	free(dtv);
124161800Smarcel	free(tcb);
125133754Sdfr}
126133754Sdfr
127133754Sdfr/*
128133754Sdfr * Allocate Static TLS using the Variant I method.
129133754Sdfr */
130133064Sdfrvoid *
131161800Smarcel__libc_allocate_tls(void *oldtcb, size_t tcbsize, size_t tcbalign __unused)
132133064Sdfr{
133133754Sdfr	Elf_Addr *dtv;
134161800Smarcel	Elf_Addr **tls;
135161800Smarcel	char *tcb;
136133754Sdfr
137161800Smarcel	if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE)
138161800Smarcel		return (oldtcb);
139133754Sdfr
140203946Smarcel	tcb = calloc(1, tls_static_space + tcbsize - TLS_TCB_SIZE);
141161800Smarcel	tls = (Elf_Addr **)(tcb + tcbsize - TLS_TCB_SIZE);
142133754Sdfr
143161800Smarcel	if (oldtcb != NULL) {
144203946Smarcel		memcpy(tls, oldtcb, tls_static_space);
145161800Smarcel		free(oldtcb);
146133754Sdfr
147161800Smarcel		/* Adjust the DTV. */
148161800Smarcel		dtv = tls[0];
149161800Smarcel		dtv[2] = (Elf_Addr)tls + TLS_TCB_SIZE;
150161800Smarcel	} else {
151161800Smarcel		dtv = malloc(3 * sizeof(Elf_Addr));
152161800Smarcel		tls[0] = dtv;
153161800Smarcel		dtv[0] = 1;
154161800Smarcel		dtv[1] = 1;
155161800Smarcel		dtv[2] = (Elf_Addr)tls + TLS_TCB_SIZE;
156133754Sdfr
157161800Smarcel		if (tls_init_size > 0)
158161827Smarcel			memcpy((void*)dtv[2], tls_init, tls_init_size);
159161800Smarcel		if (tls_static_space > tls_init_size)
160161827Smarcel			memset((void*)(dtv[2] + tls_init_size), 0,
161161827Smarcel			    tls_static_space - tls_init_size);
162133754Sdfr	}
163133754Sdfr
164161800Smarcel	return(tcb);
165133064Sdfr}
166133064Sdfr
167133754Sdfr#endif
168133754Sdfr
169133754Sdfr#ifdef TLS_VARIANT_II
170133754Sdfr
171161800Smarcel#define	TLS_TCB_SIZE	(3 * sizeof(Elf_Addr))
172161800Smarcel
173133754Sdfr/*
174133754Sdfr * Free Static TLS using the Variant II method.
175133754Sdfr */
176133064Sdfrvoid
177143921Sdavidxu__libc_free_tls(void *tcb, size_t tcbsize __unused, size_t tcbalign)
178133064Sdfr{
179133754Sdfr	size_t size;
180133754Sdfr	Elf_Addr* dtv;
181133754Sdfr	Elf_Addr tlsstart, tlsend;
182133754Sdfr
183133754Sdfr	/*
184133754Sdfr	 * Figure out the size of the initial TLS block so that we can
185133754Sdfr	 * find stuff which ___tls_get_addr() allocated dynamically.
186133754Sdfr	 */
187133754Sdfr	size = round(tls_static_space, tcbalign);
188133754Sdfr
189133754Sdfr	dtv = ((Elf_Addr**)tcb)[1];
190133754Sdfr	tlsend = (Elf_Addr) tcb;
191133754Sdfr	tlsstart = tlsend - size;
192133754Sdfr	free((void*) tlsstart);
193133754Sdfr	free(dtv);
194133064Sdfr}
195133754Sdfr
196133754Sdfr/*
197133754Sdfr * Allocate Static TLS using the Variant II method.
198133754Sdfr */
199133754Sdfrvoid *
200143921Sdavidxu__libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
201133754Sdfr{
202133754Sdfr	size_t size;
203133754Sdfr	char *tls;
204133754Sdfr	Elf_Addr *dtv;
205133754Sdfr	Elf_Addr segbase, oldsegbase;
206133754Sdfr
207133754Sdfr	size = round(tls_static_space, tcbalign);
208133754Sdfr
209166995Skientzle	if (tcbsize < 2 * sizeof(Elf_Addr))
210166995Skientzle		tcbsize = 2 * sizeof(Elf_Addr);
211157198Sdavidxu	tls = calloc(1, size + tcbsize);
212133754Sdfr	dtv = malloc(3 * sizeof(Elf_Addr));
213133754Sdfr
214133754Sdfr	segbase = (Elf_Addr)(tls + size);
215133754Sdfr	((Elf_Addr*)segbase)[0] = segbase;
216133754Sdfr	((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv;
217133754Sdfr
218133754Sdfr	dtv[0] = 1;
219133754Sdfr	dtv[1] = 1;
220133754Sdfr	dtv[2] = segbase - tls_static_space;
221133754Sdfr
222133754Sdfr	if (oldtls) {
223133754Sdfr		/*
224133754Sdfr		 * Copy the static TLS block over whole.
225133754Sdfr		 */
226133754Sdfr		oldsegbase = (Elf_Addr) oldtls;
227133754Sdfr		memcpy((void *)(segbase - tls_static_space),
228133754Sdfr		    (const void *)(oldsegbase - tls_static_space),
229133754Sdfr		    tls_static_space);
230133754Sdfr
231133754Sdfr		/*
232133754Sdfr		 * We assume that this block was the one we created with
233133754Sdfr		 * allocate_initial_tls().
234133754Sdfr		 */
235133754Sdfr		_rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
236133754Sdfr	} else {
237133754Sdfr		memcpy((void *)(segbase - tls_static_space),
238133754Sdfr		    tls_init, tls_init_size);
239133754Sdfr		memset((void *)(segbase - tls_static_space + tls_init_size),
240133754Sdfr		    0, tls_static_space - tls_init_size);
241133754Sdfr	}
242133754Sdfr
243133754Sdfr	return (void*) segbase;
244143921Sdavidxu}
245143921Sdavidxu
246143921Sdavidxu#endif /* TLS_VARIANT_II */
247143921Sdavidxu
248133754Sdfr#else
249143921Sdavidxu
250143921Sdavidxuvoid *
251143921Sdavidxu__libc_allocate_tls(void *oldtls __unused, size_t tcbsize __unused,
252143921Sdavidxu	size_t tcbalign __unused)
253143921Sdavidxu{
254133754Sdfr	return (0);
255133754Sdfr}
256133754Sdfr
257143921Sdavidxuvoid
258143921Sdavidxu__libc_free_tls(void *tcb __unused, size_t tcbsize __unused,
259143921Sdavidxu	size_t tcbalign __unused)
260143921Sdavidxu{
261143921Sdavidxu}
262133754Sdfr
263143921Sdavidxu#endif /* PIC */
264143921Sdavidxu
265143921Sdavidxuextern char **environ;
266143921Sdavidxu
267133754Sdfrvoid
268133754Sdfr_init_tls()
269133754Sdfr{
270133754Sdfr#ifndef PIC
271133754Sdfr	Elf_Addr *sp;
272133754Sdfr	Elf_Auxinfo *aux, *auxp;
273133754Sdfr	Elf_Phdr *phdr;
274133754Sdfr	size_t phent, phnum;
275133754Sdfr	int i;
276133949Sdfr	void *tls;
277133754Sdfr
278133754Sdfr	sp = (Elf_Addr *) environ;
279133754Sdfr	while (*sp++ != 0)
280133754Sdfr		;
281133754Sdfr	aux = (Elf_Auxinfo *) sp;
282133754Sdfr	phdr = 0;
283133754Sdfr	phent = phnum = 0;
284133754Sdfr	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
285133754Sdfr		switch (auxp->a_type) {
286133754Sdfr		case AT_PHDR:
287133754Sdfr			phdr = auxp->a_un.a_ptr;
288133754Sdfr			break;
289133754Sdfr
290133754Sdfr		case AT_PHENT:
291133754Sdfr			phent = auxp->a_un.a_val;
292133754Sdfr			break;
293133754Sdfr
294133754Sdfr		case AT_PHNUM:
295133754Sdfr			phnum = auxp->a_un.a_val;
296133754Sdfr			break;
297133754Sdfr		}
298133754Sdfr	}
299133754Sdfr	if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0)
300133754Sdfr		return;
301133754Sdfr
302143921Sdavidxu	for (i = 0; (unsigned) i < phnum; i++) {
303133754Sdfr		if (phdr[i].p_type == PT_TLS) {
304133754Sdfr			tls_static_space = round(phdr[i].p_memsz,
305133754Sdfr			    phdr[i].p_align);
306133754Sdfr			tls_init_size = phdr[i].p_filesz;
307133754Sdfr			tls_init = (void*) phdr[i].p_vaddr;
308133754Sdfr		}
309133754Sdfr	}
310133754Sdfr
311163118Skmacy	tls = _rtld_allocate_tls(NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN);
312133754Sdfr
313133949Sdfr	_set_tp(tls);
314133754Sdfr#endif
315133754Sdfr}
316