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