tls.c revision 2043:cce0c9bf5c8d
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 2006 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
39static ulong_t	tls_static_size = 0;	/* static TLS buffer size */
40
41#define	TLSBLOCKCNT	16	/* number of blocks of tmi_bits to allocate */
42				/* at a time. */
43typedef struct {
44	uint_t	*tmi_bits;
45	ulong_t	tmi_lowfree;
46	ulong_t	tmi_cnt;
47} Tlsmodid;
48
49static Tlsmodid	tmid = {0, 0, 0};
50
51ulong_t
52tls_getmodid()
53{
54	ulong_t		ndx;
55	ulong_t		i;
56
57	if (tmid.tmi_bits == 0) {
58		if ((tmid.tmi_bits = calloc(TLSBLOCKCNT, sizeof (uint_t))) == 0)
59			return ((ulong_t)-1);
60		tmid.tmi_bits[0] = 1;
61		tmid.tmi_lowfree = 1;
62		tmid.tmi_cnt = TLSBLOCKCNT;
63		return (0);
64	}
65
66	for (i = tmid.tmi_lowfree / (sizeof (uint_t) * 8);
67	    i < tmid.tmi_cnt; i++) {
68		uint_t	j;
69		/*
70		 * If all bits are assigned - move on.
71		 */
72		if ((tmid.tmi_bits[i] ^ ~((uint_t)0)) == 0)
73			continue;
74		for (ndx = 0, j = 1; j; j = j << 1, ndx++) {
75			if ((tmid.tmi_bits[i] & j) == 0) {
76				tmid.tmi_bits[i] |= j;
77				ndx = (i * (sizeof (uint_t)) * 8) + ndx;
78				tmid.tmi_lowfree = ndx + 1;
79				return (ndx);
80			}
81		}
82	}
83
84	/*
85	 * All bits taken - must allocate a new block
86	 */
87	if ((tmid.tmi_bits = realloc(tmid.tmi_bits,
88	    ((tmid.tmi_cnt * sizeof (uint_t)) +
89	    (TLSBLOCKCNT * sizeof (uint_t))))) == 0)
90		return ((ulong_t)-1);
91
92	/*
93	 * Clear out the tail of the new allocation.
94	 */
95	bzero(&(tmid.tmi_bits[tmid.tmi_cnt]), TLSBLOCKCNT * sizeof (uint_t));
96	tmid.tmi_bits[tmid.tmi_cnt] = 1;
97	ndx = (tmid.tmi_cnt * sizeof (uint_t)) * 8;
98	tmid.tmi_lowfree = ndx + 1;
99	tmid.tmi_cnt += TLSBLOCKCNT;
100
101	return (ndx);
102}
103
104void
105tls_freemodid(ulong_t modid)
106{
107	ulong_t	i;
108	uint_t	j;
109
110	i = modid / (sizeof (uint_t) * 8);
111	/* LINTED */
112	j = modid % (sizeof (uint_t) * 8);
113	j = ~(1 << j);
114	tmid.tmi_bits[i] &= j;
115	if (modid < tmid.tmi_lowfree)
116		tmid.tmi_lowfree = modid;
117}
118
119void
120tls_modaddrem(Rt_map *lmp, uint_t flag)
121{
122	Lm_list		*lml = LIST(lmp);
123	TLS_modinfo	tmi;
124	Phdr		*tlsphdr;
125	void		(*fptr)(TLS_modinfo *);
126
127	if (flag & TM_FLG_MODADD) {
128		fptr = (void (*)())lml->lm_lcs[CI_TLS_MODADD].lc_un.lc_func;
129	} else if (FLAGS1(lmp) & FL1_RT_TLSADD) {
130		fptr = (void (*)())lml->lm_lcs[CI_TLS_MODREM].lc_un.lc_func;
131	} else {
132		return;
133	}
134
135	tlsphdr = PTTLS(lmp);
136
137	bzero(&tmi, sizeof (tmi));
138	tmi.tm_modname = PATHNAME(lmp);
139	tmi.tm_modid = TLSMODID(lmp);
140	tmi.tm_tlsblock = (void *)(tlsphdr->p_vaddr);
141
142	if (!(FLAGS(lmp) & FLG_RT_FIXED))
143		tmi.tm_tlsblock = (void *)((uintptr_t)tmi.tm_tlsblock +
144			ADDR(lmp));
145
146	tmi.tm_filesz = tlsphdr->p_filesz;
147	tmi.tm_memsz = tlsphdr->p_memsz;
148	tmi.tm_flags = 0;
149	tmi.tm_stattlsoffset = 0;
150
151	DBG_CALL(Dbg_tls_modactivity(LIST(lmp), &tmi, flag));
152	(*fptr)(&tmi);
153
154	/*
155	 * Tag that this link-map has registered its TLS, and free up the
156	 * moduleid
157	 */
158	FLAGS1(lmp) |= FL1_RT_TLSADD;
159
160	if (flag & TM_FLG_MODREM)
161		tls_freemodid(TLSMODID(lmp));
162}
163
164void
165tls_assign_soffset(Rt_map *lmp)
166{
167	/*
168	 * Only objects on the primary link-map list are associated
169	 * with the STATIC tls block.
170	 */
171	if (LIST(lmp)->lm_flags & LML_FLG_BASELM) {
172		tls_static_size += S_ROUND(PTTLS(lmp)->p_memsz, M_TLSSTATALIGN);
173		TLSSTATOFF(lmp) = tls_static_size;
174	}
175
176	/*
177	 * Everyone get's a dynamic TLS modid.
178	 */
179	TLSMODID(lmp) = tls_getmodid();
180}
181
182int
183tls_statmod(Lm_list *lml, Rt_map *lmp)
184{
185	uint_t		tlsmodndx, tlsmodcnt = lml->lm_tls;
186	TLS_modinfo	**tlsmodlist, *tlsbuflist;
187	Phdr		*tlsphdr;
188	void		(*fptr)(TLS_modinfo **, ulong_t);
189
190	fptr = (void (*)())lml->lm_lcs[CI_TLS_STATMOD].lc_un.lc_func;
191
192	/*
193	 * If we don't have any TLS modules - report that and return.
194	 */
195	if (tlsmodcnt == 0) {
196		if (fptr)
197			(*fptr)(0, 0);
198		return (1);
199	}
200	lml->lm_tls = 0;
201
202	/*
203	 * Allocate a buffer to report the TLS modules, the buffer consists of:
204	 *
205	 *	TLS_modinfo *	ptrs[tlsmodcnt + 1]
206	 *	TLS_modinfo	bufs[tlsmodcnt]
207	 *
208	 * The ptrs are initialized to the bufs - except the last
209	 * one which null terminates the array.
210	 */
211	if ((tlsmodlist = calloc((sizeof (TLS_modinfo *) * (tlsmodcnt + 1)) +
212	    (sizeof (TLS_modinfo) * tlsmodcnt), 1)) == 0)
213		return (0);
214
215	tlsbuflist = (TLS_modinfo *)((uintptr_t)tlsmodlist +
216	    ((tlsmodcnt + 1) * sizeof (TLS_modinfo *)));
217
218	for (tlsmodndx = 0; tlsmodndx < tlsmodcnt; tlsmodndx++)
219		tlsmodlist[tlsmodndx] = &tlsbuflist[tlsmodndx];
220
221	/*
222	 * Account for the initial dtv ptr in the TLSSIZE calculation.
223	 */
224	tlsmodndx = 0;
225	for (lmp = lml->lm_head; lmp; lmp = (Rt_map *)NEXT(lmp)) {
226		if ((FCT(lmp) != &elf_fct) ||
227		    (PTTLS(lmp) == 0) || (PTTLS(lmp)->p_memsz == 0))
228			continue;
229
230		tlsphdr = PTTLS(lmp);
231
232		tlsmodlist[tlsmodndx]->tm_modname = PATHNAME(lmp);
233		tlsmodlist[tlsmodndx]->tm_modid = TLSMODID(lmp);
234		tlsmodlist[tlsmodndx]->tm_tlsblock = (void *)(tlsphdr->p_vaddr);
235
236		if (!(FLAGS(lmp) & FLG_RT_FIXED)) {
237			tlsmodlist[tlsmodndx]->tm_tlsblock = (void *)
238			    ((uintptr_t)tlsmodlist[tlsmodndx]->tm_tlsblock +
239			    ADDR(lmp));
240		}
241		tlsmodlist[tlsmodndx]->tm_filesz = tlsphdr->p_filesz;
242		tlsmodlist[tlsmodndx]->tm_memsz = tlsphdr->p_memsz;
243		tlsmodlist[tlsmodndx]->tm_flags = TM_FLG_STATICTLS;
244		tlsmodlist[tlsmodndx]->tm_stattlsoffset = TLSSTATOFF(lmp);
245		tlsmodndx++;
246	}
247
248	DBG_CALL(Dbg_tls_static_block(&lml_main, (void *)tlsmodlist,
249	    tls_static_size));
250	(*fptr)(tlsmodlist, tls_static_size);
251
252	/*
253	 * We're done with the list - clean it up.
254	 */
255	free(tlsmodlist);
256	return (1);
257}
258