xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/tls.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 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 <msg.h>
37 #include <debug.h>
38 
39 #define	TLSBLOCKCNT	16	/* number of blocks of tmi_bits to allocate */
40 				/* at a time. */
41 typedef struct {
42 	uint_t	*tmi_bits;
43 	ulong_t	tmi_lowfree;
44 	ulong_t	tmi_cnt;
45 } Tlsmodid;
46 
47 static Tlsmodid	tmid = {0, 0, 0};
48 
49 static ulong_t
50 tls_getmodid()
51 {
52 	ulong_t	ndx, cnt;
53 
54 	if (tmid.tmi_bits == 0) {
55 		if ((tmid.tmi_bits = calloc(TLSBLOCKCNT, sizeof (uint_t))) == 0)
56 			return ((ulong_t)-1);
57 		tmid.tmi_bits[0] = 1;
58 		tmid.tmi_lowfree = 1;
59 		tmid.tmi_cnt = TLSBLOCKCNT;
60 		return (0);
61 	}
62 
63 	for (cnt = tmid.tmi_lowfree / (sizeof (uint_t) * 8);
64 	    cnt < tmid.tmi_cnt; cnt++) {
65 		uint_t	bits;
66 
67 		/*
68 		 * If all bits are assigned - move on.
69 		 */
70 		if ((tmid.tmi_bits[cnt] ^ ~((uint_t)0)) == 0)
71 			continue;
72 
73 		for (ndx = 0, bits = 1; bits; bits = bits << 1, ndx++) {
74 			if ((tmid.tmi_bits[cnt] & bits) == 0) {
75 				tmid.tmi_bits[cnt] |= bits;
76 				ndx = (cnt * (sizeof (uint_t)) * 8) + ndx;
77 				tmid.tmi_lowfree = ndx + 1;
78 				return (ndx);
79 			}
80 		}
81 	}
82 
83 	/*
84 	 * All bits taken - must allocate a new block
85 	 */
86 	if ((tmid.tmi_bits = realloc(tmid.tmi_bits,
87 	    ((tmid.tmi_cnt * sizeof (uint_t)) +
88 	    (TLSBLOCKCNT * sizeof (uint_t))))) == 0)
89 		return ((ulong_t)-1);
90 
91 	/*
92 	 * Clear out the tail of the new allocation.
93 	 */
94 	bzero(&(tmid.tmi_bits[tmid.tmi_cnt]), TLSBLOCKCNT * sizeof (uint_t));
95 	tmid.tmi_bits[tmid.tmi_cnt] = 1;
96 	ndx = (tmid.tmi_cnt * sizeof (uint_t)) * 8;
97 	tmid.tmi_lowfree = ndx + 1;
98 	tmid.tmi_cnt += TLSBLOCKCNT;
99 
100 	return (ndx);
101 }
102 
103 void
104 tls_freemodid(ulong_t modid)
105 {
106 	ulong_t	i;
107 	uint_t	j;
108 
109 	i = modid / (sizeof (uint_t) * 8);
110 	/* LINTED */
111 	j = modid % (sizeof (uint_t) * 8);
112 	j = ~(1 << j);
113 	tmid.tmi_bits[i] &= j;
114 	if (modid < tmid.tmi_lowfree)
115 		tmid.tmi_lowfree = modid;
116 }
117 
118 void
119 tls_modaddrem(Rt_map *lmp, uint_t flag)
120 {
121 	Lm_list		*lml = LIST(lmp);
122 	TLS_modinfo	tmi;
123 	Phdr		*tlsphdr;
124 	void		(*fptr)(TLS_modinfo *);
125 
126 	if (flag & TM_FLG_MODADD) {
127 		fptr = (void (*)())lml->lm_lcs[CI_TLS_MODADD].lc_un.lc_func;
128 	} else if (FLAGS1(lmp) & FL1_RT_TLSADD) {
129 		fptr = (void (*)())lml->lm_lcs[CI_TLS_MODREM].lc_un.lc_func;
130 	} else {
131 		return;
132 	}
133 
134 	tlsphdr = PTTLS(lmp);
135 
136 	bzero(&tmi, sizeof (tmi));
137 	tmi.tm_modname = PATHNAME(lmp);
138 	tmi.tm_modid = TLSMODID(lmp);
139 	tmi.tm_tlsblock = (void *)(tlsphdr->p_vaddr);
140 
141 	if (!(FLAGS(lmp) & FLG_RT_FIXED))
142 		tmi.tm_tlsblock = (void *)((uintptr_t)tmi.tm_tlsblock +
143 		    ADDR(lmp));
144 
145 	tmi.tm_filesz = tlsphdr->p_filesz;
146 	tmi.tm_memsz = tlsphdr->p_memsz;
147 	tmi.tm_flags = 0;
148 	tmi.tm_stattlsoffset = 0;
149 
150 	DBG_CALL(Dbg_tls_modactivity(LIST(lmp), &tmi, flag));
151 	(*fptr)(&tmi);
152 
153 	/*
154 	 * Tag that this link-map has registered its TLS, and, if this object
155 	 * is being removed, free up the module id.
156 	 */
157 	FLAGS1(lmp) |= FL1_RT_TLSADD;
158 
159 	if (flag & TM_FLG_MODREM)
160 		tls_freemodid(TLSMODID(lmp));
161 }
162 
163 static ulong_t	tls_static_size = 0;	/* static TLS buffer size */
164 static ulong_t	tls_static_resv = 512;	/* (extra) static TLS reservation */
165 
166 /*
167  * Track any static TLS use, retain the TLS header, and assign a TLS module
168  * identifier.
169  */
170 int
171 tls_assign(Lm_list *lml, Rt_map *lmp, Phdr *phdr)
172 {
173 	ulong_t	memsz = S_ROUND(phdr->p_memsz, M_TLSSTATALIGN);
174 	ulong_t	filesz = phdr->p_filesz;
175 	ulong_t	resv = tls_static_resv;
176 
177 	/*
178 	 * If this object explicitly references static TLS, then there are some
179 	 * limitations.
180 	 */
181 	if (FLAGS1(lmp) & FL1_RT_TLSSTAT) {
182 		/*
183 		 * Static TLS is only available to objects on the primary
184 		 * link-map list.
185 		 */
186 		if (((lml->lm_flags & LML_FLG_BASELM) == 0) ||
187 		    ((rtld_flags2 & RT_FL2_NOPLM) != 0)) {
188 			eprintf(lml, ERR_FATAL, MSG_INTL(MSG_TLS_STATBASE),
189 			    NAME(lmp));
190 			return (0);
191 		}
192 
193 		/*
194 		 * All TLS blocks that are processed before thread
195 		 * initialization, are registered with libc.  This
196 		 * initialization is carried out through a handshake with libc
197 		 * prior to executing any user code (ie. before the first .init
198 		 * sections are called).  As part of this initialization, a
199 		 * small backup TLS reservation is added (tls_static_resv).
200 		 * Only explicit static TLS references that can be satisfied by
201 		 * this TLS backup reservation can be satisfied.
202 		 */
203 		if (rtld_flags2 & RT_FL2_PLMSETUP) {
204 			/*
205 			 * Initialized static TLS can not be satisfied from the
206 			 * TLS backup reservation.
207 			 */
208 			if (filesz) {
209 				eprintf(lml, ERR_FATAL,
210 				    MSG_INTL(MSG_TLS_STATINIT), NAME(lmp));
211 				return (0);
212 			}
213 
214 			/*
215 			 * Make sure the backup reservation is sufficient.
216 			 */
217 			if (memsz > tls_static_resv) {
218 				eprintf(lml, ERR_FATAL,
219 				    MSG_INTL(MSG_TLS_STATSIZE), NAME(lmp),
220 				    EC_XWORD(memsz), EC_XWORD(tls_static_resv));
221 				return (0);
222 			}
223 
224 			tls_static_resv -= memsz;
225 		}
226 	}
227 
228 	/*
229 	 * If we haven't yet initialized threads, or this static reservation can
230 	 * be satisfied from the TLS backup reservation, determine the total
231 	 * static TLS size, and assign this object a static TLS offset.
232 	 */
233 	if (((rtld_flags2 & RT_FL2_PLMSETUP) == 0) ||
234 	    (FLAGS1(lmp) & FL1_RT_TLSSTAT)) {
235 		tls_static_size += memsz;
236 		TLSSTATOFF(lmp) = tls_static_size;
237 	}
238 
239 	/*
240 	 * Retain the PT_TLS header, obtain a new module identifier, and
241 	 * indicate that this link-map list contains a new TLS object.
242 	 */
243 	PTTLS(lmp) = phdr;
244 	TLSMODID(lmp) = tls_getmodid();
245 
246 	/*
247 	 * Now that we have a TLS module id, generate any static TLS reservation
248 	 * diagnostic.
249 	 */
250 	if (resv != tls_static_resv)
251 		DBG_CALL(Dbg_tls_static_resv(lmp, memsz, tls_static_resv));
252 
253 	return (++lml->lm_tls);
254 }
255 
256 int
257 tls_statmod(Lm_list *lml, Rt_map *lmp)
258 {
259 	uint_t		tlsmodndx, tlsmodcnt = lml->lm_tls;
260 	TLS_modinfo	**tlsmodlist, *tlsbuflist;
261 	Phdr		*tlsphdr;
262 	void		(*fptr)(TLS_modinfo **, ulong_t);
263 
264 	fptr = (void (*)())lml->lm_lcs[CI_TLS_STATMOD].lc_un.lc_func;
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 one which
273 	 * null terminates the array.
274 	 *
275 	 * Note, even if no TLS has yet been observed, we still supply a
276 	 * TLS buffer with a single null entry.  This allows us to initialize
277 	 * the backup TLS reservation.
278 	 */
279 	if ((tlsmodlist = calloc((sizeof (TLS_modinfo *) * (tlsmodcnt + 1)) +
280 	    (sizeof (TLS_modinfo) * tlsmodcnt), 1)) == 0)
281 		return (0);
282 
283 	lml->lm_tls = 0;
284 
285 	/*
286 	 * If we don't have any TLS modules - report that and return.
287 	 */
288 	if (tlsmodcnt == 0) {
289 		if (fptr)
290 			(*fptr)(tlsmodlist, tls_static_resv);
291 		DBG_CALL(Dbg_tls_static_block(&lml_main, 0, 0,
292 		    tls_static_resv));
293 		return (1);
294 	}
295 
296 	/*
297 	 * Initialize the TLS buffer.
298 	 */
299 	tlsbuflist = (TLS_modinfo *)((uintptr_t)tlsmodlist +
300 	    ((tlsmodcnt + 1) * sizeof (TLS_modinfo *)));
301 
302 	for (tlsmodndx = 0; tlsmodndx < tlsmodcnt; tlsmodndx++)
303 		tlsmodlist[tlsmodndx] = &tlsbuflist[tlsmodndx];
304 
305 	/*
306 	 * Account for the initial dtv ptr in the TLSSIZE calculation.
307 	 */
308 	tlsmodndx = 0;
309 	for (lmp = lml->lm_head; lmp; lmp = (Rt_map *)NEXT(lmp)) {
310 		if ((FCT(lmp) != &elf_fct) ||
311 		    (PTTLS(lmp) == 0) || (PTTLS(lmp)->p_memsz == 0))
312 			continue;
313 
314 		tlsphdr = PTTLS(lmp);
315 
316 		tlsmodlist[tlsmodndx]->tm_modname = PATHNAME(lmp);
317 		tlsmodlist[tlsmodndx]->tm_modid = TLSMODID(lmp);
318 		tlsmodlist[tlsmodndx]->tm_tlsblock = (void *)(tlsphdr->p_vaddr);
319 
320 		if (!(FLAGS(lmp) & FLG_RT_FIXED)) {
321 			tlsmodlist[tlsmodndx]->tm_tlsblock = (void *)
322 			    ((uintptr_t)tlsmodlist[tlsmodndx]->tm_tlsblock +
323 			    ADDR(lmp));
324 		}
325 		tlsmodlist[tlsmodndx]->tm_filesz = tlsphdr->p_filesz;
326 		tlsmodlist[tlsmodndx]->tm_memsz = tlsphdr->p_memsz;
327 		tlsmodlist[tlsmodndx]->tm_flags = TM_FLG_STATICTLS;
328 		tlsmodlist[tlsmodndx]->tm_stattlsoffset = TLSSTATOFF(lmp);
329 		tlsmodndx++;
330 	}
331 
332 	DBG_CALL(Dbg_tls_static_block(&lml_main, (void *)tlsmodlist,
333 	    tls_static_size, tls_static_resv));
334 	(*fptr)(tlsmodlist, (tls_static_size + tls_static_resv));
335 
336 	/*
337 	 * We're done with the list - clean it up.
338 	 */
339 	free(tlsmodlist);
340 	return (1);
341 }
342