tls.c revision 6229:d95cc752da23
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. */
41typedef struct {
42	uint_t	*tmi_bits;
43	ulong_t	tmi_lowfree;
44	ulong_t	tmi_cnt;
45} Tlsmodid;
46
47static Tlsmodid	tmid = {0, 0, 0};
48
49static ulong_t
50tls_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
103void
104tls_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
118void
119tls_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
163static ulong_t	tls_static_size = 0;	/* static TLS buffer size */
164static 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 */
170int
171tls_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
256int
257tls_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