xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/tls.c (revision 10a4fa49f51ed9ae1c857a626de6ce9ebf41661a)
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 
39 static 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. */
43 typedef struct {
44 	uint_t	*tmi_bits;
45 	ulong_t	tmi_lowfree;
46 	ulong_t	tmi_cnt;
47 } Tlsmodid;
48 
49 static Tlsmodid	tmid = {0, 0, 0};
50 
51 ulong_t
52 tls_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 
104 void
105 tls_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 
119 void
120 tls_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 
164 void
165 tls_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 	    ((rtld_flags2 & RT_FL2_PLMSETUP) == 0)) {
173 		tls_static_size += S_ROUND(PTTLS(lmp)->p_memsz, M_TLSSTATALIGN);
174 		TLSSTATOFF(lmp) = tls_static_size;
175 	}
176 
177 	/*
178 	 * Everyone get's a dynamic TLS modid.
179 	 */
180 	TLSMODID(lmp) = tls_getmodid();
181 }
182 
183 int
184 tls_statmod(Lm_list *lml, Rt_map *lmp)
185 {
186 	uint_t		tlsmodndx, tlsmodcnt = lml->lm_tls;
187 	TLS_modinfo	**tlsmodlist, *tlsbuflist;
188 	Phdr		*tlsphdr;
189 	void		(*fptr)(TLS_modinfo **, ulong_t);
190 
191 	fptr = (void (*)())lml->lm_lcs[CI_TLS_STATMOD].lc_un.lc_func;
192 
193 	/*
194 	 * If we don't have any TLS modules - report that and return.
195 	 */
196 	if (tlsmodcnt == 0) {
197 		if (fptr)
198 			(*fptr)(0, 0);
199 		return (1);
200 	}
201 	lml->lm_tls = 0;
202 
203 	/*
204 	 * Allocate a buffer to report the TLS modules, the buffer consists of:
205 	 *
206 	 *	TLS_modinfo *	ptrs[tlsmodcnt + 1]
207 	 *	TLS_modinfo	bufs[tlsmodcnt]
208 	 *
209 	 * The ptrs are initialized to the bufs - except the last
210 	 * one which null terminates the array.
211 	 */
212 	if ((tlsmodlist = calloc((sizeof (TLS_modinfo *) * (tlsmodcnt + 1)) +
213 	    (sizeof (TLS_modinfo) * tlsmodcnt), 1)) == 0)
214 		return (0);
215 
216 	tlsbuflist = (TLS_modinfo *)((uintptr_t)tlsmodlist +
217 	    ((tlsmodcnt + 1) * sizeof (TLS_modinfo *)));
218 
219 	for (tlsmodndx = 0; tlsmodndx < tlsmodcnt; tlsmodndx++)
220 		tlsmodlist[tlsmodndx] = &tlsbuflist[tlsmodndx];
221 
222 	/*
223 	 * Account for the initial dtv ptr in the TLSSIZE calculation.
224 	 */
225 	tlsmodndx = 0;
226 	for (lmp = lml->lm_head; lmp; lmp = (Rt_map *)NEXT(lmp)) {
227 		if ((FCT(lmp) != &elf_fct) ||
228 		    (PTTLS(lmp) == 0) || (PTTLS(lmp)->p_memsz == 0))
229 			continue;
230 
231 		tlsphdr = PTTLS(lmp);
232 
233 		tlsmodlist[tlsmodndx]->tm_modname = PATHNAME(lmp);
234 		tlsmodlist[tlsmodndx]->tm_modid = TLSMODID(lmp);
235 		tlsmodlist[tlsmodndx]->tm_tlsblock = (void *)(tlsphdr->p_vaddr);
236 
237 		if (!(FLAGS(lmp) & FLG_RT_FIXED)) {
238 			tlsmodlist[tlsmodndx]->tm_tlsblock = (void *)
239 			    ((uintptr_t)tlsmodlist[tlsmodndx]->tm_tlsblock +
240 			    ADDR(lmp));
241 		}
242 		tlsmodlist[tlsmodndx]->tm_filesz = tlsphdr->p_filesz;
243 		tlsmodlist[tlsmodndx]->tm_memsz = tlsphdr->p_memsz;
244 		tlsmodlist[tlsmodndx]->tm_flags = TM_FLG_STATICTLS;
245 		tlsmodlist[tlsmodndx]->tm_stattlsoffset = TLSSTATOFF(lmp);
246 		tlsmodndx++;
247 	}
248 
249 	DBG_CALL(Dbg_tls_static_block(&lml_main, (void *)tlsmodlist,
250 	    tls_static_size));
251 	(*fptr)(tlsmodlist, tls_static_size);
252 
253 	/*
254 	 * We're done with the list - clean it up.
255 	 */
256 	free(tlsmodlist);
257 	return (1);
258 }
259