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