xref: /freebsd/lib/libc/gen/tls.c (revision ca9ac06c99bfd0150b85d4d83c396ce6237c0e05)
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$
27  */
28 
29 /*
30  * Define stubs for TLS internals so that programs and libraries can
31  * link. These functions will be replaced by functional versions at
32  * runtime from ld-elf.so.1.
33  */
34 
35 #include <stdlib.h>
36 #include <string.h>
37 #include <elf.h>
38 #include <assert.h>
39 #include "libc_private.h"
40 
41 /* XXX not sure what variants to use for arm. */
42 
43 #if defined(__ia64__) || defined(__alpha__) || defined(__powerpc__)
44 #define TLS_VARIANT_I
45 #endif
46 #if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \
47     defined(__arm__)
48 #define TLS_VARIANT_II
49 #endif
50 
51 #ifndef PIC
52 
53 #define round(size, align) \
54 	(((size) + (align) - 1) & ~((align) - 1))
55 
56 static size_t tls_static_space;
57 static size_t tls_init_size;
58 #ifdef TLS_VARIANT_I
59 static size_t tls_init_offset;
60 #endif
61 static void *tls_init;
62 
63 void _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign);
64 #endif
65 
66 #ifdef __i386__
67 
68 extern void *___tls_get_addr(void *ti) __attribute__((__regparm__(1)));
69 
70 #pragma weak ___tls_get_addr
71 __attribute__((__regparm__(1)))
72 void *
73 ___tls_get_addr(void *ti)
74 {
75 	return (0);
76 }
77 
78 #endif
79 
80 #pragma weak __tls_get_addr
81 void *
82 __tls_get_addr()
83 {
84 	return (0);
85 }
86 
87 #ifdef TLS_VARIANT_I
88 
89 #pragma weak _rtld_free_tls
90 /*
91  * Free Static TLS using the Variant I method.
92  */
93 void
94 _rtld_free_tls(void *tls, size_t tcbsize, size_t tcbalign)
95 {
96 #ifndef PIC
97 	Elf_Addr* dtv;
98 
99 	dtv = ((Elf_Addr**)tls)[0];
100 	free(tls);
101 	free(dtv);
102 #endif
103 }
104 
105 #pragma weak _rtld_allocate_tls
106 /*
107  * Allocate Static TLS using the Variant I method.
108  */
109 void *
110 _rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
111 {
112 #ifndef PIC
113 	size_t size;
114 	char *tls;
115 	Elf_Addr *dtv;
116 
117 	size = tls_static_space;
118 	if (size < tcbsize)
119 		size = tcbsize;
120 
121 	tls = malloc(size);
122 	dtv = malloc(3 * sizeof(Elf_Addr));
123 
124 	*(Elf_Addr**) tls = dtv;
125 
126 	dtv[0] = 1;
127 	dtv[1] = 1;
128 	dtv[2] = (Elf_Addr)(tls + tls_init_offset);
129 	if (oldtls) {
130 		/*
131 		 * Copy the static TLS block over whole.
132 		 */
133 		memcpy(tls + tls_init_offset,
134 		    (char*) oldtls + tls_init_offset,
135 		    tls_static_space - tls_init_offset);
136 
137 		/*
138 		 * We assume that this block was the one we created with
139 		 * allocate_initial_tls().
140 		 */
141 		_rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
142 	} else {
143 		memcpy(tls + tls_init_offset, tls_init, tls_init_size);
144 		memset(tls + tls_init_offset + tls_init_size,
145 		    0, tls_static_space - tls_init_size);
146 	}
147 
148 	return tls;
149 #else
150 	return (0);
151 #endif
152 }
153 
154 #endif
155 
156 #ifdef TLS_VARIANT_II
157 
158 /*
159  * Free Static TLS using the Variant II method.
160  */
161 #pragma weak _rtld_free_tls
162 void
163 _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
164 {
165 #ifndef PIC
166 	size_t size;
167 	Elf_Addr* dtv;
168 	Elf_Addr tlsstart, tlsend;
169 
170 	/*
171 	 * Figure out the size of the initial TLS block so that we can
172 	 * find stuff which ___tls_get_addr() allocated dynamically.
173 	 */
174 	size = round(tls_static_space, tcbalign);
175 
176 	dtv = ((Elf_Addr**)tcb)[1];
177 	tlsend = (Elf_Addr) tcb;
178 	tlsstart = tlsend - size;
179 	free((void*) tlsstart);
180 	free(dtv);
181 #endif
182 }
183 
184 #pragma weak _rtld_allocate_tls
185 /*
186  * Allocate Static TLS using the Variant II method.
187  */
188 void *
189 _rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
190 {
191 #ifndef PIC
192 	size_t size;
193 	char *tls;
194 	Elf_Addr *dtv;
195 	Elf_Addr segbase, oldsegbase;
196 
197 	size = round(tls_static_space, tcbalign);
198 
199 	assert(tcbsize >= 2*sizeof(Elf_Addr));
200 	tls = malloc(size + tcbsize);
201 	dtv = malloc(3 * sizeof(Elf_Addr));
202 
203 	segbase = (Elf_Addr)(tls + size);
204 	((Elf_Addr*)segbase)[0] = segbase;
205 	((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv;
206 
207 	dtv[0] = 1;
208 	dtv[1] = 1;
209 	dtv[2] = segbase - tls_static_space;
210 
211 	if (oldtls) {
212 		/*
213 		 * Copy the static TLS block over whole.
214 		 */
215 		oldsegbase = (Elf_Addr) oldtls;
216 		memcpy((void *)(segbase - tls_static_space),
217 		    (const void *)(oldsegbase - tls_static_space),
218 		    tls_static_space);
219 
220 		/*
221 		 * We assume that this block was the one we created with
222 		 * allocate_initial_tls().
223 		 */
224 		_rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
225 	} else {
226 		memcpy((void *)(segbase - tls_static_space),
227 		    tls_init, tls_init_size);
228 		memset((void *)(segbase - tls_static_space + tls_init_size),
229 		    0, tls_static_space - tls_init_size);
230 	}
231 
232 	return (void*) segbase;
233 #else
234 	return (0);
235 #endif
236 }
237 
238 #endif
239 
240 void
241 _init_tls()
242 {
243 #ifndef PIC
244 	extern char **environ;
245 	Elf_Addr *sp;
246 	Elf_Auxinfo *aux, *auxp;
247 	Elf_Phdr *phdr;
248 	size_t phent, phnum;
249 	int i;
250 	void *tls;
251 
252 	sp = (Elf_Addr *) environ;
253 	while (*sp++ != 0)
254 		;
255 	aux = (Elf_Auxinfo *) sp;
256 	phdr = 0;
257 	phent = phnum = 0;
258 	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
259 		switch (auxp->a_type) {
260 		case AT_PHDR:
261 			phdr = auxp->a_un.a_ptr;
262 			break;
263 
264 		case AT_PHENT:
265 			phent = auxp->a_un.a_val;
266 			break;
267 
268 		case AT_PHNUM:
269 			phnum = auxp->a_un.a_val;
270 			break;
271 		}
272 	}
273 	if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0)
274 		return;
275 
276 	for (i = 0; i < phnum; i++) {
277 		if (phdr[i].p_type == PT_TLS) {
278 #ifdef TLS_VARIANT_I
279 			tls_static_space = round(2*sizeof(Elf_Addr),
280 			    phdr[i].p_align) + phdr[i].p_memsz;
281 			tls_init_offset = round(2*sizeof(Elf_Addr),
282 			    phdr[i].p_align);
283 #else
284 			tls_static_space = round(phdr[i].p_memsz,
285 			    phdr[i].p_align);
286 #endif
287 			tls_init_size = phdr[i].p_filesz;
288 			tls_init = (void*) phdr[i].p_vaddr;
289 		}
290 	}
291 
292 	tls = _rtld_allocate_tls(NULL, 2*sizeof(Elf_Addr),
293 	    sizeof(Elf_Addr));
294 
295 	_set_tp(tls);
296 #endif
297 }
298