Cleanup the TLS implementation:
[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.5 2005/03/29 19:26:19 joerg 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 <sys/tls.h>
38
39 #include <machine/tls.h>
40
41 #include <stdlib.h>
42 #include <string.h>
43 #include <elf.h>
44 #include <assert.h>
45
46 #include "libc_private.h"
47
48 __weak_reference(__libc_allocate_tls, _rtld_allocate_tls);
49 __weak_reference(__libc_free_tls, _rtld_free_tls);
50 #ifdef __i386__
51 __weak_reference(___libc_tls_get_addr, ___tls_get_addr);
52 #endif
53 __weak_reference(__libc_tls_get_addr, __tls_get_addr);
54
55 struct tls_tcb *__libc_allocate_tls(struct tls_tcb *old_tcb, size_t tcbsize,
56                                     int flags);
57 void __libc_free_tls(struct tls_tcb *tcb, size_t tcb_size);
58
59 #if !defined(RTLD_STATIC_TLS_VARIANT_II)
60 #error "Unsupported TLS layout"
61 #endif
62
63 #ifndef PIC
64
65 #define round(size, align) \
66         (((size) + (align) - 1) & ~((align) - 1))
67
68 static size_t tls_static_space;
69 static size_t tls_init_size;
70 static void *tls_init;
71 #endif
72
73 #ifdef __i386__
74
75 /* GNU ABI */
76
77 void *___libc_tls_get_addr(void *ti) __attribute__((__regparm__(1)));
78
79 __attribute__((__regparm__(1)))
80 void *
81 ___libc_tls_get_addr(void *ti __unused)
82 {
83         return (0);
84 }
85
86 #endif
87
88 void *__libc_tls_get_addr(void *ti);
89
90 void *
91 __libc_tls_get_addr(void *ti __unused)
92 {
93         return (0);
94 }
95
96 #ifndef PIC
97
98 /*
99  * Free Static TLS
100  */
101 void
102 __libc_free_tls(struct tls_tcb *tcb, size_t tcb_size __unused)
103 {
104         size_t data_size;
105
106         if (tcb->tcb_dtv)
107                 free(tcb->tcb_dtv);
108         data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
109                     ~RTLD_STATIC_TLS_ALIGN_MASK;
110         free((char *)tcb - data_size);
111 }
112
113 /*
114  * Allocate Static TLS.
115  */
116 struct tls_tcb *
117 __libc_allocate_tls(struct tls_tcb *old_tcb, size_t tcb_size, int flags)
118 {
119         size_t data_size;
120         struct tls_tcb *tcb;
121         Elf_Addr *dtv;
122
123         data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
124                     ~RTLD_STATIC_TLS_ALIGN_MASK;
125         tcb = malloc(data_size + tcb_size);
126         tcb = (struct tls_tcb *)((char *)tcb + data_size);
127         dtv = malloc(3 * sizeof(Elf_Addr));
128
129 #ifdef RTLD_TCB_HAS_SELF_POINTER
130         tcb->tcb_self = tcb;
131 #endif
132         tcb->tcb_dtv = dtv;
133         tcb->tcb_pthread = NULL;
134
135         dtv[0] = 1;
136         dtv[1] = 1;
137         dtv[2] = (Elf_Addr)((char *)tcb - tls_static_space);
138
139         if (old_tcb) {
140                 /*
141                  * Copy the static TLS block over whole.
142                  */
143                 memcpy((char *)tcb - tls_static_space,
144                         (char *)old_tcb - tls_static_space,
145                         tls_static_space);
146
147                 /*
148                  * We assume that this block was the one we created with
149                  * allocate_initial_tls().
150                  */
151                 _rtld_free_tls(old_tcb, sizeof(struct tls_tcb));
152         } else {
153                 memcpy((char *)tcb - tls_static_space,
154                         tls_init, tls_init_size);
155                 memset((char *)tcb - tls_static_space + tls_init_size,
156                         0, tls_static_space - tls_init_size);
157         }
158         return (tcb);
159 }
160
161 #else
162
163 struct tls_tcb *
164 __libc_allocate_tls(struct tls_tcb *old_tls __unused, size_t tcb_size __unused,
165                         int flags __unused)
166 {
167         return (0);
168 }
169
170 void
171 __libc_free_tls(struct tls_tcb *tcb __unused, size_t tcb_size __unused)
172 {
173 }
174
175 #endif /* PIC */
176
177 extern char **environ;
178
179 void
180 _init_tls()
181 {
182 #ifndef PIC
183         Elf_Addr *sp;
184         Elf_Auxinfo *aux, *auxp;
185         Elf_Phdr *phdr;
186         size_t phent, phnum;
187         int i;
188         struct tls_tcb *tcb;
189
190         sp = (Elf_Addr *) environ;
191         while (*sp++ != 0)
192                 ;
193         aux = (Elf_Auxinfo *) sp;
194         phdr = 0;
195         phent = phnum = 0;
196         for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
197                 switch (auxp->a_type) {
198                 case AT_PHDR:
199                         phdr = auxp->a_un.a_ptr;
200                         break;
201
202                 case AT_PHENT:
203                         phent = auxp->a_un.a_val;
204                         break;
205
206                 case AT_PHNUM:
207                         phnum = auxp->a_un.a_val;
208                         break;
209                 }
210         }
211         if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0)
212                 return;
213
214         for (i = 0; (unsigned)i < phnum; i++) {
215                 if (phdr[i].p_type == PT_TLS) {
216                         tls_static_space = round(phdr[i].p_memsz,
217                             phdr[i].p_align);
218                         tls_init_size = phdr[i].p_filesz;
219                         tls_init = (void*) phdr[i].p_vaddr;
220                 }
221         }
222
223         if (tls_static_space) {
224                 tcb = _rtld_allocate_tls(NULL, sizeof(struct tls_tcb), 0);
225                 tls_set_tcb(tcb);
226         }
227 #endif
228 }