xref: /dflybsd-src/lib/libc/gen/tls.c (revision 0402ebbc7d4b6f34d02791995169d25c4aec3b15)
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.8 2005/04/29 22:00:20 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);
56 void __libc_free_tls(struct tls_tcb *tcb);
57 
58 #if !defined(RTLD_STATIC_TLS_VARIANT_II)
59 #error "Unsupported TLS layout"
60 #endif
61 
62 #ifndef PIC
63 
64 #define round(size, align) \
65 	(((size) + (align) - 1) & ~((align) - 1))
66 
67 static size_t tls_static_space;
68 static size_t tls_init_size;
69 static void *tls_init;
70 #endif
71 
72 #ifdef __i386__
73 
74 /* GNU ABI */
75 
76 void *___libc_tls_get_addr(void *ti) __attribute__((__regparm__(1)));
77 
78 __attribute__((__regparm__(1)))
79 void *
80 ___libc_tls_get_addr(void *ti __unused)
81 {
82 	return (0);
83 }
84 
85 #endif
86 
87 void *__libc_tls_get_addr(void *ti);
88 
89 void *
90 __libc_tls_get_addr(void *ti __unused)
91 {
92 	return (0);
93 }
94 
95 #ifndef PIC
96 
97 /*
98  * Free Static TLS
99  */
100 void
101 __libc_free_tls(struct tls_tcb *tcb)
102 {
103 	size_t data_size;
104 
105 	if (tcb->tcb_dtv)
106 		free(tcb->tcb_dtv);
107 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
108 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
109 	free((char *)tcb - data_size);
110 }
111 
112 /*
113  * Allocate Static TLS.
114  *
115  * !!!WARNING!!! The first run for a static binary is done without valid
116  * TLS storage.  It must not call any system calls which want to change
117  * errno.
118  */
119 struct tls_tcb *
120 __libc_allocate_tls(struct tls_tcb *old_tcb)
121 {
122 	static int first_run = 0;
123 	size_t data_size;
124 	struct tls_tcb *tcb;
125 	Elf_Addr *dtv;
126 
127 retry:
128 	data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
129 		    ~RTLD_STATIC_TLS_ALIGN_MASK;
130 
131 	if (first_run)
132 		tcb = malloc(data_size + sizeof(*tcb));
133 	else
134 		tcb = alloca(data_size + sizeof(*tcb));
135 	tcb = (struct tls_tcb *)((char *)tcb + data_size);
136 	if (first_run)
137 		dtv = malloc(3 * sizeof(Elf_Addr));
138 	else
139 		dtv = alloca(3 * sizeof(Elf_Addr));
140 
141 #ifdef RTLD_TCB_HAS_SELF_POINTER
142 	tcb->tcb_self = tcb;
143 #endif
144 	tcb->tcb_dtv = dtv;
145 	tcb->tcb_pthread = NULL;
146 
147 	dtv[0] = 1;
148 	dtv[1] = 1;
149 	dtv[2] = (Elf_Addr)((char *)tcb - tls_static_space);
150 
151 	if (old_tcb) {
152 		/*
153 		 * Copy the static TLS block over whole.
154 		 */
155 		memcpy((char *)tcb - tls_static_space,
156 			(char *)old_tcb - tls_static_space,
157 			tls_static_space);
158 
159 		/*
160 		 * We assume that this block was the one we created with
161 		 * allocate_initial_tls().
162 		 */
163 		_rtld_free_tls(old_tcb);
164 	} else {
165 		memcpy((char *)tcb - tls_static_space,
166 			tls_init, tls_init_size);
167 		memset((char *)tcb - tls_static_space + tls_init_size,
168 			0, tls_static_space - tls_init_size);
169 	}
170 
171 	if (first_run)
172 		return (tcb);
173 
174 	tls_set_tcb(tcb);
175 	first_run = 1;
176 	goto retry;
177 }
178 
179 #else
180 
181 struct tls_tcb *
182 __libc_allocate_tls(struct tls_tcb *old_tls __unused)
183 {
184 	return (0);
185 }
186 
187 void
188 __libc_free_tls(struct tls_tcb *tcb __unused)
189 {
190 }
191 
192 #endif /* PIC */
193 
194 extern char **environ;
195 
196 void
197 _init_tls()
198 {
199 #ifndef PIC
200 	Elf_Addr *sp;
201 	Elf_Auxinfo *aux, *auxp;
202 	Elf_Phdr *phdr;
203 	size_t phent, phnum;
204 	int i;
205 	struct tls_tcb *tcb;
206 
207 	sp = (Elf_Addr *) environ;
208 	while (*sp++ != 0)
209 		;
210 	aux = (Elf_Auxinfo *) sp;
211 	phdr = 0;
212 	phent = phnum = 0;
213 	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
214 		switch (auxp->a_type) {
215 		case AT_PHDR:
216 			phdr = auxp->a_un.a_ptr;
217 			break;
218 
219 		case AT_PHENT:
220 			phent = auxp->a_un.a_val;
221 			break;
222 
223 		case AT_PHNUM:
224 			phnum = auxp->a_un.a_val;
225 			break;
226 		}
227 	}
228 	if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0)
229 		return;
230 
231 	for (i = 0; (unsigned)i < phnum; i++) {
232 		if (phdr[i].p_type == PT_TLS) {
233 			tls_static_space = round(phdr[i].p_memsz,
234 			    phdr[i].p_align);
235 			tls_init_size = phdr[i].p_filesz;
236 			tls_init = (void*) phdr[i].p_vaddr;
237 		}
238 	}
239 
240 	tcb = _rtld_allocate_tls(NULL);
241 	tls_set_tcb(tcb);
242 #endif
243 }
244