#include <sys/param.h>
#include <sys/tls.h>
#include <sys/mman.h>
#include <machine/tls.h>
#include <stdlib.h>
#include <string.h>
#include <elf.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include "libc_private.h"
struct tls_tcb *__libc_allocate_tls(void);
void __libc_free_tls(struct tls_tcb *tcb);
void __libc_call_init(void);
#if !defined(RTLD_STATIC_TLS_VARIANT_II)
#error "Unsupported TLS layout"
#endif
#ifndef PIC
static size_t tls_static_space;
static size_t tls_init_size;
static void *tls_init;
static struct tls_tcb *initial_tcb;
#endif
void *__libc_tls_get_addr(void *ti);
void *__libc_tls_get_addr_tcb(struct tls_tcb *, void *);
void *
__libc_tls_get_addr(void *ti __unused)
{
return (NULL);
}
void *
__libc_tls_get_addr_tcb(struct tls_tcb *tcb __unused, void *got_ptr __unused)
{
return (NULL);
}
#ifndef PIC
void
__libc_free_tls(struct tls_tcb *tcb)
{
size_t data_size;
data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
~RTLD_STATIC_TLS_ALIGN_MASK;
if (tcb == initial_tcb) {
} else {
free((char *)tcb - data_size);
}
}
struct tls_tcb *
__libc_allocate_tls(void)
{
size_t data_size;
struct tls_tcb *tcb;
Elf_Addr *dtv;
data_size = (tls_static_space + RTLD_STATIC_TLS_ALIGN_MASK) &
~RTLD_STATIC_TLS_ALIGN_MASK;
if (initial_tcb == NULL) {
size_t bytes;
bytes = data_size + sizeof(*tcb) + 3 * sizeof(*dtv);
bytes = (bytes + PAGE_MASK) & ~(size_t)PAGE_MASK;
tcb = mmap((void *)1, bytes,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1, 0);
} else {
tcb = malloc(data_size + sizeof(*tcb) + 3 * sizeof(*dtv));
}
tcb = (struct tls_tcb *)((char *)tcb + data_size);
dtv = (Elf_Addr *)(tcb + 1);
memset(tcb, 0, sizeof(*tcb));
#ifdef RTLD_TCB_HAS_SELF_POINTER
tcb->tcb_self = tcb;
#endif
tcb->tcb_dtv = dtv;
dtv[0] = 1;
dtv[1] = 1;
dtv[2] = (Elf_Addr)((char *)tcb - tls_static_space);
memcpy((char *)tcb - tls_static_space,
tls_init, tls_init_size);
memset((char *)tcb - tls_static_space + tls_init_size,
0, tls_static_space - tls_init_size);
if (initial_tcb == NULL) {
initial_tcb = tcb;
tls_set_tcb(tcb);
}
return (tcb);
}
#else
struct tls_tcb *
__libc_allocate_tls(void)
{
return (NULL);
}
void
__libc_free_tls(struct tls_tcb *tcb __unused)
{
}
#endif
void
__libc_call_init(void)
{
}
extern char **environ;
struct tls_tcb *
_libc_init_tls(void)
{
struct tls_tcb *tcb;
#ifndef PIC
Elf_Addr *sp;
Elf_Auxinfo *aux, *auxp;
Elf_Phdr *phdr;
size_t phent, phnum;
int i;
sp = (Elf_Addr *) environ;
while (*sp++ != 0)
;
aux = (Elf_Auxinfo *) sp;
phdr = NULL;
phent = phnum = 0;
for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
switch (auxp->a_type) {
case AT_PHDR:
phdr = auxp->a_un.a_ptr;
break;
case AT_PHENT:
phent = auxp->a_un.a_val;
break;
case AT_PHNUM:
phnum = auxp->a_un.a_val;
break;
}
}
if (phdr == NULL || phent != sizeof(Elf_Phdr) || phnum == 0)
return(NULL);
for (i = 0; (unsigned)i < phnum; i++) {
if (phdr[i].p_type == PT_TLS) {
tls_static_space = roundup2(phdr[i].p_memsz,
phdr[i].p_align);
tls_init_size = phdr[i].p_filesz;
tls_init = (void*) phdr[i].p_vaddr;
}
}
#endif
tcb = _libc_allocate_tls();
tls_set_tcb(tcb);
return(tcb);
}
struct tls_tcb *
_libc_allocate_tls(void)
{
struct tls_tcb *tcb;
tcb = _rtld_allocate_tls();
return(tcb);
}
__weak_reference(__libc_allocate_tls, _rtld_allocate_tls);
__weak_reference(__libc_free_tls, _rtld_free_tls);
__weak_reference(__libc_call_init, _rtld_call_init);
__weak_reference(__libc_tls_get_addr, __tls_get_addr);
__weak_reference(__libc_tls_get_addr_tcb, __tls_get_addr_tcb);
__weak_reference(_libc_init_tls, _init_tls);