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