xref: /illumos-gate/usr/src/lib/libc/port/threads/tls.c (revision adc04c2d)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
58cd45542Sraf  * Common Development and Distribution License (the "License").
68cd45542Sraf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
218cd45542Sraf 
227c478bd9Sstevel@tonic-gate /*
238cd45542Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include "lint.h"
287c478bd9Sstevel@tonic-gate #include "thr_uberdata.h"
297c478bd9Sstevel@tonic-gate 
30da8597e3SRoger A. Faulkner #define	MIN_MOD_SLOTS	8
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate /*
338cd45542Sraf  * Used to inform libc_init() that we are on the primary link map,
348cd45542Sraf  * and to cause certain functions (like malloc() and sbrk()) to fail
358cd45542Sraf  * (with ENOTSUP) when they are called on an alternate link map.
367c478bd9Sstevel@tonic-gate  */
378cd45542Sraf int primary_link_map = 0;
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #if defined(_LP64)
407c478bd9Sstevel@tonic-gate #define	ALIGN	16
417c478bd9Sstevel@tonic-gate #else
427c478bd9Sstevel@tonic-gate #define	ALIGN	8
437c478bd9Sstevel@tonic-gate #endif
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate /*
467c478bd9Sstevel@tonic-gate  * Grow the TLS module information array as necessary to include the
477c478bd9Sstevel@tonic-gate  * specified module-id.  tls_modinfo->tls_size must be a power of two.
487c478bd9Sstevel@tonic-gate  * Return a pointer to the (possibly reallocated) module information array.
497c478bd9Sstevel@tonic-gate  */
507c478bd9Sstevel@tonic-gate static TLS_modinfo *
tls_modinfo_alloc(tls_metadata_t * tlsm,ulong_t moduleid)517c478bd9Sstevel@tonic-gate tls_modinfo_alloc(tls_metadata_t *tlsm, ulong_t moduleid)
527c478bd9Sstevel@tonic-gate {
537c478bd9Sstevel@tonic-gate 	tls_t *tls_modinfo = &tlsm->tls_modinfo;
547c478bd9Sstevel@tonic-gate 	TLS_modinfo *modinfo;
557c478bd9Sstevel@tonic-gate 	size_t mod_slots;
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate 	if ((modinfo = tls_modinfo->tls_data) == NULL ||
587c478bd9Sstevel@tonic-gate 	    tls_modinfo->tls_size <= moduleid) {
597c478bd9Sstevel@tonic-gate 		if ((mod_slots = tls_modinfo->tls_size) == 0)
607c478bd9Sstevel@tonic-gate 			mod_slots = MIN_MOD_SLOTS;
617c478bd9Sstevel@tonic-gate 		while (mod_slots <= moduleid)
627c478bd9Sstevel@tonic-gate 			mod_slots *= 2;
637c478bd9Sstevel@tonic-gate 		modinfo = lmalloc(mod_slots * sizeof (TLS_modinfo));
647c478bd9Sstevel@tonic-gate 		if (tls_modinfo->tls_data != NULL) {
658cd45542Sraf 			(void) memcpy(modinfo, tls_modinfo->tls_data,
668cd45542Sraf 			    tls_modinfo->tls_size * sizeof (TLS_modinfo));
677c478bd9Sstevel@tonic-gate 			lfree(tls_modinfo->tls_data,
688cd45542Sraf 			    tls_modinfo->tls_size * sizeof (TLS_modinfo));
697c478bd9Sstevel@tonic-gate 		}
707c478bd9Sstevel@tonic-gate 		tls_modinfo->tls_data = modinfo;
717c478bd9Sstevel@tonic-gate 		tls_modinfo->tls_size = mod_slots;
727c478bd9Sstevel@tonic-gate 	}
737c478bd9Sstevel@tonic-gate 	return (modinfo);
747c478bd9Sstevel@tonic-gate }
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate /*
777c478bd9Sstevel@tonic-gate  * This is called from the dynamic linker, before libc_init() is called,
787c478bd9Sstevel@tonic-gate  * to setup all of the TLS blocks that are available at process startup
797c478bd9Sstevel@tonic-gate  * and hence must be included as part of the static TLS block.
807c478bd9Sstevel@tonic-gate  * No locks are needed because we are single-threaded at this point.
817c478bd9Sstevel@tonic-gate  * We must be careful not to call any function that could possibly
827c478bd9Sstevel@tonic-gate  * invoke the dynamic linker.  That is, we must only call functions
837c478bd9Sstevel@tonic-gate  * that are wholly private to libc.
847c478bd9Sstevel@tonic-gate  */
857c478bd9Sstevel@tonic-gate void
__tls_static_mods(TLS_modinfo ** tlslist,unsigned long statictlssize)867c478bd9Sstevel@tonic-gate __tls_static_mods(TLS_modinfo **tlslist, unsigned long statictlssize)
877c478bd9Sstevel@tonic-gate {
887c478bd9Sstevel@tonic-gate 	ulwp_t *oldself = __curthread();
897c478bd9Sstevel@tonic-gate 	tls_metadata_t *tlsm;
907c478bd9Sstevel@tonic-gate 	TLS_modinfo **tlspp;
917c478bd9Sstevel@tonic-gate 	TLS_modinfo *tlsp;
927c478bd9Sstevel@tonic-gate 	TLS_modinfo *modinfo;
937c478bd9Sstevel@tonic-gate 	caddr_t data;
947c478bd9Sstevel@tonic-gate 	caddr_t data_end;
957c478bd9Sstevel@tonic-gate 	int max_modid;
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate 	primary_link_map = 1;		/* inform libc_init */
987c478bd9Sstevel@tonic-gate 	if (statictlssize == 0)
997c478bd9Sstevel@tonic-gate 		return;
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	/*
1027c478bd9Sstevel@tonic-gate 	 * Retrieve whatever dynamic TLS metadata was generated by code
1037c478bd9Sstevel@tonic-gate 	 * running on alternate link maps prior to now (we must be running
1047c478bd9Sstevel@tonic-gate 	 * on the primary link map now since __tls_static_mods() is only
1057c478bd9Sstevel@tonic-gate 	 * called on the primary link map).
1067c478bd9Sstevel@tonic-gate 	 */
1077c478bd9Sstevel@tonic-gate 	tlsm = &__uberdata.tls_metadata;
1087c478bd9Sstevel@tonic-gate 	if (oldself != NULL) {
1098cd45542Sraf 		(void) memcpy(tlsm,
1100293487cSraf 		    &oldself->ul_uberdata->tls_metadata, sizeof (*tlsm));
1117c478bd9Sstevel@tonic-gate 		ASSERT(tlsm->static_tls.tls_data == NULL);
1127c478bd9Sstevel@tonic-gate 	}
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate 	/*
1157c478bd9Sstevel@tonic-gate 	 * We call lmalloc() to allocate the template even though libc_init()
1167c478bd9Sstevel@tonic-gate 	 * has not yet been called.  lmalloc() must and does deal with this.
1177c478bd9Sstevel@tonic-gate 	 */
1187c478bd9Sstevel@tonic-gate 	ASSERT((statictlssize & (ALIGN - 1)) == 0);
1197c478bd9Sstevel@tonic-gate 	tlsm->static_tls.tls_data = data = lmalloc(statictlssize);
1207c478bd9Sstevel@tonic-gate 	data_end = data + statictlssize;
1217c478bd9Sstevel@tonic-gate 	tlsm->static_tls.tls_size = statictlssize;
1227c478bd9Sstevel@tonic-gate 	/*
1237c478bd9Sstevel@tonic-gate 	 * Initialize the static TLS template.
1247c478bd9Sstevel@tonic-gate 	 * We make no assumptions about the order in memory of the TLS
1257c478bd9Sstevel@tonic-gate 	 * modules we are processing, only that they fit within the
1267c478bd9Sstevel@tonic-gate 	 * total size we are given and that they are self-consistent.
1277c478bd9Sstevel@tonic-gate 	 * We do not assume any order for the moduleid's; we only assume
1287c478bd9Sstevel@tonic-gate 	 * that they are reasonably small integers.
1297c478bd9Sstevel@tonic-gate 	 */
1307c478bd9Sstevel@tonic-gate 	for (max_modid = 0, tlspp = tlslist; (tlsp = *tlspp) != NULL; tlspp++) {
1317c478bd9Sstevel@tonic-gate 		ASSERT(tlsp->tm_flags & TM_FLG_STATICTLS);
1327c478bd9Sstevel@tonic-gate 		ASSERT(tlsp->tm_stattlsoffset > 0);
1337c478bd9Sstevel@tonic-gate 		ASSERT(tlsp->tm_stattlsoffset <= statictlssize);
1347c478bd9Sstevel@tonic-gate 		ASSERT((tlsp->tm_stattlsoffset & (ALIGN - 1)) == 0);
1357c478bd9Sstevel@tonic-gate 		ASSERT(tlsp->tm_filesz <= tlsp->tm_memsz);
1367c478bd9Sstevel@tonic-gate 		ASSERT(tlsp->tm_memsz <= tlsp->tm_stattlsoffset);
1377c478bd9Sstevel@tonic-gate 		if (tlsp->tm_filesz)
1388cd45542Sraf 			(void) memcpy(data_end-tlsp->tm_stattlsoffset,
1398cd45542Sraf 			    tlsp->tm_tlsblock, tlsp->tm_filesz);
1407c478bd9Sstevel@tonic-gate 		if (max_modid < tlsp->tm_modid)
1417c478bd9Sstevel@tonic-gate 			max_modid = tlsp->tm_modid;
1427c478bd9Sstevel@tonic-gate 	}
1437c478bd9Sstevel@tonic-gate 	/*
1447c478bd9Sstevel@tonic-gate 	 * Record the static TLS_modinfo information.
1457c478bd9Sstevel@tonic-gate 	 */
1467c478bd9Sstevel@tonic-gate 	modinfo = tls_modinfo_alloc(tlsm, max_modid);
1477c478bd9Sstevel@tonic-gate 	for (tlspp = tlslist; (tlsp = *tlspp) != NULL; tlspp++)
1488cd45542Sraf 		(void) memcpy(&modinfo[tlsp->tm_modid],
1490293487cSraf 		    tlsp, sizeof (*tlsp));
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate 	/*
1527c478bd9Sstevel@tonic-gate 	 * Copy the new tls_metadata back to the old, if any,
1537c478bd9Sstevel@tonic-gate 	 * since it will be copied up again in libc_init().
1547c478bd9Sstevel@tonic-gate 	 */
1557c478bd9Sstevel@tonic-gate 	if (oldself != NULL)
1568cd45542Sraf 		(void) memcpy(&oldself->ul_uberdata->tls_metadata,
1570293487cSraf 		    tlsm, sizeof (*tlsm));
1587c478bd9Sstevel@tonic-gate }
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate /*
1617c478bd9Sstevel@tonic-gate  * This is called from the dynamic linker for each module not included
1627c478bd9Sstevel@tonic-gate  * in the static TLS mod list, after the module has been loaded but
1637c478bd9Sstevel@tonic-gate  * before any of the module's init code has been executed.
1647c478bd9Sstevel@tonic-gate  */
1657c478bd9Sstevel@tonic-gate void
__tls_mod_add(TLS_modinfo * tlsp)1667c478bd9Sstevel@tonic-gate __tls_mod_add(TLS_modinfo *tlsp)
1677c478bd9Sstevel@tonic-gate {
1687c478bd9Sstevel@tonic-gate 	tls_metadata_t *tlsm = &curthread->ul_uberdata->tls_metadata;
1697c478bd9Sstevel@tonic-gate 	ulong_t moduleid = tlsp->tm_modid;
1707c478bd9Sstevel@tonic-gate 	TLS_modinfo *modinfo;
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	lmutex_lock(&tlsm->tls_lock);
1737c478bd9Sstevel@tonic-gate 	ASSERT(!(tlsp->tm_flags & TM_FLG_STATICTLS));
1747c478bd9Sstevel@tonic-gate 	ASSERT(tlsp->tm_filesz <= tlsp->tm_memsz);
1757c478bd9Sstevel@tonic-gate 	modinfo = tls_modinfo_alloc(tlsm, moduleid);
1768cd45542Sraf 	(void) memcpy(&modinfo[moduleid], tlsp, sizeof (*tlsp));
1777c478bd9Sstevel@tonic-gate 	lmutex_unlock(&tlsm->tls_lock);
1787c478bd9Sstevel@tonic-gate }
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate /*
1817c478bd9Sstevel@tonic-gate  * Called for each module as it is unloaded from memory by dlclose().
1827c478bd9Sstevel@tonic-gate  */
1837c478bd9Sstevel@tonic-gate void
__tls_mod_remove(TLS_modinfo * tlsp)1847c478bd9Sstevel@tonic-gate __tls_mod_remove(TLS_modinfo *tlsp)
1857c478bd9Sstevel@tonic-gate {
1867c478bd9Sstevel@tonic-gate 	tls_metadata_t *tlsm = &curthread->ul_uberdata->tls_metadata;
1877c478bd9Sstevel@tonic-gate 	ulong_t moduleid = tlsp->tm_modid;
1887c478bd9Sstevel@tonic-gate 	TLS_modinfo *modinfo;
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 	lmutex_lock(&tlsm->tls_lock);
1917c478bd9Sstevel@tonic-gate 	ASSERT(tlsm->tls_modinfo.tls_data != NULL &&
1927c478bd9Sstevel@tonic-gate 	    moduleid < tlsm->tls_modinfo.tls_size);
1937c478bd9Sstevel@tonic-gate 	modinfo = tlsm->tls_modinfo.tls_data;
1948cd45542Sraf 	(void) memset(&modinfo[moduleid], 0, sizeof (TLS_modinfo));
1957c478bd9Sstevel@tonic-gate 	lmutex_unlock(&tlsm->tls_lock);
1967c478bd9Sstevel@tonic-gate }
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate extern	int	_preexec_exit_handlers();
1997c478bd9Sstevel@tonic-gate extern	void	libc_init();
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate const Lc_interface tls_rtldinfo[] = {
202*adc04c2dSToomas Soome 	{ .ci_tag = CI_VERSION,	.ci_un.ci_val = CI_V_CURRENT },
203*adc04c2dSToomas Soome 	{ .ci_tag = CI_ATEXIT, .ci_un.ci_func = _preexec_exit_handlers },
204*adc04c2dSToomas Soome 	{ .ci_tag = CI_TLS_MODADD, .ci_un.ci_func = __tls_mod_add },
205*adc04c2dSToomas Soome 	{ .ci_tag = CI_TLS_MODREM, .ci_un.ci_func = __tls_mod_remove },
206*adc04c2dSToomas Soome 	{ .ci_tag = CI_TLS_STATMOD, .ci_un.ci_func = __tls_static_mods },
207*adc04c2dSToomas Soome 	{ .ci_tag = CI_THRINIT,	.ci_un.ci_func = libc_init },
208*adc04c2dSToomas Soome 	{ .ci_tag = CI_NULL, .ci_un.ci_func = NULL }
2097c478bd9Sstevel@tonic-gate };
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate /*
2127c478bd9Sstevel@tonic-gate  * Return the address of a TLS variable for the current thread.
2137c478bd9Sstevel@tonic-gate  * Run the constructors for newly-allocated dynamic TLS.
2147c478bd9Sstevel@tonic-gate  */
2157c478bd9Sstevel@tonic-gate void *
slow_tls_get_addr(TLS_index * tls_index)2167c478bd9Sstevel@tonic-gate slow_tls_get_addr(TLS_index *tls_index)
2177c478bd9Sstevel@tonic-gate {
2187c478bd9Sstevel@tonic-gate 	ulwp_t *self = curthread;
2197c478bd9Sstevel@tonic-gate 	tls_metadata_t *tlsm = &self->ul_uberdata->tls_metadata;
2207c478bd9Sstevel@tonic-gate 	TLS_modinfo *tlsp;
2217c478bd9Sstevel@tonic-gate 	ulong_t moduleid;
2227c478bd9Sstevel@tonic-gate 	tls_t *tlsent;
2237c478bd9Sstevel@tonic-gate 	caddr_t	base;
2247c478bd9Sstevel@tonic-gate 	void (**initarray)(void);
2257c478bd9Sstevel@tonic-gate 	ulong_t arraycnt = 0;
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	/*
2287c478bd9Sstevel@tonic-gate 	 * Defer signals until we have finished calling
2297c478bd9Sstevel@tonic-gate 	 * all of the constructors.
2307c478bd9Sstevel@tonic-gate 	 */
2317c478bd9Sstevel@tonic-gate 	sigoff(self);
2327c478bd9Sstevel@tonic-gate 	lmutex_lock(&tlsm->tls_lock);
2337c478bd9Sstevel@tonic-gate 	if ((moduleid = tls_index->ti_moduleid) < self->ul_ntlsent)
2347c478bd9Sstevel@tonic-gate 		tlsent = self->ul_tlsent;
2357c478bd9Sstevel@tonic-gate 	else {
2367c478bd9Sstevel@tonic-gate 		ASSERT(moduleid < tlsm->tls_modinfo.tls_size);
2377c478bd9Sstevel@tonic-gate 		tlsent = lmalloc(tlsm->tls_modinfo.tls_size * sizeof (tls_t));
2387c478bd9Sstevel@tonic-gate 		if (self->ul_tlsent != NULL) {
2398cd45542Sraf 			(void) memcpy(tlsent, self->ul_tlsent,
2408cd45542Sraf 			    self->ul_ntlsent * sizeof (tls_t));
2417c478bd9Sstevel@tonic-gate 			lfree(self->ul_tlsent,
2428cd45542Sraf 			    self->ul_ntlsent * sizeof (tls_t));
2437c478bd9Sstevel@tonic-gate 		}
2447c478bd9Sstevel@tonic-gate 		self->ul_tlsent = tlsent;
2457c478bd9Sstevel@tonic-gate 		self->ul_ntlsent = tlsm->tls_modinfo.tls_size;
2467c478bd9Sstevel@tonic-gate 	}
2477c478bd9Sstevel@tonic-gate 	tlsent += moduleid;
2487c478bd9Sstevel@tonic-gate 	if ((base = tlsent->tls_data) == NULL) {
2497c478bd9Sstevel@tonic-gate 		tlsp = (TLS_modinfo *)tlsm->tls_modinfo.tls_data + moduleid;
2507c478bd9Sstevel@tonic-gate 		if (tlsp->tm_memsz == 0) {	/* dlclose()d module? */
2517c478bd9Sstevel@tonic-gate 			base = NULL;
2527c478bd9Sstevel@tonic-gate 		} else if (tlsp->tm_flags & TM_FLG_STATICTLS) {
2537c478bd9Sstevel@tonic-gate 			/* static TLS is already allocated/initialized */
2547c478bd9Sstevel@tonic-gate 			base = (caddr_t)self - tlsp->tm_stattlsoffset;
2557c478bd9Sstevel@tonic-gate 			tlsent->tls_data = base;
2567c478bd9Sstevel@tonic-gate 			tlsent->tls_size = 0;	/* don't lfree() this space */
2577c478bd9Sstevel@tonic-gate 		} else {
2587c478bd9Sstevel@tonic-gate 			/* allocate/initialize the dynamic TLS */
2597c478bd9Sstevel@tonic-gate 			base = lmalloc(tlsp->tm_memsz);
2607c478bd9Sstevel@tonic-gate 			if (tlsp->tm_filesz != 0)
2618cd45542Sraf 				(void) memcpy(base, tlsp->tm_tlsblock,
2628cd45542Sraf 				    tlsp->tm_filesz);
2637c478bd9Sstevel@tonic-gate 			tlsent->tls_data = base;
2647c478bd9Sstevel@tonic-gate 			tlsent->tls_size = tlsp->tm_memsz;
2657c478bd9Sstevel@tonic-gate 			/* remember the constructors */
2667c478bd9Sstevel@tonic-gate 			arraycnt = tlsp->tm_tlsinitarraycnt;
2677c478bd9Sstevel@tonic-gate 			initarray = tlsp->tm_tlsinitarray;
2687c478bd9Sstevel@tonic-gate 		}
2697c478bd9Sstevel@tonic-gate 	}
2707c478bd9Sstevel@tonic-gate 	lmutex_unlock(&tlsm->tls_lock);
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	/*
2737c478bd9Sstevel@tonic-gate 	 * Call constructors, if any, in ascending order.
2747c478bd9Sstevel@tonic-gate 	 * We have to do this after dropping tls_lock because
2757c478bd9Sstevel@tonic-gate 	 * we have no idea what the constructors will do.
2767c478bd9Sstevel@tonic-gate 	 * At least we have signals deferred until they are done.
2777c478bd9Sstevel@tonic-gate 	 */
2787c478bd9Sstevel@tonic-gate 	if (arraycnt) {
2797c478bd9Sstevel@tonic-gate 		do {
2807c478bd9Sstevel@tonic-gate 			(**initarray++)();
2817c478bd9Sstevel@tonic-gate 		} while (--arraycnt != 0);
2827c478bd9Sstevel@tonic-gate 	}
2837c478bd9Sstevel@tonic-gate 
2848cd45542Sraf 	if (base == NULL)	/* kludge to get x86/x64 to boot */
2858cd45542Sraf 		base = (caddr_t)self - 512;
2868cd45542Sraf 
2877c478bd9Sstevel@tonic-gate 	sigon(self);
2887c478bd9Sstevel@tonic-gate 	return (base + tls_index->ti_tlsoffset);
2897c478bd9Sstevel@tonic-gate }
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate #ifdef	TLS_GET_ADDR_IS_WRITTEN_IN_ASSEMBLER
2927c478bd9Sstevel@tonic-gate /*
2937c478bd9Sstevel@tonic-gate  * For speed, we do not make reference to any static data in this function.
2947c478bd9Sstevel@tonic-gate  * If necessary to do so, we do a tail call to slow_tls_get_addr().
2957c478bd9Sstevel@tonic-gate  */
2967c478bd9Sstevel@tonic-gate void *
__tls_get_addr(TLS_index * tls_index)2977c478bd9Sstevel@tonic-gate __tls_get_addr(TLS_index *tls_index)
2987c478bd9Sstevel@tonic-gate {
2997c478bd9Sstevel@tonic-gate 	ulwp_t *self = curthread;
3007c478bd9Sstevel@tonic-gate 	tls_t *tlsent = self->ul_tlsent;
3017c478bd9Sstevel@tonic-gate 	ulong_t moduleid;
3027c478bd9Sstevel@tonic-gate 	caddr_t	base;
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	if ((moduleid = tls_index->ti_moduleid) < self->ul_ntlsent &&
3057c478bd9Sstevel@tonic-gate 	    (base = tlsent[moduleid].tls_data) != NULL)
3067c478bd9Sstevel@tonic-gate 		return (base + tls_index->ti_tlsoffset);
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	return (slow_tls_get_addr(tls_index));
3097c478bd9Sstevel@tonic-gate }
3107c478bd9Sstevel@tonic-gate #endif	/* TLS_GET_ADDR_IS_WRITTEN_IN_ASSEMBLER */
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate /*
3137257d1b4Sraf  * This is called by _thrp_setup() to initialize the thread's static TLS.
3147c478bd9Sstevel@tonic-gate  * Constructors for initially allocated static TLS are called here.
3157c478bd9Sstevel@tonic-gate  */
3167c478bd9Sstevel@tonic-gate void
tls_setup()3177c478bd9Sstevel@tonic-gate tls_setup()
3187c478bd9Sstevel@tonic-gate {
3197c478bd9Sstevel@tonic-gate 	ulwp_t *self = curthread;
3207c478bd9Sstevel@tonic-gate 	tls_metadata_t *tlsm = &self->ul_uberdata->tls_metadata;
3217c478bd9Sstevel@tonic-gate 	TLS_modinfo *tlsp;
3227c478bd9Sstevel@tonic-gate 	long moduleid;
3237c478bd9Sstevel@tonic-gate 	ulong_t nmods;
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	if (tlsm->static_tls.tls_size == 0)	/* no static TLS */
3267c478bd9Sstevel@tonic-gate 		return;
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	/* static TLS initialization */
3298cd45542Sraf 	(void) memcpy((caddr_t)self - tlsm->static_tls.tls_size,
3308cd45542Sraf 	    tlsm->static_tls.tls_data, tlsm->static_tls.tls_size);
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	/* call TLS constructors for the static TLS just initialized */
3337c478bd9Sstevel@tonic-gate 	lmutex_lock(&tlsm->tls_lock);
3347c478bd9Sstevel@tonic-gate 	nmods = tlsm->tls_modinfo.tls_size;
3357c478bd9Sstevel@tonic-gate 	for (moduleid = 0; moduleid < nmods; moduleid++) {
3367c478bd9Sstevel@tonic-gate 		/*
3377c478bd9Sstevel@tonic-gate 		 * Resume where we left off in the module array.
3387c478bd9Sstevel@tonic-gate 		 * tls_modinfo.tls_data may have changed since we
3397c478bd9Sstevel@tonic-gate 		 * dropped and reacquired tls_lock, but TLS modules
3407c478bd9Sstevel@tonic-gate 		 * retain their positions in the new array.
3417c478bd9Sstevel@tonic-gate 		 */
3427c478bd9Sstevel@tonic-gate 		tlsp = (TLS_modinfo *)tlsm->tls_modinfo.tls_data + moduleid;
3437c478bd9Sstevel@tonic-gate 		/*
3447c478bd9Sstevel@tonic-gate 		 * Call constructors for this module if there are any
3457c478bd9Sstevel@tonic-gate 		 * to be called and if it is part of the static TLS.
3467c478bd9Sstevel@tonic-gate 		 */
3477c478bd9Sstevel@tonic-gate 		if (tlsp->tm_tlsinitarraycnt != 0 &&
3487c478bd9Sstevel@tonic-gate 		    (tlsp->tm_flags & TM_FLG_STATICTLS)) {
3497c478bd9Sstevel@tonic-gate 			ulong_t arraycnt = tlsp->tm_tlsinitarraycnt;
3507c478bd9Sstevel@tonic-gate 			void (**initarray)(void) = tlsp->tm_tlsinitarray;
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 			/*
3537c478bd9Sstevel@tonic-gate 			 * Call the constructors in ascending order.
3547c478bd9Sstevel@tonic-gate 			 * We must drop tls_lock while doing this because
3557c478bd9Sstevel@tonic-gate 			 * we have no idea what the constructors will do.
3567c478bd9Sstevel@tonic-gate 			 */
3577c478bd9Sstevel@tonic-gate 			lmutex_unlock(&tlsm->tls_lock);
3587c478bd9Sstevel@tonic-gate 			do {
3597c478bd9Sstevel@tonic-gate 				(**initarray++)();
3607c478bd9Sstevel@tonic-gate 			} while (--arraycnt != 0);
3617c478bd9Sstevel@tonic-gate 			lmutex_lock(&tlsm->tls_lock);
3627c478bd9Sstevel@tonic-gate 		}
3637c478bd9Sstevel@tonic-gate 	}
3647c478bd9Sstevel@tonic-gate 	lmutex_unlock(&tlsm->tls_lock);
3657c478bd9Sstevel@tonic-gate }
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate /*
3687c478bd9Sstevel@tonic-gate  * This is called by _thrp_exit() to deallocate the thread's TLS.
3697c478bd9Sstevel@tonic-gate  * Destructors for all allocated TLS are called here.
3707c478bd9Sstevel@tonic-gate  */
3717c478bd9Sstevel@tonic-gate void
tls_exit()3727c478bd9Sstevel@tonic-gate tls_exit()
3737c478bd9Sstevel@tonic-gate {
3747c478bd9Sstevel@tonic-gate 	ulwp_t *self = curthread;
3757c478bd9Sstevel@tonic-gate 	tls_metadata_t *tlsm = &self->ul_uberdata->tls_metadata;
3767c478bd9Sstevel@tonic-gate 	tls_t *tlsent;
3777c478bd9Sstevel@tonic-gate 	TLS_modinfo *tlsp;
3787c478bd9Sstevel@tonic-gate 	long moduleid;
3797c478bd9Sstevel@tonic-gate 	ulong_t nmods;
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	if (tlsm->static_tls.tls_size == 0 && self->ul_ntlsent == 0)
3827c478bd9Sstevel@tonic-gate 		return;		/* no TLS */
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	/*
3857c478bd9Sstevel@tonic-gate 	 * Call TLS destructors for all TLS allocated for this thread.
3867c478bd9Sstevel@tonic-gate 	 */
3877c478bd9Sstevel@tonic-gate 	lmutex_lock(&tlsm->tls_lock);
3887c478bd9Sstevel@tonic-gate 	nmods = tlsm->tls_modinfo.tls_size;
3897c478bd9Sstevel@tonic-gate 	for (moduleid = nmods - 1; moduleid >= 0; --moduleid) {
3907c478bd9Sstevel@tonic-gate 		/*
3917c478bd9Sstevel@tonic-gate 		 * Resume where we left off in the module array.
3927c478bd9Sstevel@tonic-gate 		 * tls_modinfo.tls_data may have changed since we
3937c478bd9Sstevel@tonic-gate 		 * dropped and reacquired tls_lock, but TLS modules
3947c478bd9Sstevel@tonic-gate 		 * retain their positions in the new array.
3957c478bd9Sstevel@tonic-gate 		 */
3967c478bd9Sstevel@tonic-gate 		tlsp = (TLS_modinfo *)tlsm->tls_modinfo.tls_data + moduleid;
3977c478bd9Sstevel@tonic-gate 		/*
3987c478bd9Sstevel@tonic-gate 		 * Call destructors for this module if there are any
3997c478bd9Sstevel@tonic-gate 		 * to be called and if it is part of the static TLS or
4007c478bd9Sstevel@tonic-gate 		 * if the dynamic TLS for the module has been allocated.
4017c478bd9Sstevel@tonic-gate 		 */
4027c478bd9Sstevel@tonic-gate 		if (tlsp->tm_tlsfiniarraycnt != 0 &&
4037c478bd9Sstevel@tonic-gate 		    ((tlsp->tm_flags & TM_FLG_STATICTLS) ||
4047c478bd9Sstevel@tonic-gate 		    (moduleid < self->ul_ntlsent &&
4057c478bd9Sstevel@tonic-gate 		    (tlsent = self->ul_tlsent) != NULL &&
4067c478bd9Sstevel@tonic-gate 		    tlsent[moduleid].tls_data != NULL))) {
4077c478bd9Sstevel@tonic-gate 			ulong_t arraycnt = tlsp->tm_tlsfiniarraycnt;
4087c478bd9Sstevel@tonic-gate 			void (**finiarray)(void) = tlsp->tm_tlsfiniarray;
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 			/*
4117c478bd9Sstevel@tonic-gate 			 * Call the destructors in descending order.
4127c478bd9Sstevel@tonic-gate 			 * We must drop tls_lock while doing this because
4137c478bd9Sstevel@tonic-gate 			 * we have no idea what the destructors will do.
4147c478bd9Sstevel@tonic-gate 			 */
4157c478bd9Sstevel@tonic-gate 			lmutex_unlock(&tlsm->tls_lock);
4167c478bd9Sstevel@tonic-gate 			finiarray += arraycnt;
4177c478bd9Sstevel@tonic-gate 			do {
4187c478bd9Sstevel@tonic-gate 				(**--finiarray)();
4197c478bd9Sstevel@tonic-gate 			} while (--arraycnt != 0);
4207c478bd9Sstevel@tonic-gate 			lmutex_lock(&tlsm->tls_lock);
4217c478bd9Sstevel@tonic-gate 		}
4227c478bd9Sstevel@tonic-gate 	}
4237c478bd9Sstevel@tonic-gate 	lmutex_unlock(&tlsm->tls_lock);
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 	tls_free(self);
4267c478bd9Sstevel@tonic-gate }
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate /*
4297c478bd9Sstevel@tonic-gate  * We only free the dynamically allocated TLS; the statically
4307c478bd9Sstevel@tonic-gate  * allocated TLS is reused when the ulwp_t is reallocated.
4317c478bd9Sstevel@tonic-gate  */
4327c478bd9Sstevel@tonic-gate void
tls_free(ulwp_t * ulwp)4337c478bd9Sstevel@tonic-gate tls_free(ulwp_t *ulwp)
4347c478bd9Sstevel@tonic-gate {
4357c478bd9Sstevel@tonic-gate 	ulong_t moduleid;
4367c478bd9Sstevel@tonic-gate 	tls_t *tlsent;
4377c478bd9Sstevel@tonic-gate 	size_t ntlsent;
4387c478bd9Sstevel@tonic-gate 	void *base;
4397c478bd9Sstevel@tonic-gate 	size_t size;
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 	if ((tlsent = ulwp->ul_tlsent) == NULL ||
4427c478bd9Sstevel@tonic-gate 	    (ntlsent = ulwp->ul_ntlsent) == 0)
4437c478bd9Sstevel@tonic-gate 		return;
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	for (moduleid = 0; moduleid < ntlsent; moduleid++, tlsent++) {
4467c478bd9Sstevel@tonic-gate 		if ((base = tlsent->tls_data) != NULL &&
4477c478bd9Sstevel@tonic-gate 		    (size = tlsent->tls_size) != 0)
4487c478bd9Sstevel@tonic-gate 			lfree(base, size);
4497c478bd9Sstevel@tonic-gate 		tlsent->tls_data = NULL;	/* paranoia */
4507c478bd9Sstevel@tonic-gate 		tlsent->tls_size = 0;
4517c478bd9Sstevel@tonic-gate 	}
4527c478bd9Sstevel@tonic-gate 	lfree(ulwp->ul_tlsent, ntlsent * sizeof (tls_t));
4537c478bd9Sstevel@tonic-gate 	ulwp->ul_tlsent = NULL;
4547c478bd9Sstevel@tonic-gate 	ulwp->ul_ntlsent = 0;
4557c478bd9Sstevel@tonic-gate }
456