xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/tls.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  *	Copyright 2001-2003 Sun Microsystems, Inc.  All rights reserved.
24  *	Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 
29 #include <stdio.h>
30 #include <strings.h>
31 #include <sys/types.h>
32 #include <dlfcn.h>
33 #include <libc_int.h>
34 #include <_rtld.h>
35 #include <_elf.h>
36 #include <conv.h>
37 #include <msg.h>
38 #include <debug.h>
39 
40 
41 static void (*	fptr_tls_modadd)(TLS_modinfo *) = 0;
42 static void (*	fptr_tls_modrem)(TLS_modinfo *) = 0;
43 static void (*	fptr_tls_statmods)(TLS_modinfo **, unsigned long) = 0;
44 
45 static int	tlsinitialized = 0;
46 static unsigned long tls_static_size = 0;	/* static TLS buffer size */
47 
48 #define	TLSBLOCKCNT	16	/* number of blocks of tmi_bits to allocate */
49 				/* at a time. */
50 typedef struct {
51 	uint_t	*tmi_bits;
52 	ulong_t	tmi_lowfree;
53 	ulong_t	tmi_cnt;
54 } Tlsmodid;
55 
56 static Tlsmodid	tmid = {0, 0, 0};
57 
58 unsigned long
59 tls_getmodid()
60 {
61 	ulong_t		ndx;
62 	ulong_t		i;
63 
64 	if (tmid.tmi_bits == 0) {
65 		if ((tmid.tmi_bits =
66 		    (uint_t *)calloc(TLSBLOCKCNT, sizeof (uint_t))) == 0)
67 			return ((unsigned long)-1);
68 		tmid.tmi_bits[0] = 1;
69 		tmid.tmi_lowfree = 1;
70 		tmid.tmi_cnt = TLSBLOCKCNT;
71 		return (0);
72 	}
73 
74 	for (i = tmid.tmi_lowfree / (sizeof (uint_t) * 8);
75 	    i < tmid.tmi_cnt; i++) {
76 		uint_t	j;
77 		/*
78 		 * If all bits are assigned - move on.
79 		 */
80 		if ((tmid.tmi_bits[i] ^ ~((uint_t)0)) == 0)
81 			continue;
82 		for (ndx = 0, j = 1; j; j = j << 1, ndx++) {
83 			if ((tmid.tmi_bits[i] & j) == 0) {
84 				tmid.tmi_bits[i] |= j;
85 				ndx = (i * (sizeof (uint_t)) * 8) + ndx;
86 				tmid.tmi_lowfree = ndx + 1;
87 				return (ndx);
88 			}
89 		}
90 	}
91 
92 	/*
93 	 * All bits taken - must allocate a new block
94 	 */
95 	if ((tmid.tmi_bits = (uint_t *)realloc(tmid.tmi_bits,
96 	    ((tmid.tmi_cnt * sizeof (uint_t)) +
97 	    (TLSBLOCKCNT * sizeof (uint_t))))) == 0)
98 		return ((unsigned long)-1);
99 	/*
100 	 * clear out the tail of the new allocation
101 	 */
102 	bzero(&(tmid.tmi_bits[tmid.tmi_cnt]), TLSBLOCKCNT * sizeof (uint_t));
103 	tmid.tmi_bits[tmid.tmi_cnt] = 1;
104 	ndx = (tmid.tmi_cnt * sizeof (uint_t)) * 8;
105 	tmid.tmi_lowfree = ndx + 1;
106 	tmid.tmi_cnt += TLSBLOCKCNT;
107 
108 	return (ndx);
109 }
110 
111 
112 void
113 tls_freemodid(unsigned long modid)
114 {
115 	ulong_t	i;
116 	uint_t	j;
117 
118 	i = modid / (sizeof (uint_t) * 8);
119 	/* LINTED */
120 	j = modid % (sizeof (uint_t) * 8);
121 	j = ~(1 << j);
122 	tmid.tmi_bits[i] &= j;
123 	if (modid < tmid.tmi_lowfree)
124 		tmid.tmi_lowfree = modid;
125 }
126 
127 
128 void
129 tls_setroutines(Lm_list *lml, void * modadd, void * modrem, void * statmod)
130 {
131 	/*
132 	 * If a version of libc/libthread gives us only a subset
133 	 * of the TLS interfaces - it's confused and we discard
134 	 * the whole lot.
135 	 */
136 	if (!modadd || !modrem || !statmod)
137 		return;
138 
139 	if ((fptr_tls_modadd == 0) || (lml->lm_flags & LML_FLG_BASELM))
140 		fptr_tls_modadd = (void(*)(TLS_modinfo *)) modadd;
141 	if ((fptr_tls_modrem == 0) || (lml->lm_flags & LML_FLG_BASELM))
142 		fptr_tls_modrem = (void(*)(TLS_modinfo *)) modrem;
143 	/*
144 	 * The 'statmods' interface is only relevent for the
145 	 * primary link-map - ignore all other instances.
146 	 */
147 	if (lml->lm_flags & LML_FLG_BASELM)
148 		fptr_tls_statmods =
149 			(void(*)(TLS_modinfo **, unsigned long)) statmod;
150 }
151 
152 
153 void
154 tls_modactivity(Rt_map * lmp, uint_t flag)
155 {
156 	TLS_modinfo	tmi;
157 	Phdr *		tlsphdr;
158 	void (*		fptr)(TLS_modinfo *);
159 
160 	if (flag & TM_FLG_MODADD)
161 		fptr = fptr_tls_modadd;
162 	else
163 		fptr = fptr_tls_modrem;
164 
165 	/*
166 	 * We only report TLS modactivity for the primary link-map
167 	 * after all the objects have been loaded and we've reported
168 	 * the STATIC tls modlist (see tls_report_modules()).
169 	 */
170 	if (((tlsinitialized == 0) &&
171 	    (LIST(lmp)->lm_flags & LML_FLG_BASELM)) ||
172 	    (fptr == 0) || (lmp == 0) || (FCT(lmp) != &elf_fct) ||
173 	    (PTTLS(lmp) == 0))
174 		return;
175 
176 	tlsphdr = PTTLS(lmp);
177 
178 	bzero(&tmi, sizeof (tmi));
179 	tmi.tm_modname = PATHNAME(lmp);
180 	tmi.tm_modid = TLSMODID(lmp);
181 	tmi.tm_tlsblock = (void *)(tlsphdr->p_vaddr);
182 	if (!(FLAGS(lmp) & FLG_RT_FIXED))
183 		tmi.tm_tlsblock = (void *)((uintptr_t)tmi.tm_tlsblock +
184 			ADDR(lmp));
185 	tmi.tm_filesz = tlsphdr->p_filesz;
186 	tmi.tm_memsz = tlsphdr->p_memsz;
187 	tmi.tm_flags = 0;
188 	tmi.tm_stattlsoffset = 0;
189 
190 	DBG_CALL(Dbg_tls_modactivity(&tmi, flag));
191 	fptr(&tmi);
192 
193 	/*
194 	 * Free up the moduleid
195 	 */
196 	if (flag & TM_FLG_MODREM)
197 		tls_freemodid(TLSMODID(lmp));
198 }
199 
200 
201 void
202 tls_assign_soffset(Rt_map * lmp)
203 {
204 	if (PTTLS(lmp) == 0)
205 		return;
206 
207 	/*
208 	 * Only objects on the primary link-map list are associated
209 	 * with the STATIC tls block.
210 	 */
211 	if (LIST(lmp)->lm_flags & LML_FLG_BASELM) {
212 		tls_static_size += S_ROUND(PTTLS(lmp)->p_memsz, M_TLSSTATALIGN);
213 		TLSSTATOFF(lmp) = tls_static_size;
214 	}
215 
216 	/*
217 	 * Everyone get's a dynamic TLS modid
218 	 */
219 	TLSMODID(lmp) = tls_getmodid();
220 }
221 
222 int
223 tls_report_modules()
224 {
225 	Rt_map *	lmp;
226 	uint_t		tlsmodcnt;
227 	uint_t		tlsmodndx;
228 	TLS_modinfo **	tlsmodlist;
229 	TLS_modinfo *	tlsbuflist;
230 	Phdr *		tlsphdr;
231 
232 	tlsinitialized++;
233 	/*
234 	 * Scan through all objects to determine how many have TLS
235 	 * storage.
236 	 */
237 	tlsmodcnt = 0;
238 	for (lmp = lml_main.lm_head; lmp; lmp = (Rt_map *)NEXT(lmp)) {
239 		if ((FCT(lmp) != &elf_fct) ||
240 		    (PTTLS(lmp) == 0) || (PTTLS(lmp)->p_memsz == 0))
241 			continue;
242 		tlsmodcnt++;
243 
244 		if (fptr_tls_statmods)
245 			continue;
246 
247 		/*
248 		 * If a module has TLS - but the TLS interfaces
249 		 * are not present (no libthread?). Then this is
250 		 * a fatal condition.
251 		 */
252 		eprintf(ERR_FATAL, MSG_INTL(MSG_ERR_TLS_NOTLS),
253 		    NAME(lmp));
254 		return (0);
255 	}
256 
257 	/*
258 	 * If we don't have any TLS modules - report that and return.
259 	 */
260 	if (tlsmodcnt == 0) {
261 		if (fptr_tls_statmods != 0)
262 			fptr_tls_statmods(0, 0);
263 		return (1);
264 	}
265 
266 	/*
267 	 * Allocate a buffer to report the TLS modules, the buffer consists of:
268 	 *
269 	 *	TLS_modinfo *	ptrs[tlsmodcnt + 1]
270 	 *	TLS_modinfo	bufs[tlsmodcnt]
271 	 *
272 	 * The ptrs are initialized to the bufs - except the last
273 	 * one which null terminates the array.
274 	 */
275 	if ((tlsmodlist = calloc((sizeof (TLS_modinfo *) * tlsmodcnt + 1) +
276 	    (sizeof (TLS_modinfo) * tlsmodcnt), 1)) == 0)
277 		return (0);
278 
279 	tlsbuflist = (TLS_modinfo *)((uintptr_t)tlsmodlist +
280 		((tlsmodcnt + 1) * sizeof (TLS_modinfo *)));
281 	for (tlsmodndx = 0; tlsmodndx < tlsmodcnt; tlsmodndx++)
282 		tlsmodlist[tlsmodndx] = &tlsbuflist[tlsmodndx];
283 
284 	/*
285 	 * Account for the initial dtv ptr in the TLSSIZE calculation.
286 	 */
287 	tlsmodndx = 0;
288 	for (lmp = lml_main.lm_head; lmp; lmp = (Rt_map *)NEXT(lmp)) {
289 		if ((FCT(lmp) != &elf_fct) ||
290 		    (PTTLS(lmp) == 0) || (PTTLS(lmp)->p_memsz == 0))
291 			continue;
292 
293 		tlsphdr = PTTLS(lmp);
294 
295 		tlsmodlist[tlsmodndx]->tm_modname = PATHNAME(lmp);
296 		tlsmodlist[tlsmodndx]->tm_modid = TLSMODID(lmp);
297 		tlsmodlist[tlsmodndx]->tm_tlsblock =
298 			(void *)(tlsphdr->p_vaddr);
299 		if (!(FLAGS(lmp) & FLG_RT_FIXED))
300 			tlsmodlist[tlsmodndx]->tm_tlsblock =
301 				(void *)((uintptr_t)tlsmodlist[
302 				tlsmodndx]->tm_tlsblock + ADDR(lmp));
303 		tlsmodlist[tlsmodndx]->tm_filesz = tlsphdr->p_filesz;
304 		tlsmodlist[tlsmodndx]->tm_memsz = tlsphdr->p_memsz;
305 		tlsmodlist[tlsmodndx]->tm_flags = TM_FLG_STATICTLS;
306 		tlsmodlist[tlsmodndx]->tm_stattlsoffset =
307 			TLSSTATOFF(lmp);
308 		tlsmodndx++;
309 	}
310 
311 	DBG_CALL(Dbg_tls_static_block((void *)tlsmodlist, tls_static_size));
312 	fptr_tls_statmods(tlsmodlist, tls_static_size);
313 
314 	/*
315 	 * We're done with the list - clean it up.
316 	 */
317 	free(tlsmodlist);
318 	return (1);
319 }
320