#include "lint.h"
#include "thr_uberdata.h"
#define MIN_MOD_SLOTS 8
int primary_link_map = 0;
#if defined(_LP64)
#define ALIGN 16
#else
#define ALIGN 8
#endif
static TLS_modinfo *
tls_modinfo_alloc(tls_metadata_t *tlsm, ulong_t moduleid)
{
tls_t *tls_modinfo = &tlsm->tls_modinfo;
TLS_modinfo *modinfo;
size_t mod_slots;
if ((modinfo = tls_modinfo->tls_data) == NULL ||
tls_modinfo->tls_size <= moduleid) {
if ((mod_slots = tls_modinfo->tls_size) == 0)
mod_slots = MIN_MOD_SLOTS;
while (mod_slots <= moduleid)
mod_slots *= 2;
modinfo = lmalloc(mod_slots * sizeof (TLS_modinfo));
if (tls_modinfo->tls_data != NULL) {
(void) memcpy(modinfo, tls_modinfo->tls_data,
tls_modinfo->tls_size * sizeof (TLS_modinfo));
lfree(tls_modinfo->tls_data,
tls_modinfo->tls_size * sizeof (TLS_modinfo));
}
tls_modinfo->tls_data = modinfo;
tls_modinfo->tls_size = mod_slots;
}
return (modinfo);
}
void
__tls_static_mods(TLS_modinfo **tlslist, unsigned long statictlssize)
{
ulwp_t *oldself = __curthread();
tls_metadata_t *tlsm;
TLS_modinfo **tlspp;
TLS_modinfo *tlsp;
TLS_modinfo *modinfo;
caddr_t data;
caddr_t data_end;
int max_modid;
primary_link_map = 1;
if (statictlssize == 0)
return;
tlsm = &__uberdata.tls_metadata;
if (oldself != NULL) {
(void) memcpy(tlsm,
&oldself->ul_uberdata->tls_metadata, sizeof (*tlsm));
ASSERT(tlsm->static_tls.tls_data == NULL);
}
ASSERT((statictlssize & (ALIGN - 1)) == 0);
tlsm->static_tls.tls_data = data = lmalloc(statictlssize);
data_end = data + statictlssize;
tlsm->static_tls.tls_size = statictlssize;
for (max_modid = 0, tlspp = tlslist; (tlsp = *tlspp) != NULL; tlspp++) {
ASSERT(tlsp->tm_flags & TM_FLG_STATICTLS);
ASSERT(tlsp->tm_stattlsoffset > 0);
ASSERT(tlsp->tm_stattlsoffset <= statictlssize);
ASSERT((tlsp->tm_stattlsoffset & (ALIGN - 1)) == 0);
ASSERT(tlsp->tm_filesz <= tlsp->tm_memsz);
ASSERT(tlsp->tm_memsz <= tlsp->tm_stattlsoffset);
if (tlsp->tm_filesz)
(void) memcpy(data_end-tlsp->tm_stattlsoffset,
tlsp->tm_tlsblock, tlsp->tm_filesz);
if (max_modid < tlsp->tm_modid)
max_modid = tlsp->tm_modid;
}
modinfo = tls_modinfo_alloc(tlsm, max_modid);
for (tlspp = tlslist; (tlsp = *tlspp) != NULL; tlspp++)
(void) memcpy(&modinfo[tlsp->tm_modid],
tlsp, sizeof (*tlsp));
if (oldself != NULL)
(void) memcpy(&oldself->ul_uberdata->tls_metadata,
tlsm, sizeof (*tlsm));
}
void
__tls_mod_add(TLS_modinfo *tlsp)
{
tls_metadata_t *tlsm = &curthread->ul_uberdata->tls_metadata;
ulong_t moduleid = tlsp->tm_modid;
TLS_modinfo *modinfo;
lmutex_lock(&tlsm->tls_lock);
ASSERT(!(tlsp->tm_flags & TM_FLG_STATICTLS));
ASSERT(tlsp->tm_filesz <= tlsp->tm_memsz);
modinfo = tls_modinfo_alloc(tlsm, moduleid);
(void) memcpy(&modinfo[moduleid], tlsp, sizeof (*tlsp));
lmutex_unlock(&tlsm->tls_lock);
}
void
__tls_mod_remove(TLS_modinfo *tlsp)
{
tls_metadata_t *tlsm = &curthread->ul_uberdata->tls_metadata;
ulong_t moduleid = tlsp->tm_modid;
TLS_modinfo *modinfo;
lmutex_lock(&tlsm->tls_lock);
ASSERT(tlsm->tls_modinfo.tls_data != NULL &&
moduleid < tlsm->tls_modinfo.tls_size);
modinfo = tlsm->tls_modinfo.tls_data;
(void) memset(&modinfo[moduleid], 0, sizeof (TLS_modinfo));
lmutex_unlock(&tlsm->tls_lock);
}
extern int _preexec_exit_handlers();
extern void libc_init();
const Lc_interface tls_rtldinfo[] = {
{ .ci_tag = CI_VERSION, .ci_un.ci_val = CI_V_CURRENT },
{ .ci_tag = CI_ATEXIT, .ci_un.ci_func = _preexec_exit_handlers },
{ .ci_tag = CI_TLS_MODADD, .ci_un.ci_func = __tls_mod_add },
{ .ci_tag = CI_TLS_MODREM, .ci_un.ci_func = __tls_mod_remove },
{ .ci_tag = CI_TLS_STATMOD, .ci_un.ci_func = __tls_static_mods },
{ .ci_tag = CI_THRINIT, .ci_un.ci_func = libc_init },
{ .ci_tag = CI_NULL, .ci_un.ci_func = NULL }
};
void *
slow_tls_get_addr(TLS_index *tls_index)
{
ulwp_t *self = curthread;
tls_metadata_t *tlsm = &self->ul_uberdata->tls_metadata;
TLS_modinfo *tlsp;
ulong_t moduleid;
tls_t *tlsent;
caddr_t base;
void (**initarray)(void);
ulong_t arraycnt = 0;
sigoff(self);
lmutex_lock(&tlsm->tls_lock);
if ((moduleid = tls_index->ti_moduleid) < self->ul_ntlsent)
tlsent = self->ul_tlsent;
else {
ASSERT(moduleid < tlsm->tls_modinfo.tls_size);
tlsent = lmalloc(tlsm->tls_modinfo.tls_size * sizeof (tls_t));
if (self->ul_tlsent != NULL) {
(void) memcpy(tlsent, self->ul_tlsent,
self->ul_ntlsent * sizeof (tls_t));
lfree(self->ul_tlsent,
self->ul_ntlsent * sizeof (tls_t));
}
self->ul_tlsent = tlsent;
self->ul_ntlsent = tlsm->tls_modinfo.tls_size;
}
tlsent += moduleid;
if ((base = tlsent->tls_data) == NULL) {
tlsp = (TLS_modinfo *)tlsm->tls_modinfo.tls_data + moduleid;
if (tlsp->tm_memsz == 0) {
base = NULL;
} else if (tlsp->tm_flags & TM_FLG_STATICTLS) {
base = (caddr_t)self - tlsp->tm_stattlsoffset;
tlsent->tls_data = base;
tlsent->tls_size = 0;
} else {
base = lmalloc(tlsp->tm_memsz);
if (tlsp->tm_filesz != 0)
(void) memcpy(base, tlsp->tm_tlsblock,
tlsp->tm_filesz);
tlsent->tls_data = base;
tlsent->tls_size = tlsp->tm_memsz;
arraycnt = tlsp->tm_tlsinitarraycnt;
initarray = tlsp->tm_tlsinitarray;
}
}
lmutex_unlock(&tlsm->tls_lock);
if (arraycnt) {
do {
(**initarray++)();
} while (--arraycnt != 0);
}
if (base == NULL)
base = (caddr_t)self - 512;
sigon(self);
return (base + tls_index->ti_tlsoffset);
}
#ifdef TLS_GET_ADDR_IS_WRITTEN_IN_ASSEMBLER
void *
__tls_get_addr(TLS_index *tls_index)
{
ulwp_t *self = curthread;
tls_t *tlsent = self->ul_tlsent;
ulong_t moduleid;
caddr_t base;
if ((moduleid = tls_index->ti_moduleid) < self->ul_ntlsent &&
(base = tlsent[moduleid].tls_data) != NULL)
return (base + tls_index->ti_tlsoffset);
return (slow_tls_get_addr(tls_index));
}
#endif
void
tls_setup()
{
ulwp_t *self = curthread;
tls_metadata_t *tlsm = &self->ul_uberdata->tls_metadata;
TLS_modinfo *tlsp;
long moduleid;
ulong_t nmods;
if (tlsm->static_tls.tls_size == 0)
return;
(void) memcpy((caddr_t)self - tlsm->static_tls.tls_size,
tlsm->static_tls.tls_data, tlsm->static_tls.tls_size);
lmutex_lock(&tlsm->tls_lock);
nmods = tlsm->tls_modinfo.tls_size;
for (moduleid = 0; moduleid < nmods; moduleid++) {
tlsp = (TLS_modinfo *)tlsm->tls_modinfo.tls_data + moduleid;
if (tlsp->tm_tlsinitarraycnt != 0 &&
(tlsp->tm_flags & TM_FLG_STATICTLS)) {
ulong_t arraycnt = tlsp->tm_tlsinitarraycnt;
void (**initarray)(void) = tlsp->tm_tlsinitarray;
lmutex_unlock(&tlsm->tls_lock);
do {
(**initarray++)();
} while (--arraycnt != 0);
lmutex_lock(&tlsm->tls_lock);
}
}
lmutex_unlock(&tlsm->tls_lock);
}
void
tls_exit()
{
ulwp_t *self = curthread;
tls_metadata_t *tlsm = &self->ul_uberdata->tls_metadata;
tls_t *tlsent;
TLS_modinfo *tlsp;
long moduleid;
ulong_t nmods;
if (tlsm->static_tls.tls_size == 0 && self->ul_ntlsent == 0)
return;
lmutex_lock(&tlsm->tls_lock);
nmods = tlsm->tls_modinfo.tls_size;
for (moduleid = nmods - 1; moduleid >= 0; --moduleid) {
tlsp = (TLS_modinfo *)tlsm->tls_modinfo.tls_data + moduleid;
if (tlsp->tm_tlsfiniarraycnt != 0 &&
((tlsp->tm_flags & TM_FLG_STATICTLS) ||
(moduleid < self->ul_ntlsent &&
(tlsent = self->ul_tlsent) != NULL &&
tlsent[moduleid].tls_data != NULL))) {
ulong_t arraycnt = tlsp->tm_tlsfiniarraycnt;
void (**finiarray)(void) = tlsp->tm_tlsfiniarray;
lmutex_unlock(&tlsm->tls_lock);
finiarray += arraycnt;
do {
(**--finiarray)();
} while (--arraycnt != 0);
lmutex_lock(&tlsm->tls_lock);
}
}
lmutex_unlock(&tlsm->tls_lock);
tls_free(self);
}
void
tls_free(ulwp_t *ulwp)
{
ulong_t moduleid;
tls_t *tlsent;
size_t ntlsent;
void *base;
size_t size;
if ((tlsent = ulwp->ul_tlsent) == NULL ||
(ntlsent = ulwp->ul_ntlsent) == 0)
return;
for (moduleid = 0; moduleid < ntlsent; moduleid++, tlsent++) {
if ((base = tlsent->tls_data) != NULL &&
(size = tlsent->tls_size) != 0)
lfree(base, size);
tlsent->tls_data = NULL;
tlsent->tls_size = 0;
}
lfree(ulwp->ul_tlsent, ntlsent * sizeof (tls_t));
ulwp->ul_tlsent = NULL;
ulwp->ul_ntlsent = 0;
}