xref: /dflybsd-src/lib/libc/gen/tls.c (revision b721b9ee4205a28b1f2d01b5c95851add56bf82b)
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.4 2005/03/28 03:33:12 dillon 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 #include <stdlib.h>
39 #include <string.h>
40 #include <elf.h>
41 #include <assert.h>
42 
43 #include "libc_private.h"
44 
45 __weak_reference(__libc_allocate_tls, _rtld_allocate_tls);
46 __weak_reference(__libc_free_tls, _rtld_free_tls);
47 #ifdef __i386__
48 __weak_reference(___libc_tls_get_addr, ___tls_get_addr);
49 #endif
50 __weak_reference(__libc_tls_get_addr, __tls_get_addr);
51 
52 struct tls_tcb *_rtld_allocate_tls(struct tls_tcb *, size_t tcbsize, int flags);
53 void _rtld_free_tls(struct tls_tcb *tcb, size_t tcb_size);
54 struct tls_tcb *__libc_allocate_tls(struct tls_tcb *old_tcb, size_t tcbsize,
55 				    int flags);
56 void __libc_free_tls(struct tls_tcb *tcb, size_t tcb_size);
57 
58 #if defined(__ia64__) || defined(__powerpc__)
59 #define TLS_VARIANT_I
60 #endif
61 #if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \
62     defined(__arm__)
63 #define TLS_VARIANT_II
64 #endif
65 
66 #ifndef PIC
67 
68 #define round(size, align) \
69 	(((size) + (align) - 1) & ~((align) - 1))
70 
71 static size_t tls_static_space;
72 static size_t tls_init_size;
73 static void *tls_init;
74 #endif
75 
76 #ifdef __i386__
77 
78 /* GNU ABI */
79 
80 void *___libc_tls_get_addr(void *ti) __attribute__((__regparm__(1)));
81 
82 __attribute__((__regparm__(1)))
83 void *
84 ___libc_tls_get_addr(void *ti __unused)
85 {
86 	return (0);
87 }
88 
89 #endif
90 
91 void *__libc_tls_get_addr(void *ti);
92 
93 void *
94 __libc_tls_get_addr(void *ti __unused)
95 {
96 	return (0);
97 }
98 
99 #ifndef PIC
100 
101 /*
102  * Free Static TLS
103  */
104 void
105 __libc_free_tls(struct tls_tcb *tcb, size_t tcb_size __unused)
106 {
107 	size_t data_size;
108 
109 	if (tcb->dtv_base)
110 		free(tcb->dtv_base);
111 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
112 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
113 	free((char *)tcb - data_size);
114 }
115 
116 /*
117  * Allocate Static TLS.
118  */
119 struct tls_tcb *
120 __libc_allocate_tls(struct tls_tcb *old_tcb, size_t tcb_size, int flags)
121 {
122 	size_t data_size;
123 	struct tls_tcb *tcb;
124 	Elf_Addr *dtv;
125 
126 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
127 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
128 	tcb = malloc(data_size + tcb_size);
129 	tcb = (struct tls_tcb *)((char *)tcb + data_size);
130 	bzero(tcb, tcb_size);
131 	dtv = malloc(3 * sizeof(Elf_Addr));
132 	tcb->tcb_base = tcb;
133 	tcb->dtv_base = dtv;
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 		_set_tp(tcb, (size_t)-1);
226 	}
227 #endif
228 }
229