Commit | Line | Data |
---|---|---|
b00401f0 DX |
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 $ | |
b00401f0 DX |
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 | ||
965b839f | 35 | #include <sys/param.h> |
bc633d63 | 36 | #include <sys/tls.h> |
dc676eae | 37 | #include <sys/mman.h> |
9e2ee207 JS |
38 | |
39 | #include <machine/tls.h> | |
40 | ||
b00401f0 DX |
41 | #include <stdlib.h> |
42 | #include <string.h> | |
43 | #include <elf.h> | |
44 | #include <assert.h> | |
a1eee96a MD |
45 | #include <errno.h> |
46 | #include <unistd.h> | |
92df6c3e | 47 | |
b00401f0 DX |
48 | #include "libc_private.h" |
49 | ||
a1eee96a | 50 | struct tls_tcb *__libc_allocate_tls(void); |
f20fd431 | 51 | void __libc_free_tls(struct tls_tcb *tcb); |
a32e3ba6 | 52 | void __libc_call_init(void); |
b00401f0 | 53 | |
9e2ee207 JS |
54 | #if !defined(RTLD_STATIC_TLS_VARIANT_II) |
55 | #error "Unsupported TLS layout" | |
b00401f0 DX |
56 | #endif |
57 | ||
58 | #ifndef PIC | |
59 | ||
b00401f0 DX |
60 | static size_t tls_static_space; |
61 | static size_t tls_init_size; | |
b00401f0 | 62 | static void *tls_init; |
a1eee96a | 63 | static struct tls_tcb *initial_tcb; |
b00401f0 DX |
64 | #endif |
65 | ||
92df6c3e | 66 | void *__libc_tls_get_addr(void *ti); |
a1eee96a | 67 | void *__libc_tls_get_addr_tcb(struct tls_tcb *, void *); |
bc633d63 | 68 | |
b00401f0 | 69 | void * |
92df6c3e | 70 | __libc_tls_get_addr(void *ti __unused) |
b00401f0 | 71 | { |
a1eee96a MD |
72 | return (NULL); |
73 | } | |
74 | ||
75 | void * | |
76 | __libc_tls_get_addr_tcb(struct tls_tcb *tcb __unused, void *got_ptr __unused) | |
77 | { | |
78 | return (NULL); | |
b00401f0 DX |
79 | } |
80 | ||
92df6c3e DX |
81 | #ifndef PIC |
82 | ||
b00401f0 | 83 | /* |
a1eee96a | 84 | * Free Static TLS, weakly bound to _rtld_free_tls() |
b00401f0 DX |
85 | */ |
86 | void | |
f20fd431 | 87 | __libc_free_tls(struct tls_tcb *tcb) |
b00401f0 | 88 | { |
bc633d63 | 89 | size_t data_size; |
b00401f0 | 90 | |
bc633d63 MD |
91 | data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) & |
92 | ~RTLD_STATIC_TLS_ALIGN_MASK; | |
a1eee96a MD |
93 | |
94 | if (tcb == initial_tcb) { | |
dc676eae | 95 | /* initial_tcb was allocated with mmap(), cannot call free() */ |
a1eee96a MD |
96 | } else { |
97 | free((char *)tcb - data_size); | |
98 | } | |
b00401f0 DX |
99 | } |
100 | ||
b00401f0 | 101 | /* |
a1eee96a | 102 | * Allocate Static TLS, weakly bound to _rtld_allocate_tls() |
a378ce7d | 103 | * |
a1eee96a MD |
104 | * NOTE! There is a chicken-and-egg problem here because no TLS exists |
105 | * on the first call into this function. | |
b00401f0 | 106 | */ |
bc633d63 | 107 | struct tls_tcb * |
a1eee96a | 108 | __libc_allocate_tls(void) |
b00401f0 | 109 | { |
bc633d63 MD |
110 | size_t data_size; |
111 | struct tls_tcb *tcb; | |
b00401f0 DX |
112 | Elf_Addr *dtv; |
113 | ||
bc633d63 MD |
114 | data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) & |
115 | ~RTLD_STATIC_TLS_ALIGN_MASK; | |
a378ce7d | 116 | |
a1eee96a MD |
117 | /* |
118 | * Allocate space. malloc() may require a working TLS segment | |
dc676eae MD |
119 | * so we use sbrk() for main's TLS. Oops, but in order to be |
120 | * compatible with older kernels we cannot use sbrk() because it | |
121 | * will generate an errno (which needs the TLS), so use mmap(). | |
a1eee96a | 122 | */ |
dc676eae MD |
123 | if (initial_tcb == NULL) { |
124 | size_t bytes; | |
125 | ||
126 | bytes = data_size + sizeof(*tcb) + 3 * sizeof(*dtv); | |
127 | bytes = (bytes + PAGE_MASK) & ~(size_t)PAGE_MASK; | |
128 | tcb = mmap((void *)1, bytes, | |
129 | PROT_READ | PROT_WRITE, | |
130 | MAP_PRIVATE | MAP_ANON, | |
131 | -1, 0); | |
132 | } else { | |
97c6bef2 | 133 | tcb = malloc(data_size + sizeof(*tcb) + 3 * sizeof(*dtv)); |
dc676eae | 134 | } |
a1eee96a | 135 | |
bc633d63 | 136 | tcb = (struct tls_tcb *)((char *)tcb + data_size); |
a1eee96a | 137 | dtv = (Elf_Addr *)(tcb + 1); |
9e2ee207 | 138 | |
a1eee96a | 139 | memset(tcb, 0, sizeof(*tcb)); |
9e2ee207 JS |
140 | #ifdef RTLD_TCB_HAS_SELF_POINTER |
141 | tcb->tcb_self = tcb; | |
142 | #endif | |
143 | tcb->tcb_dtv = dtv; | |
b00401f0 | 144 | |
a1eee96a MD |
145 | /* |
146 | * Dummy-up the module array. A static binary has only one. This | |
147 | * allows us to support the generic __tls_get_addr compiler ABI | |
148 | * function. However, if there is no RTLD linked in, nothing in | |
149 | * the program should ever call __tls_get_addr (and our version | |
150 | * of it doesn't do anything). | |
151 | */ | |
b00401f0 DX |
152 | dtv[0] = 1; |
153 | dtv[1] = 1; | |
bc633d63 | 154 | dtv[2] = (Elf_Addr)((char *)tcb - tls_static_space); |
b00401f0 | 155 | |
a1eee96a MD |
156 | memcpy((char *)tcb - tls_static_space, |
157 | tls_init, tls_init_size); | |
158 | memset((char *)tcb - tls_static_space + tls_init_size, | |
159 | 0, tls_static_space - tls_init_size); | |
160 | ||
161 | /* | |
162 | * Activate the initial TCB | |
163 | */ | |
164 | if (initial_tcb == NULL) { | |
165 | initial_tcb = tcb; | |
166 | tls_set_tcb(tcb); | |
b00401f0 | 167 | } |
a1eee96a | 168 | return (tcb); |
92df6c3e DX |
169 | } |
170 | ||
b00401f0 | 171 | #else |
92df6c3e | 172 | |
bc633d63 | 173 | struct tls_tcb * |
a1eee96a | 174 | __libc_allocate_tls(void) |
92df6c3e | 175 | { |
a1eee96a | 176 | return (NULL); |
b00401f0 DX |
177 | } |
178 | ||
92df6c3e | 179 | void |
f20fd431 | 180 | __libc_free_tls(struct tls_tcb *tcb __unused) |
92df6c3e DX |
181 | { |
182 | } | |
183 | ||
184 | #endif /* PIC */ | |
b00401f0 | 185 | |
a1eee96a MD |
186 | void |
187 | __libc_call_init(void) | |
188 | { | |
189 | } | |
190 | ||
b00401f0 DX |
191 | extern char **environ; |
192 | ||
a1eee96a MD |
193 | struct tls_tcb * |
194 | _libc_init_tls(void) | |
b00401f0 | 195 | { |
a1eee96a MD |
196 | struct tls_tcb *tcb; |
197 | ||
b00401f0 | 198 | #ifndef PIC |
a1eee96a MD |
199 | /* |
200 | * If this is a static binary there is no RTLD and so we have not | |
201 | * yet calculated the static space requirement. Do so now. | |
202 | */ | |
b00401f0 DX |
203 | Elf_Addr *sp; |
204 | Elf_Auxinfo *aux, *auxp; | |
205 | Elf_Phdr *phdr; | |
206 | size_t phent, phnum; | |
207 | int i; | |
b00401f0 DX |
208 | |
209 | sp = (Elf_Addr *) environ; | |
210 | while (*sp++ != 0) | |
211 | ; | |
212 | aux = (Elf_Auxinfo *) sp; | |
678e8cc6 | 213 | phdr = NULL; |
b00401f0 DX |
214 | phent = phnum = 0; |
215 | for (auxp = aux; auxp->a_type != AT_NULL; auxp++) { | |
216 | switch (auxp->a_type) { | |
217 | case AT_PHDR: | |
218 | phdr = auxp->a_un.a_ptr; | |
219 | break; | |
220 | ||
221 | case AT_PHENT: | |
222 | phent = auxp->a_un.a_val; | |
223 | break; | |
224 | ||
225 | case AT_PHNUM: | |
226 | phnum = auxp->a_un.a_val; | |
227 | break; | |
228 | } | |
229 | } | |
678e8cc6 | 230 | if (phdr == NULL || phent != sizeof(Elf_Phdr) || phnum == 0) |
b0e25abd | 231 | return(NULL); |
b00401f0 DX |
232 | |
233 | for (i = 0; (unsigned)i < phnum; i++) { | |
234 | if (phdr[i].p_type == PT_TLS) { | |
965b839f | 235 | tls_static_space = roundup2(phdr[i].p_memsz, |
b00401f0 | 236 | phdr[i].p_align); |
b00401f0 DX |
237 | tls_init_size = phdr[i].p_filesz; |
238 | tls_init = (void*) phdr[i].p_vaddr; | |
239 | } | |
240 | } | |
a1eee96a | 241 | #endif |
b00401f0 | 242 | |
a1eee96a MD |
243 | /* |
244 | * Allocate the initial TLS segment. The TLS has not been set up | |
245 | * yet for either the static or dynamic linked case (RTLD no longer | |
246 | * sets up an initial TLS segment for us). | |
247 | */ | |
248 | tcb = _libc_allocate_tls(); | |
7d6ff7ad | 249 | tls_set_tcb(tcb); |
a1eee96a MD |
250 | return(tcb); |
251 | } | |
252 | ||
253 | /* | |
254 | * Allocate a standard TLS. This function is called by libc and by | |
255 | * thread libraries to create a new TCB with libc-related fields properly | |
256 | * initialized (whereas _rtld_allocate_tls() is unable to completely set | |
257 | * up the TCB). | |
258 | * | |
259 | * Note that this is different from __libc_allocate_tls which is the | |
260 | * weakly bound symbol that handles the case where _rtld_allocate_tls | |
261 | * does not exist. | |
262 | */ | |
263 | struct tls_tcb * | |
264 | _libc_allocate_tls(void) | |
265 | { | |
266 | struct tls_tcb *tcb; | |
267 | ||
268 | tcb = _rtld_allocate_tls(); | |
a1eee96a | 269 | return(tcb); |
b00401f0 | 270 | } |
a1eee96a | 271 | |
f8406b33 | 272 | __weak_reference(__libc_allocate_tls, _rtld_allocate_tls); |
273 | __weak_reference(__libc_free_tls, _rtld_free_tls); | |
274 | __weak_reference(__libc_call_init, _rtld_call_init); | |
275 | __weak_reference(__libc_tls_get_addr, __tls_get_addr); | |
276 | __weak_reference(__libc_tls_get_addr_tcb, __tls_get_addr_tcb); | |
277 | __weak_reference(_libc_init_tls, _init_tls); |