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 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 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 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 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 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