#define _DYN_LOADER
#include <sys/types.h>
#include "syscall.h"
#include "util.h"
#include "resolve.h"
#define __set_tcb(tcb) _dl___set_tcb(tcb)
__dso_hidden void *allocate_tib(size_t);
#ifdef TIB_EXTRA_ALIGN
# define TIB_ALIGN MAXIMUM(__alignof__(struct tib), TIB_EXTRA_ALIGN)
#else
# define TIB_ALIGN __alignof__(struct tib)
#endif
static int static_tls_size;
static int static_tls_align;
static int static_tls_align_offset;
int _dl_tib_static_done;
void *
allocate_tib(size_t extra)
{
char *base;
struct tib *tib;
char *thread = NULL;
struct elf_object *obj;
#if TLS_VARIANT == 1
size_t unpad_extra = (extra <= static_tls_align_offset) ? 0 :
ELF_ROUND(extra - static_tls_align_offset, static_tls_align);
base = _dl_aligned_alloc(static_tls_align, unpad_extra +
static_tls_align_offset + sizeof *tib + static_tls_size);
if (base == NULL)
return NULL;
tib = (struct tib *)(base + unpad_extra + static_tls_align_offset);
if (extra)
thread = base;
#define TLS_ADDR(tibp, offset) ((char *)(tibp) + sizeof(struct tib) + (offset))
#elif TLS_VARIANT == 2
base = _dl_aligned_alloc(static_tls_align, static_tls_size +
static_tls_align_offset + ELF_ROUND(sizeof *tib, TIB_EXTRA_ALIGN) +
extra);
if (base == NULL)
return NULL;
base += static_tls_align_offset;
tib = (struct tib *)(base + static_tls_size);
if (extra)
thread = (char *)tib + ELF_ROUND(sizeof *tib, TIB_EXTRA_ALIGN);
#define TLS_ADDR(tibp, offset) ((char *)(tibp) - (offset))
#endif
for (obj = _dl_objects; obj != NULL; obj = obj->next) {
if (obj->tls_msize != 0) {
char *addr = TLS_ADDR(tib, obj->tls_offset);
_dl_memset(addr + obj->tls_fsize, 0,
obj->tls_msize - obj->tls_fsize);
if (obj->tls_static_data != NULL)
_dl_bcopy(obj->tls_static_data, addr,
obj->tls_fsize);
DL_DEB(("\t%s has index %u addr %p msize %u fsize %u\n",
obj->load_name, obj->tls_offset,
(void *)addr, obj->tls_msize, obj->tls_fsize));
}
}
TIB_INIT(tib, NULL, thread);
DL_DEB(("tib new=%p\n", (void *)tib));
return (tib);
}
__strong_alias(_dl_allocate_tib, allocate_tib);
void
_dl_free_tib(void *tib, size_t extra)
{
size_t tib_offset;
#if TLS_VARIANT == 1
tib_offset = (extra <= static_tls_align_offset) ? 0 :
ELF_ROUND(extra - static_tls_align_offset, static_tls_align);
#elif TLS_VARIANT == 2
tib_offset = static_tls_size;
#endif
tib_offset += static_tls_align_offset;
DL_DEB(("free tib=%p\n", (void *)tib));
_dl_free((char *)tib - tib_offset);
}
void
_dl_set_tls(elf_object_t *object, Elf_Phdr *ptls, Elf_Addr libaddr,
const char *libname)
{
if (ptls->p_vaddr != 0 && ptls->p_filesz != 0)
object->tls_static_data = (void *)(ptls->p_vaddr + libaddr);
object->tls_fsize = ptls->p_filesz;
object->tls_msize = ptls->p_memsz;
object->tls_align = ptls->p_align;
DL_DEB(("tls %x %x %x %x\n",
object->tls_static_data, object->tls_fsize, object->tls_msize,
object->tls_align));
}
static inline Elf_Addr
allocate_tls_offset(Elf_Addr msize, Elf_Addr align, int for_exe)
{
Elf_Addr offset;
if (for_exe && static_tls_size != 0)
_dl_die("TLS allocation before executable!");
#if TLS_VARIANT == 1
if (for_exe) {
static_tls_align = MAXIMUM(align, TIB_ALIGN);
static_tls_align_offset =
ELF_ROUND(sizeof(struct tib), static_tls_align) -
sizeof(struct tib);
offset = 0;
static_tls_size = msize;
} else {
if (static_tls_align < align) {
static_tls_align_offset += align - static_tls_align;
static_tls_align = align;
}
offset = static_tls_align_offset + sizeof(struct tib) +
static_tls_size;
offset = ELF_ROUND(offset, align) - static_tls_align_offset
- sizeof(struct tib);
static_tls_size = offset + msize;
}
#elif TLS_VARIANT == 2
if (static_tls_align < align)
static_tls_align = align;
static_tls_size = ELF_ROUND(static_tls_size + msize, align);
offset = static_tls_size;
#else
# error "unknown TLS_VARIANT"
#endif
return offset;
}
void
_dl_allocate_tls_offsets(void)
{
struct elf_object *obj;
static_tls_align = TIB_ALIGN;
for (obj = _dl_objects; obj != NULL; obj = obj->next) {
if (obj->tls_msize != 0) {
obj->tls_offset = allocate_tls_offset(obj->tls_msize,
obj->tls_align, obj->obj_type == OBJTYPE_EXE);
}
}
#if TLS_VARIANT == 2
static_tls_align_offset = ELF_ROUND(static_tls_size, static_tls_align)
- static_tls_size;
#endif
_dl_tib_static_done = 1;
DL_DEB(("static tls size=%x align=%x offset=%x\n",
static_tls_size, static_tls_align, static_tls_align_offset));
}
void
_dl_allocate_first_tib(void)
{
struct tib *tib;
tib = allocate_tib(0);
tib->tib_tid = _dl_getthrid();
TCB_SET(TIB_TO_TCB(tib));
}