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
55aefb655Srie * Common Development and Distribution License (the "License").
65aefb655Srie * 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 */
215aefb655Srie
227c478bd9Sstevel@tonic-gate /*
23*56deab07SRod Evans * Copyright 2009 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 <stdio.h>
287c478bd9Sstevel@tonic-gate #include <strings.h>
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <dlfcn.h>
317c478bd9Sstevel@tonic-gate #include <libc_int.h>
327c478bd9Sstevel@tonic-gate #include <_rtld.h>
337c478bd9Sstevel@tonic-gate #include <_elf.h>
347c478bd9Sstevel@tonic-gate #include <msg.h>
357c478bd9Sstevel@tonic-gate #include <debug.h>
367c478bd9Sstevel@tonic-gate
377c478bd9Sstevel@tonic-gate #define TLSBLOCKCNT 16 /* number of blocks of tmi_bits to allocate */
387c478bd9Sstevel@tonic-gate /* at a time. */
397c478bd9Sstevel@tonic-gate typedef struct {
407c478bd9Sstevel@tonic-gate uint_t *tmi_bits;
417c478bd9Sstevel@tonic-gate ulong_t tmi_lowfree;
427c478bd9Sstevel@tonic-gate ulong_t tmi_cnt;
437c478bd9Sstevel@tonic-gate } Tlsmodid;
447c478bd9Sstevel@tonic-gate
457c478bd9Sstevel@tonic-gate static Tlsmodid tmid = {0, 0, 0};
467c478bd9Sstevel@tonic-gate
47d326b23bSrie static ulong_t
tls_getmodid()487c478bd9Sstevel@tonic-gate tls_getmodid()
497c478bd9Sstevel@tonic-gate {
50d326b23bSrie ulong_t ndx, cnt;
517c478bd9Sstevel@tonic-gate
527c478bd9Sstevel@tonic-gate if (tmid.tmi_bits == 0) {
53*56deab07SRod Evans if ((tmid.tmi_bits =
54*56deab07SRod Evans calloc(TLSBLOCKCNT, sizeof (uint_t))) == NULL)
5510a4fa49Srie return ((ulong_t)-1);
567c478bd9Sstevel@tonic-gate tmid.tmi_bits[0] = 1;
577c478bd9Sstevel@tonic-gate tmid.tmi_lowfree = 1;
587c478bd9Sstevel@tonic-gate tmid.tmi_cnt = TLSBLOCKCNT;
597c478bd9Sstevel@tonic-gate return (0);
607c478bd9Sstevel@tonic-gate }
617c478bd9Sstevel@tonic-gate
62d326b23bSrie for (cnt = tmid.tmi_lowfree / (sizeof (uint_t) * 8);
63d326b23bSrie cnt < tmid.tmi_cnt; cnt++) {
64d326b23bSrie uint_t bits;
65d326b23bSrie
667c478bd9Sstevel@tonic-gate /*
677c478bd9Sstevel@tonic-gate * If all bits are assigned - move on.
687c478bd9Sstevel@tonic-gate */
69d326b23bSrie if ((tmid.tmi_bits[cnt] ^ ~((uint_t)0)) == 0)
707c478bd9Sstevel@tonic-gate continue;
71d326b23bSrie
72d326b23bSrie for (ndx = 0, bits = 1; bits; bits = bits << 1, ndx++) {
73d326b23bSrie if ((tmid.tmi_bits[cnt] & bits) == 0) {
74d326b23bSrie tmid.tmi_bits[cnt] |= bits;
75d326b23bSrie ndx = (cnt * (sizeof (uint_t)) * 8) + ndx;
767c478bd9Sstevel@tonic-gate tmid.tmi_lowfree = ndx + 1;
777c478bd9Sstevel@tonic-gate return (ndx);
787c478bd9Sstevel@tonic-gate }
797c478bd9Sstevel@tonic-gate }
807c478bd9Sstevel@tonic-gate }
817c478bd9Sstevel@tonic-gate
827c478bd9Sstevel@tonic-gate /*
837c478bd9Sstevel@tonic-gate * All bits taken - must allocate a new block
847c478bd9Sstevel@tonic-gate */
8510a4fa49Srie if ((tmid.tmi_bits = realloc(tmid.tmi_bits,
867c478bd9Sstevel@tonic-gate ((tmid.tmi_cnt * sizeof (uint_t)) +
87*56deab07SRod Evans (TLSBLOCKCNT * sizeof (uint_t))))) == NULL)
8810a4fa49Srie return ((ulong_t)-1);
8910a4fa49Srie
907c478bd9Sstevel@tonic-gate /*
9110a4fa49Srie * Clear out the tail of the new allocation.
927c478bd9Sstevel@tonic-gate */
937c478bd9Sstevel@tonic-gate bzero(&(tmid.tmi_bits[tmid.tmi_cnt]), TLSBLOCKCNT * sizeof (uint_t));
947c478bd9Sstevel@tonic-gate tmid.tmi_bits[tmid.tmi_cnt] = 1;
957c478bd9Sstevel@tonic-gate ndx = (tmid.tmi_cnt * sizeof (uint_t)) * 8;
967c478bd9Sstevel@tonic-gate tmid.tmi_lowfree = ndx + 1;
977c478bd9Sstevel@tonic-gate tmid.tmi_cnt += TLSBLOCKCNT;
987c478bd9Sstevel@tonic-gate
997c478bd9Sstevel@tonic-gate return (ndx);
1007c478bd9Sstevel@tonic-gate }
1017c478bd9Sstevel@tonic-gate
1027c478bd9Sstevel@tonic-gate void
tls_freemodid(ulong_t modid)10310a4fa49Srie tls_freemodid(ulong_t modid)
1047c478bd9Sstevel@tonic-gate {
1057c478bd9Sstevel@tonic-gate ulong_t i;
1067c478bd9Sstevel@tonic-gate uint_t j;
1077c478bd9Sstevel@tonic-gate
1087c478bd9Sstevel@tonic-gate i = modid / (sizeof (uint_t) * 8);
1097c478bd9Sstevel@tonic-gate /* LINTED */
1107c478bd9Sstevel@tonic-gate j = modid % (sizeof (uint_t) * 8);
1117c478bd9Sstevel@tonic-gate j = ~(1 << j);
1127c478bd9Sstevel@tonic-gate tmid.tmi_bits[i] &= j;
1137c478bd9Sstevel@tonic-gate if (modid < tmid.tmi_lowfree)
1147c478bd9Sstevel@tonic-gate tmid.tmi_lowfree = modid;
1157c478bd9Sstevel@tonic-gate }
1167c478bd9Sstevel@tonic-gate
1177c478bd9Sstevel@tonic-gate void
tls_modaddrem(Rt_map * lmp,uint_t flag)11810a4fa49Srie tls_modaddrem(Rt_map *lmp, uint_t flag)
1197c478bd9Sstevel@tonic-gate {
12010a4fa49Srie Lm_list *lml = LIST(lmp);
1217c478bd9Sstevel@tonic-gate TLS_modinfo tmi;
1227c478bd9Sstevel@tonic-gate Phdr *tlsphdr;
1237c478bd9Sstevel@tonic-gate void (*fptr)(TLS_modinfo *);
1247c478bd9Sstevel@tonic-gate
12510a4fa49Srie if (flag & TM_FLG_MODADD) {
12610a4fa49Srie fptr = (void (*)())lml->lm_lcs[CI_TLS_MODADD].lc_un.lc_func;
12710a4fa49Srie } else if (FLAGS1(lmp) & FL1_RT_TLSADD) {
12810a4fa49Srie fptr = (void (*)())lml->lm_lcs[CI_TLS_MODREM].lc_un.lc_func;
12910a4fa49Srie } else {
1307c478bd9Sstevel@tonic-gate return;
13110a4fa49Srie }
1327c478bd9Sstevel@tonic-gate
1337c478bd9Sstevel@tonic-gate tlsphdr = PTTLS(lmp);
1347c478bd9Sstevel@tonic-gate
1357c478bd9Sstevel@tonic-gate bzero(&tmi, sizeof (tmi));
1367c478bd9Sstevel@tonic-gate tmi.tm_modname = PATHNAME(lmp);
1377c478bd9Sstevel@tonic-gate tmi.tm_modid = TLSMODID(lmp);
1387c478bd9Sstevel@tonic-gate tmi.tm_tlsblock = (void *)(tlsphdr->p_vaddr);
13910a4fa49Srie
1407c478bd9Sstevel@tonic-gate if (!(FLAGS(lmp) & FLG_RT_FIXED))
1417c478bd9Sstevel@tonic-gate tmi.tm_tlsblock = (void *)((uintptr_t)tmi.tm_tlsblock +
1427c478bd9Sstevel@tonic-gate ADDR(lmp));
14310a4fa49Srie
1447c478bd9Sstevel@tonic-gate tmi.tm_filesz = tlsphdr->p_filesz;
1457c478bd9Sstevel@tonic-gate tmi.tm_memsz = tlsphdr->p_memsz;
1467c478bd9Sstevel@tonic-gate tmi.tm_flags = 0;
1477c478bd9Sstevel@tonic-gate tmi.tm_stattlsoffset = 0;
1487c478bd9Sstevel@tonic-gate
1495aefb655Srie DBG_CALL(Dbg_tls_modactivity(LIST(lmp), &tmi, flag));
15010a4fa49Srie (*fptr)(&tmi);
1517c478bd9Sstevel@tonic-gate
1527c478bd9Sstevel@tonic-gate /*
153d326b23bSrie * Tag that this link-map has registered its TLS, and, if this object
154d326b23bSrie * is being removed, free up the module id.
1557c478bd9Sstevel@tonic-gate */
15610a4fa49Srie FLAGS1(lmp) |= FL1_RT_TLSADD;
15710a4fa49Srie
1587c478bd9Sstevel@tonic-gate if (flag & TM_FLG_MODREM)
1597c478bd9Sstevel@tonic-gate tls_freemodid(TLSMODID(lmp));
1607c478bd9Sstevel@tonic-gate }
1617c478bd9Sstevel@tonic-gate
162d326b23bSrie static ulong_t tls_static_size = 0; /* static TLS buffer size */
163d326b23bSrie static ulong_t tls_static_resv = 512; /* (extra) static TLS reservation */
164d326b23bSrie
1657c478bd9Sstevel@tonic-gate /*
166d326b23bSrie * Track any static TLS use, retain the TLS header, and assign a TLS module
167d326b23bSrie * identifier.
1687c478bd9Sstevel@tonic-gate */
169d326b23bSrie int
tls_assign(Lm_list * lml,Rt_map * lmp,Phdr * phdr)170d326b23bSrie tls_assign(Lm_list *lml, Rt_map *lmp, Phdr *phdr)
171d326b23bSrie {
172d326b23bSrie ulong_t memsz = S_ROUND(phdr->p_memsz, M_TLSSTATALIGN);
173d326b23bSrie ulong_t filesz = phdr->p_filesz;
174d326b23bSrie ulong_t resv = tls_static_resv;
175d326b23bSrie
176d326b23bSrie /*
177d326b23bSrie * If this object explicitly references static TLS, then there are some
178d326b23bSrie * limitations.
179d326b23bSrie */
180d326b23bSrie if (FLAGS1(lmp) & FL1_RT_TLSSTAT) {
181d326b23bSrie /*
182d326b23bSrie * Static TLS is only available to objects on the primary
183d326b23bSrie * link-map list.
184d326b23bSrie */
185b71d513aSedp if (((lml->lm_flags & LML_FLG_BASELM) == 0) ||
186b71d513aSedp ((rtld_flags2 & RT_FL2_NOPLM) != 0)) {
187d326b23bSrie eprintf(lml, ERR_FATAL, MSG_INTL(MSG_TLS_STATBASE),
188d326b23bSrie NAME(lmp));
189d326b23bSrie return (0);
190d326b23bSrie }
191d326b23bSrie
192d326b23bSrie /*
193d326b23bSrie * All TLS blocks that are processed before thread
194d326b23bSrie * initialization, are registered with libc. This
195d326b23bSrie * initialization is carried out through a handshake with libc
196d326b23bSrie * prior to executing any user code (ie. before the first .init
197d326b23bSrie * sections are called). As part of this initialization, a
198d326b23bSrie * small backup TLS reservation is added (tls_static_resv).
199d326b23bSrie * Only explicit static TLS references that can be satisfied by
200d326b23bSrie * this TLS backup reservation can be satisfied.
201d326b23bSrie */
202d326b23bSrie if (rtld_flags2 & RT_FL2_PLMSETUP) {
203d326b23bSrie /*
204d326b23bSrie * Initialized static TLS can not be satisfied from the
205d326b23bSrie * TLS backup reservation.
206d326b23bSrie */
207d326b23bSrie if (filesz) {
208d326b23bSrie eprintf(lml, ERR_FATAL,
209d326b23bSrie MSG_INTL(MSG_TLS_STATINIT), NAME(lmp));
210d326b23bSrie return (0);
211d326b23bSrie }
212d326b23bSrie
213d326b23bSrie /*
214d326b23bSrie * Make sure the backup reservation is sufficient.
215d326b23bSrie */
216d326b23bSrie if (memsz > tls_static_resv) {
217d326b23bSrie eprintf(lml, ERR_FATAL,
218d326b23bSrie MSG_INTL(MSG_TLS_STATSIZE), NAME(lmp),
219d326b23bSrie EC_XWORD(memsz), EC_XWORD(tls_static_resv));
220d326b23bSrie return (0);
221d326b23bSrie }
222d326b23bSrie
223d326b23bSrie tls_static_resv -= memsz;
224d326b23bSrie }
225d326b23bSrie }
226d326b23bSrie
227d326b23bSrie /*
228d326b23bSrie * If we haven't yet initialized threads, or this static reservation can
229d326b23bSrie * be satisfied from the TLS backup reservation, determine the total
230d326b23bSrie * static TLS size, and assign this object a static TLS offset.
231d326b23bSrie */
232d326b23bSrie if (((rtld_flags2 & RT_FL2_PLMSETUP) == 0) ||
233d326b23bSrie (FLAGS1(lmp) & FL1_RT_TLSSTAT)) {
234d326b23bSrie tls_static_size += memsz;
2357c478bd9Sstevel@tonic-gate TLSSTATOFF(lmp) = tls_static_size;
2367c478bd9Sstevel@tonic-gate }
2377c478bd9Sstevel@tonic-gate
2387c478bd9Sstevel@tonic-gate /*
239d326b23bSrie * Retain the PT_TLS header, obtain a new module identifier, and
240d326b23bSrie * indicate that this link-map list contains a new TLS object.
2417c478bd9Sstevel@tonic-gate */
242d326b23bSrie PTTLS(lmp) = phdr;
2437c478bd9Sstevel@tonic-gate TLSMODID(lmp) = tls_getmodid();
244d326b23bSrie
245d326b23bSrie /*
246d326b23bSrie * Now that we have a TLS module id, generate any static TLS reservation
247d326b23bSrie * diagnostic.
248d326b23bSrie */
249d326b23bSrie if (resv != tls_static_resv)
250d326b23bSrie DBG_CALL(Dbg_tls_static_resv(lmp, memsz, tls_static_resv));
251d326b23bSrie
252d326b23bSrie return (++lml->lm_tls);
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate
2557c478bd9Sstevel@tonic-gate int
tls_statmod(Lm_list * lml,Rt_map * lmp)25610a4fa49Srie tls_statmod(Lm_list *lml, Rt_map *lmp)
2577c478bd9Sstevel@tonic-gate {
25810a4fa49Srie uint_t tlsmodndx, tlsmodcnt = lml->lm_tls;
25910a4fa49Srie TLS_modinfo **tlsmodlist, *tlsbuflist;
2607c478bd9Sstevel@tonic-gate Phdr *tlsphdr;
26110a4fa49Srie void (*fptr)(TLS_modinfo **, ulong_t);
2627c478bd9Sstevel@tonic-gate
26310a4fa49Srie fptr = (void (*)())lml->lm_lcs[CI_TLS_STATMOD].lc_un.lc_func;
2647c478bd9Sstevel@tonic-gate
2657c478bd9Sstevel@tonic-gate /*
2667c478bd9Sstevel@tonic-gate * Allocate a buffer to report the TLS modules, the buffer consists of:
2677c478bd9Sstevel@tonic-gate *
2687c478bd9Sstevel@tonic-gate * TLS_modinfo * ptrs[tlsmodcnt + 1]
2697c478bd9Sstevel@tonic-gate * TLS_modinfo bufs[tlsmodcnt]
2707c478bd9Sstevel@tonic-gate *
271f79d60b6Srie * The ptrs are initialized to the bufs - except the last one which
272f79d60b6Srie * null terminates the array.
273f79d60b6Srie *
274f79d60b6Srie * Note, even if no TLS has yet been observed, we still supply a
275f79d60b6Srie * TLS buffer with a single null entry. This allows us to initialize
276f79d60b6Srie * the backup TLS reservation.
2777c478bd9Sstevel@tonic-gate */
27810a4fa49Srie if ((tlsmodlist = calloc((sizeof (TLS_modinfo *) * (tlsmodcnt + 1)) +
279*56deab07SRod Evans (sizeof (TLS_modinfo) * tlsmodcnt), 1)) == NULL)
2807c478bd9Sstevel@tonic-gate return (0);
2817c478bd9Sstevel@tonic-gate
282f79d60b6Srie lml->lm_tls = 0;
283f79d60b6Srie
284f79d60b6Srie /*
285f79d60b6Srie * If we don't have any TLS modules - report that and return.
286f79d60b6Srie */
287f79d60b6Srie if (tlsmodcnt == 0) {
288f79d60b6Srie if (fptr)
289f79d60b6Srie (*fptr)(tlsmodlist, tls_static_resv);
290f79d60b6Srie DBG_CALL(Dbg_tls_static_block(&lml_main, 0, 0,
291f79d60b6Srie tls_static_resv));
292f79d60b6Srie return (1);
293f79d60b6Srie }
294f79d60b6Srie
295f79d60b6Srie /*
296f79d60b6Srie * Initialize the TLS buffer.
297f79d60b6Srie */
2987c478bd9Sstevel@tonic-gate tlsbuflist = (TLS_modinfo *)((uintptr_t)tlsmodlist +
2997c478bd9Sstevel@tonic-gate ((tlsmodcnt + 1) * sizeof (TLS_modinfo *)));
30010a4fa49Srie
3017c478bd9Sstevel@tonic-gate for (tlsmodndx = 0; tlsmodndx < tlsmodcnt; tlsmodndx++)
3027c478bd9Sstevel@tonic-gate tlsmodlist[tlsmodndx] = &tlsbuflist[tlsmodndx];
3037c478bd9Sstevel@tonic-gate
3047c478bd9Sstevel@tonic-gate /*
3057c478bd9Sstevel@tonic-gate * Account for the initial dtv ptr in the TLSSIZE calculation.
3067c478bd9Sstevel@tonic-gate */
3077c478bd9Sstevel@tonic-gate tlsmodndx = 0;
308cb511613SAli Bahrami for (lmp = lml->lm_head; lmp; lmp = NEXT_RT_MAP(lmp)) {
309*56deab07SRod Evans if (THIS_IS_NOT_ELF(lmp) ||
3107c478bd9Sstevel@tonic-gate (PTTLS(lmp) == 0) || (PTTLS(lmp)->p_memsz == 0))
3117c478bd9Sstevel@tonic-gate continue;
3127c478bd9Sstevel@tonic-gate
3137c478bd9Sstevel@tonic-gate tlsphdr = PTTLS(lmp);
3147c478bd9Sstevel@tonic-gate
3157c478bd9Sstevel@tonic-gate tlsmodlist[tlsmodndx]->tm_modname = PATHNAME(lmp);
3167c478bd9Sstevel@tonic-gate tlsmodlist[tlsmodndx]->tm_modid = TLSMODID(lmp);
31710a4fa49Srie tlsmodlist[tlsmodndx]->tm_tlsblock = (void *)(tlsphdr->p_vaddr);
31810a4fa49Srie
31910a4fa49Srie if (!(FLAGS(lmp) & FLG_RT_FIXED)) {
32010a4fa49Srie tlsmodlist[tlsmodndx]->tm_tlsblock = (void *)
32110a4fa49Srie ((uintptr_t)tlsmodlist[tlsmodndx]->tm_tlsblock +
32210a4fa49Srie ADDR(lmp));
32310a4fa49Srie }
3247c478bd9Sstevel@tonic-gate tlsmodlist[tlsmodndx]->tm_filesz = tlsphdr->p_filesz;
3257c478bd9Sstevel@tonic-gate tlsmodlist[tlsmodndx]->tm_memsz = tlsphdr->p_memsz;
3267c478bd9Sstevel@tonic-gate tlsmodlist[tlsmodndx]->tm_flags = TM_FLG_STATICTLS;
32710a4fa49Srie tlsmodlist[tlsmodndx]->tm_stattlsoffset = TLSSTATOFF(lmp);
3287c478bd9Sstevel@tonic-gate tlsmodndx++;
3297c478bd9Sstevel@tonic-gate }
3307c478bd9Sstevel@tonic-gate
3315aefb655Srie DBG_CALL(Dbg_tls_static_block(&lml_main, (void *)tlsmodlist,
332d326b23bSrie tls_static_size, tls_static_resv));
333d326b23bSrie (*fptr)(tlsmodlist, (tls_static_size + tls_static_resv));
3347c478bd9Sstevel@tonic-gate
3357c478bd9Sstevel@tonic-gate /*
3367c478bd9Sstevel@tonic-gate * We're done with the list - clean it up.
3377c478bd9Sstevel@tonic-gate */
3387c478bd9Sstevel@tonic-gate free(tlsmodlist);
3397c478bd9Sstevel@tonic-gate return (1);
3407c478bd9Sstevel@tonic-gate }
341