/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include <_rtld.h> #include <_elf.h> #include #include static ulong_t tls_static_size = 0; /* static TLS buffer size */ #define TLSBLOCKCNT 16 /* number of blocks of tmi_bits to allocate */ /* at a time. */ typedef struct { uint_t *tmi_bits; ulong_t tmi_lowfree; ulong_t tmi_cnt; } Tlsmodid; static Tlsmodid tmid = {0, 0, 0}; ulong_t tls_getmodid() { ulong_t ndx; ulong_t i; if (tmid.tmi_bits == 0) { if ((tmid.tmi_bits = calloc(TLSBLOCKCNT, sizeof (uint_t))) == 0) return ((ulong_t)-1); tmid.tmi_bits[0] = 1; tmid.tmi_lowfree = 1; tmid.tmi_cnt = TLSBLOCKCNT; return (0); } for (i = tmid.tmi_lowfree / (sizeof (uint_t) * 8); i < tmid.tmi_cnt; i++) { uint_t j; /* * If all bits are assigned - move on. */ if ((tmid.tmi_bits[i] ^ ~((uint_t)0)) == 0) continue; for (ndx = 0, j = 1; j; j = j << 1, ndx++) { if ((tmid.tmi_bits[i] & j) == 0) { tmid.tmi_bits[i] |= j; ndx = (i * (sizeof (uint_t)) * 8) + ndx; tmid.tmi_lowfree = ndx + 1; return (ndx); } } } /* * All bits taken - must allocate a new block */ if ((tmid.tmi_bits = realloc(tmid.tmi_bits, ((tmid.tmi_cnt * sizeof (uint_t)) + (TLSBLOCKCNT * sizeof (uint_t))))) == 0) return ((ulong_t)-1); /* * Clear out the tail of the new allocation. */ bzero(&(tmid.tmi_bits[tmid.tmi_cnt]), TLSBLOCKCNT * sizeof (uint_t)); tmid.tmi_bits[tmid.tmi_cnt] = 1; ndx = (tmid.tmi_cnt * sizeof (uint_t)) * 8; tmid.tmi_lowfree = ndx + 1; tmid.tmi_cnt += TLSBLOCKCNT; return (ndx); } void tls_freemodid(ulong_t modid) { ulong_t i; uint_t j; i = modid / (sizeof (uint_t) * 8); /* LINTED */ j = modid % (sizeof (uint_t) * 8); j = ~(1 << j); tmid.tmi_bits[i] &= j; if (modid < tmid.tmi_lowfree) tmid.tmi_lowfree = modid; } void tls_modaddrem(Rt_map *lmp, uint_t flag) { Lm_list *lml = LIST(lmp); TLS_modinfo tmi; Phdr *tlsphdr; void (*fptr)(TLS_modinfo *); if (flag & TM_FLG_MODADD) { fptr = (void (*)())lml->lm_lcs[CI_TLS_MODADD].lc_un.lc_func; } else if (FLAGS1(lmp) & FL1_RT_TLSADD) { fptr = (void (*)())lml->lm_lcs[CI_TLS_MODREM].lc_un.lc_func; } else { return; } tlsphdr = PTTLS(lmp); bzero(&tmi, sizeof (tmi)); tmi.tm_modname = PATHNAME(lmp); tmi.tm_modid = TLSMODID(lmp); tmi.tm_tlsblock = (void *)(tlsphdr->p_vaddr); if (!(FLAGS(lmp) & FLG_RT_FIXED)) tmi.tm_tlsblock = (void *)((uintptr_t)tmi.tm_tlsblock + ADDR(lmp)); tmi.tm_filesz = tlsphdr->p_filesz; tmi.tm_memsz = tlsphdr->p_memsz; tmi.tm_flags = 0; tmi.tm_stattlsoffset = 0; DBG_CALL(Dbg_tls_modactivity(LIST(lmp), &tmi, flag)); (*fptr)(&tmi); /* * Tag that this link-map has registered its TLS, and free up the * moduleid */ FLAGS1(lmp) |= FL1_RT_TLSADD; if (flag & TM_FLG_MODREM) tls_freemodid(TLSMODID(lmp)); } void tls_assign_soffset(Rt_map *lmp) { /* * Only objects on the primary link-map list are associated * with the STATIC tls block. */ if ((LIST(lmp)->lm_flags & LML_FLG_BASELM) && ((rtld_flags2 & RT_FL2_PLMSETUP) == 0)) { tls_static_size += S_ROUND(PTTLS(lmp)->p_memsz, M_TLSSTATALIGN); TLSSTATOFF(lmp) = tls_static_size; } /* * Everyone get's a dynamic TLS modid. */ TLSMODID(lmp) = tls_getmodid(); } int tls_statmod(Lm_list *lml, Rt_map *lmp) { uint_t tlsmodndx, tlsmodcnt = lml->lm_tls; TLS_modinfo **tlsmodlist, *tlsbuflist; Phdr *tlsphdr; void (*fptr)(TLS_modinfo **, ulong_t); fptr = (void (*)())lml->lm_lcs[CI_TLS_STATMOD].lc_un.lc_func; /* * If we don't have any TLS modules - report that and return. */ if (tlsmodcnt == 0) { if (fptr) (*fptr)(0, 0); return (1); } lml->lm_tls = 0; /* * Allocate a buffer to report the TLS modules, the buffer consists of: * * TLS_modinfo * ptrs[tlsmodcnt + 1] * TLS_modinfo bufs[tlsmodcnt] * * The ptrs are initialized to the bufs - except the last * one which null terminates the array. */ if ((tlsmodlist = calloc((sizeof (TLS_modinfo *) * (tlsmodcnt + 1)) + (sizeof (TLS_modinfo) * tlsmodcnt), 1)) == 0) return (0); tlsbuflist = (TLS_modinfo *)((uintptr_t)tlsmodlist + ((tlsmodcnt + 1) * sizeof (TLS_modinfo *))); for (tlsmodndx = 0; tlsmodndx < tlsmodcnt; tlsmodndx++) tlsmodlist[tlsmodndx] = &tlsbuflist[tlsmodndx]; /* * Account for the initial dtv ptr in the TLSSIZE calculation. */ tlsmodndx = 0; for (lmp = lml->lm_head; lmp; lmp = (Rt_map *)NEXT(lmp)) { if ((FCT(lmp) != &elf_fct) || (PTTLS(lmp) == 0) || (PTTLS(lmp)->p_memsz == 0)) continue; tlsphdr = PTTLS(lmp); tlsmodlist[tlsmodndx]->tm_modname = PATHNAME(lmp); tlsmodlist[tlsmodndx]->tm_modid = TLSMODID(lmp); tlsmodlist[tlsmodndx]->tm_tlsblock = (void *)(tlsphdr->p_vaddr); if (!(FLAGS(lmp) & FLG_RT_FIXED)) { tlsmodlist[tlsmodndx]->tm_tlsblock = (void *) ((uintptr_t)tlsmodlist[tlsmodndx]->tm_tlsblock + ADDR(lmp)); } tlsmodlist[tlsmodndx]->tm_filesz = tlsphdr->p_filesz; tlsmodlist[tlsmodndx]->tm_memsz = tlsphdr->p_memsz; tlsmodlist[tlsmodndx]->tm_flags = TM_FLG_STATICTLS; tlsmodlist[tlsmodndx]->tm_stattlsoffset = TLSSTATOFF(lmp); tlsmodndx++; } DBG_CALL(Dbg_tls_static_block(&lml_main, (void *)tlsmodlist, tls_static_size)); (*fptr)(tlsmodlist, tls_static_size); /* * We're done with the list - clean it up. */ free(tlsmodlist); return (1); }