xref: /dflybsd-src/lib/libc/gen/tls.c (revision 09bb7c065612e05665d0da54f7670621900eec2a)
1 /*-
2  * Copyright (c) 2004 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$FreeBSD: src/lib/libc/gen/tls.c,v 1.7 2005/03/01 23:42:00 davidxu Exp $
27  *	$DragonFly: src/lib/libc/gen/tls.c,v 1.1 2005/03/08 13:04:38 davidxu Exp $
28  */
29 
30 /*
31  * Define stubs for TLS internals so that programs and libraries can
32  * link. These functions will be replaced by functional versions at
33  * runtime from ld-elf.so.1.
34  */
35 
36 #include <stdlib.h>
37 #include <string.h>
38 #include <elf.h>
39 #include <assert.h>
40 #include "libc_private.h"
41 
42 /* XXX not sure what variants to use for arm. */
43 
44 #if defined(__ia64__) || defined(__powerpc__)
45 #define TLS_VARIANT_I
46 #endif
47 #if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \
48     defined(__arm__)
49 #define TLS_VARIANT_II
50 #endif
51 
52 #ifndef PIC
53 
54 #define round(size, align) \
55 	(((size) + (align) - 1) & ~((align) - 1))
56 
57 static size_t tls_static_space;
58 static size_t tls_init_size;
59 #ifdef TLS_VARIANT_I
60 static size_t tls_init_offset;
61 #endif
62 static void *tls_init;
63 #endif
64 
65 void *_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign);
66 void _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign);
67 void *__tls_get_addr(void *);
68 
69 #ifdef __i386__
70 
71 extern void *___tls_get_addr(void *ti) __attribute__((__regparm__(1)));
72 
73 #pragma weak ___tls_get_addr
74 __attribute__((__regparm__(1)))
75 void *
76 ___tls_get_addr(void *ti __unused)
77 {
78 	return (0);
79 }
80 
81 #endif
82 
83 #pragma weak __tls_get_addr
84 void *
85 __tls_get_addr(void *ti __unused)
86 {
87 	return (0);
88 }
89 
90 #ifdef TLS_VARIANT_I
91 
92 #pragma weak _rtld_free_tls
93 /*
94  * Free Static TLS using the Variant I method.
95  */
96 void
97 _rtld_free_tls(void *tls, size_t tcbsize, size_t tcbalign)
98 {
99 #ifndef PIC
100 	Elf_Addr* dtv;
101 
102 	dtv = ((Elf_Addr**)tls)[0];
103 	free(tls);
104 	free(dtv);
105 #endif
106 }
107 
108 #pragma weak _rtld_allocate_tls
109 /*
110  * Allocate Static TLS using the Variant I method.
111  */
112 void *
113 _rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
114 {
115 #ifndef PIC
116 	size_t size;
117 	char *tls;
118 	Elf_Addr *dtv;
119 
120 	size = tls_static_space;
121 	if (size < tcbsize)
122 		size = tcbsize;
123 
124 	tls = malloc(size);
125 	dtv = malloc(3 * sizeof(Elf_Addr));
126 
127 	*(Elf_Addr**) tls = dtv;
128 
129 	dtv[0] = 1;
130 	dtv[1] = 1;
131 	dtv[2] = (Elf_Addr)(tls + tls_init_offset);
132 	if (oldtls) {
133 		/*
134 		 * Copy the static TLS block over whole.
135 		 */
136 		memcpy(tls + tls_init_offset,
137 		    (char*) oldtls + tls_init_offset,
138 		    tls_static_space - tls_init_offset);
139 
140 		/*
141 		 * We assume that this block was the one we created with
142 		 * allocate_initial_tls().
143 		 */
144 		_rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
145 	} else {
146 		memcpy(tls + tls_init_offset, tls_init, tls_init_size);
147 		memset(tls + tls_init_offset + tls_init_size,
148 		    0, tls_static_space - tls_init_size);
149 	}
150 
151 	return tls;
152 #else
153 	return (0);
154 #endif
155 }
156 
157 #endif
158 
159 #ifdef TLS_VARIANT_II
160 
161 /*
162  * Free Static TLS using the Variant II method.
163  */
164 #pragma weak _rtld_free_tls
165 void
166 _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
167 {
168 #ifndef PIC
169 	size_t size;
170 	Elf_Addr* dtv;
171 	Elf_Addr tlsstart, tlsend;
172 
173 	/*
174 	 * Figure out the size of the initial TLS block so that we can
175 	 * find stuff which ___tls_get_addr() allocated dynamically.
176 	 */
177 	size = round(tls_static_space, tcbalign);
178 
179 	dtv = ((Elf_Addr**)tcb)[1];
180 	tlsend = (Elf_Addr) tcb;
181 	tlsstart = tlsend - size;
182 	free((void*) tlsstart);
183 	free(dtv);
184 #endif
185 }
186 
187 #pragma weak _rtld_allocate_tls
188 /*
189  * Allocate Static TLS using the Variant II method.
190  */
191 void *
192 _rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
193 {
194 #ifndef PIC
195 	size_t size;
196 	char *tls;
197 	Elf_Addr *dtv;
198 	Elf_Addr segbase, oldsegbase;
199 
200 	size = round(tls_static_space, tcbalign);
201 
202 	assert(tcbsize >= 2*sizeof(Elf_Addr));
203 	tls = malloc(size + tcbsize);
204 	dtv = malloc(3 * sizeof(Elf_Addr));
205 
206 	segbase = (Elf_Addr)(tls + size);
207 	((Elf_Addr*)segbase)[0] = segbase;
208 	((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv;
209 
210 	dtv[0] = 1;
211 	dtv[1] = 1;
212 	dtv[2] = segbase - tls_static_space;
213 
214 	if (oldtls) {
215 		/*
216 		 * Copy the static TLS block over whole.
217 		 */
218 		oldsegbase = (Elf_Addr) oldtls;
219 		memcpy((void *)(segbase - tls_static_space),
220 		    (const void *)(oldsegbase - tls_static_space),
221 		    tls_static_space);
222 
223 		/*
224 		 * We assume that this block was the one we created with
225 		 * allocate_initial_tls().
226 		 */
227 		_rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
228 	} else {
229 		memcpy((void *)(segbase - tls_static_space),
230 		    tls_init, tls_init_size);
231 		memset((void *)(segbase - tls_static_space + tls_init_size),
232 		    0, tls_static_space - tls_init_size);
233 	}
234 
235 	return (void*) segbase;
236 #else
237 	return (0);
238 #endif
239 }
240 
241 #endif /* TLS_VARIANT_II */
242 
243 extern char **environ;
244 
245 void
246 _init_tls()
247 {
248 #ifndef PIC
249 	Elf_Addr *sp;
250 	Elf_Auxinfo *aux, *auxp;
251 	Elf_Phdr *phdr;
252 	size_t phent, phnum;
253 	int i;
254 	void *tls;
255 
256 	sp = (Elf_Addr *) environ;
257 	while (*sp++ != 0)
258 		;
259 	aux = (Elf_Auxinfo *) sp;
260 	phdr = 0;
261 	phent = phnum = 0;
262 	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
263 		switch (auxp->a_type) {
264 		case AT_PHDR:
265 			phdr = auxp->a_un.a_ptr;
266 			break;
267 
268 		case AT_PHENT:
269 			phent = auxp->a_un.a_val;
270 			break;
271 
272 		case AT_PHNUM:
273 			phnum = auxp->a_un.a_val;
274 			break;
275 		}
276 	}
277 	if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0)
278 		return;
279 
280 	for (i = 0; (unsigned)i < phnum; i++) {
281 		if (phdr[i].p_type == PT_TLS) {
282 #ifdef TLS_VARIANT_I
283 			tls_static_space = round(2*sizeof(Elf_Addr),
284 			    phdr[i].p_align) + phdr[i].p_memsz;
285 			tls_init_offset = round(2*sizeof(Elf_Addr),
286 			    phdr[i].p_align);
287 #else
288 			tls_static_space = round(phdr[i].p_memsz,
289 			    phdr[i].p_align);
290 #endif
291 			tls_init_size = phdr[i].p_filesz;
292 			tls_init = (void*) phdr[i].p_vaddr;
293 		}
294 	}
295 
296 	tls = _rtld_allocate_tls(NULL, 2*sizeof(Elf_Addr),
297 	    sizeof(Elf_Addr));
298 
299 	_set_tp(tls, 2 * sizeof(Elf_Addr));
300 #endif
301 }
302