summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libexec/rtld-elf/alpha/reloc.c22
-rw-r--r--libexec/rtld-elf/alpha/rtld_machdep.h15
-rw-r--r--libexec/rtld-elf/amd64/reloc.c130
-rw-r--r--libexec/rtld-elf/amd64/rtld_machdep.h15
-rw-r--r--libexec/rtld-elf/i386/reloc.c114
-rw-r--r--libexec/rtld-elf/i386/rtld_machdep.h16
-rw-r--r--libexec/rtld-elf/ia64/reloc.c77
-rw-r--r--libexec/rtld-elf/ia64/rtld_machdep.h10
-rw-r--r--libexec/rtld-elf/map_object.c16
-rw-r--r--libexec/rtld-elf/rtld.c342
-rw-r--r--libexec/rtld-elf/rtld.h23
-rw-r--r--libexec/rtld-elf/rtld_tls.h69
-rw-r--r--libexec/rtld-elf/sparc64/reloc.c21
-rw-r--r--libexec/rtld-elf/sparc64/rtld_machdep.h15
14 files changed, 883 insertions, 2 deletions
diff --git a/libexec/rtld-elf/alpha/reloc.c b/libexec/rtld-elf/alpha/reloc.c
index 535419c..1ec66ca 100644
--- a/libexec/rtld-elf/alpha/reloc.c
+++ b/libexec/rtld-elf/alpha/reloc.c
@@ -505,3 +505,25 @@ init_pltgot(Obj_Entry *obj)
obj->pltgot[3] = (Elf_Addr) obj;
}
}
+
+void
+allocate_initial_tls(Obj_Entry *list)
+{
+ void *tls;
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+ tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA;
+ tls = allocate_tls(list, 0, 16, 16);
+ alpha_pal_wrunique((u_int64_t) tls);
+}
+
+void *__tls_get_addr(tls_index* ti)
+{
+ Elf_Addr** tp = (Elf_Addr**) alpha_pal_rdunique();
+
+ return tls_get_addr_common(tp, ti->ti_module, ti->ti_offset);
+}
diff --git a/libexec/rtld-elf/alpha/rtld_machdep.h b/libexec/rtld-elf/alpha/rtld_machdep.h
index 008abce..397139e 100644
--- a/libexec/rtld-elf/alpha/rtld_machdep.h
+++ b/libexec/rtld-elf/alpha/rtld_machdep.h
@@ -60,4 +60,19 @@ Elf_Addr reloc_jmpslot(Elf_Addr *, Elf_Addr,
/* Lazy binding entry point, called via PLT. */
void _rtld_bind_start_old(void);
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(16, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round(prev_offset + prev_size, align)
+#define calculate_tls_end(off, size) ((off) + (size))
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *__tls_get_addr(tls_index *ti);
+
#endif
diff --git a/libexec/rtld-elf/amd64/reloc.c b/libexec/rtld-elf/amd64/reloc.c
index dcb492c..985b481 100644
--- a/libexec/rtld-elf/amd64/reloc.c
+++ b/libexec/rtld-elf/amd64/reloc.c
@@ -33,6 +33,7 @@
#include <sys/param.h>
#include <sys/mman.h>
+#include <machine/sysarch.h>
#include <dlfcn.h>
#include <err.h>
@@ -199,6 +200,111 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
}
break;
+ case R_X86_64_TPOFF64:
+ {
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ false, cache);
+ if (def == NULL)
+ goto done;
+
+ /*
+ * We lazily allocate offsets for static TLS as we
+ * see the first relocation that references the
+ * TLS block. This allows us to support (small
+ * amounts of) static TLS in dynamically loaded
+ * modules. If we run out of space, we generate an
+ * error.
+ */
+ if (!defobj->tls_done) {
+ if (!allocate_tls_offset((Obj_Entry*) defobj)) {
+ _rtld_error("%s: No space available for static "
+ "Thread Local Storage", obj->path);
+ goto done;
+ }
+ }
+
+ *where = (Elf_Addr) (def->st_value - defobj->tlsoffset +
+ rela->r_addend);
+ }
+ break;
+
+ case R_X86_64_TPOFF32:
+ {
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ false, cache);
+ if (def == NULL)
+ goto done;
+
+ /*
+ * We lazily allocate offsets for static TLS as we
+ * see the first relocation that references the
+ * TLS block. This allows us to support (small
+ * amounts of) static TLS in dynamically loaded
+ * modules. If we run out of space, we generate an
+ * error.
+ */
+ if (!defobj->tls_done) {
+ if (!allocate_tls_offset((Obj_Entry*) defobj)) {
+ _rtld_error("%s: No space available for static "
+ "Thread Local Storage", obj->path);
+ goto done;
+ }
+ }
+
+ *where32 = (Elf32_Addr) (def->st_value -
+ defobj->tlsoffset +
+ rela->r_addend);
+ }
+ break;
+
+ case R_X86_64_DTPMOD64:
+ {
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ false, cache);
+ if (def == NULL)
+ goto done;
+
+ *where += (Elf_Addr) defobj->tlsindex;
+ }
+ break;
+
+ case R_X86_64_DTPOFF64:
+ {
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ false, cache);
+ if (def == NULL)
+ goto done;
+
+ *where += (Elf_Addr) (def->st_value + rela->r_addend);
+ }
+ break;
+
+ case R_X86_64_DTPOFF32:
+ {
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ false, cache);
+ if (def == NULL)
+ goto done;
+
+ *where32 += (Elf32_Addr) (def->st_value + rela->r_addend);
+ }
+ break;
+
case R_X86_64_RELATIVE:
*where = (Elf_Addr)(obj->relocbase + rela->r_addend);
break;
@@ -265,3 +371,27 @@ reloc_jmpslots(Obj_Entry *obj)
obj->jmpslots_done = true;
return 0;
}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+ tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA;
+ amd64_set_fsbase(allocate_tls(objs, 0,
+ 2*sizeof(Elf_Addr), sizeof(Elf_Addr)));
+}
+
+void *__tls_get_addr(tls_index *ti)
+{
+ Elf_Addr** segbase;
+ Elf_Addr* dtv;
+
+ __asm __volatile("movq %%fs:0, %0" : "=r" (segbase));
+ dtv = segbase[1];
+
+ return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset);
+}
diff --git a/libexec/rtld-elf/amd64/rtld_machdep.h b/libexec/rtld-elf/amd64/rtld_machdep.h
index 0fb431b..57bd0de 100644
--- a/libexec/rtld-elf/amd64/rtld_machdep.h
+++ b/libexec/rtld-elf/amd64/rtld_machdep.h
@@ -58,4 +58,19 @@ reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
#define call_initfini_pointer(obj, target) \
(((InitFunc)(target))())
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(size, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round((prev_offset) + (size), align)
+#define calculate_tls_end(off, size) (off)
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *__tls_get_addr(tls_index *ti);
+
#endif
diff --git a/libexec/rtld-elf/i386/reloc.c b/libexec/rtld-elf/i386/reloc.c
index 6778574..b02fdca 100644
--- a/libexec/rtld-elf/i386/reloc.c
+++ b/libexec/rtld-elf/i386/reloc.c
@@ -33,6 +33,8 @@
#include <sys/param.h>
#include <sys/mman.h>
+#include <machine/segments.h>
+#include <machine/sysarch.h>
#include <dlfcn.h>
#include <err.h>
@@ -202,6 +204,64 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
*where += (Elf_Addr) obj->relocbase;
break;
+ case R_386_TLS_TPOFF:
+ {
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+ false, cache);
+ if (def == NULL)
+ goto done;
+
+ /*
+ * We lazily allocate offsets for static TLS as we
+ * see the first relocation that references the
+ * TLS block. This allows us to support (small
+ * amounts of) static TLS in dynamically loaded
+ * modules. If we run out of space, we generate an
+ * error.
+ */
+ if (!defobj->tls_done) {
+ if (!allocate_tls_offset((Obj_Entry*) defobj)) {
+ _rtld_error("%s: No space available for static "
+ "Thread Local Storage", obj->path);
+ goto done;
+ }
+ }
+
+ *where += (Elf_Addr) (def->st_value - defobj->tlsoffset);
+ }
+ break;
+
+ case R_386_TLS_DTPMOD32:
+ {
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+ false, cache);
+ if (def == NULL)
+ goto done;
+
+ *where += (Elf_Addr) defobj->tlsindex;
+ }
+ break;
+
+ case R_386_TLS_DTPOFF32:
+ {
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+ false, cache);
+ if (def == NULL)
+ goto done;
+
+ *where += (Elf_Addr) def->st_value;
+ }
+ break;
+
default:
_rtld_error("%s: Unsupported relocation type %d"
" in non-PLT relocations\n", obj->path,
@@ -262,3 +322,57 @@ reloc_jmpslots(Obj_Entry *obj)
obj->jmpslots_done = true;
return 0;
}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+ void* tls;
+ union descriptor ldt;
+ int sel;
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+ tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA;
+ tls = allocate_tls(objs, NULL, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
+
+ memset(&ldt, 0, sizeof(ldt));
+ ldt.sd.sd_lolimit = 0xffff; /* 4G limit */
+ ldt.sd.sd_lobase = ((Elf_Addr)tls) & 0xffffff;
+ ldt.sd.sd_type = SDT_MEMRWA;
+ ldt.sd.sd_dpl = SEL_UPL;
+ ldt.sd.sd_p = 1; /* present */
+ ldt.sd.sd_hilimit = 0xf; /* 4G limit */
+ ldt.sd.sd_def32 = 1; /* 32 bit */
+ ldt.sd.sd_gran = 1; /* limit in pages */
+ ldt.sd.sd_hibase = (((Elf_Addr)tls) >> 24) & 0xff;
+ sel = i386_set_ldt(LDT_AUTO_ALLOC, &ldt, 1);
+ __asm __volatile("movl %0,%%gs" : : "rm" ((sel << 3) | 7));
+}
+
+/* GNU ABI */
+__attribute__((__regparm__(1)))
+void *___tls_get_addr(tls_index *ti)
+{
+ Elf_Addr** segbase;
+ Elf_Addr* dtv;
+
+ __asm __volatile("movl %%gs:0, %0" : "=r" (segbase));
+ dtv = segbase[1];
+
+ return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset);
+}
+
+/* Sun ABI */
+void *__tls_get_addr(tls_index *ti)
+{
+ Elf_Addr** segbase;
+ Elf_Addr* dtv;
+
+ __asm __volatile("movl %%gs:0, %0" : "=r" (segbase));
+ dtv = segbase[1];
+
+ return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset);
+}
diff --git a/libexec/rtld-elf/i386/rtld_machdep.h b/libexec/rtld-elf/i386/rtld_machdep.h
index 953e289..e104f86 100644
--- a/libexec/rtld-elf/i386/rtld_machdep.h
+++ b/libexec/rtld-elf/i386/rtld_machdep.h
@@ -58,4 +58,20 @@ reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
#define call_initfini_pointer(obj, target) \
(((InitFunc)(target))())
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(size, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round((prev_offset) + (size), align)
+#define calculate_tls_end(off, size) (off)
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *___tls_get_addr(tls_index *ti) __attribute__((__regparm__(1)));
+extern void *__tls_get_addr(tls_index *ti);
+
#endif
diff --git a/libexec/rtld-elf/ia64/reloc.c b/libexec/rtld-elf/ia64/reloc.c
index ca68ca0..d718293 100644
--- a/libexec/rtld-elf/ia64/reloc.c
+++ b/libexec/rtld-elf/ia64/reloc.c
@@ -259,6 +259,61 @@ reloc_non_plt_obj(Obj_Entry *obj_rtld, Obj_Entry *obj, const Elf_Rela *rela,
break;
}
+ case R_IA64_DTPMOD64LSB: {
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ false, cache);
+ if (def == NULL)
+ return -1;
+
+ store64(where, defobj->tlsindex);
+ break;
+ }
+
+ case R_IA64_DTPREL64LSB: {
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ false, cache);
+ if (def == NULL)
+ return -1;
+
+ store64(where, def->st_value + rela->r_addend);
+ break;
+ }
+
+ case R_IA64_TPREL64LSB: {
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ false, cache);
+ if (def == NULL)
+ return -1;
+
+ /*
+ * We lazily allocate offsets for static TLS as we
+ * see the first relocation that references the
+ * TLS block. This allows us to support (small
+ * amounts of) static TLS in dynamically loaded
+ * modules. If we run out of space, we generate an
+ * error.
+ */
+ if (!defobj->tls_done) {
+ if (!allocate_tls_offset((Obj_Entry*) defobj)) {
+ _rtld_error("%s: No space available for static "
+ "Thread Local Storage", obj->path);
+ return -1;
+ }
+ }
+
+ store64(where, defobj->tlsoffset + def->st_value + rela->r_addend);
+ break;
+ }
+
case R_IA64_NONE:
break;
@@ -535,3 +590,25 @@ init_pltgot(Obj_Entry *obj)
pltres[1] = FPTR_TARGET(_rtld_bind_start);
pltres[2] = FPTR_GP(_rtld_bind_start);
}
+
+void
+allocate_initial_tls(Obj_Entry *list)
+{
+ register Elf_Addr** tp __asm__("r13");
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+ tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA;
+
+ tp = allocate_tls(list, 0, 16, 16);
+}
+
+void *__tls_get_addr(unsigned long module, unsigned long offset)
+{
+ register Elf_Addr** tp __asm__("r13");
+
+ return tls_get_addr_common(tp, module, offset);
+}
diff --git a/libexec/rtld-elf/ia64/rtld_machdep.h b/libexec/rtld-elf/ia64/rtld_machdep.h
index bf1261e..21c3a8f 100644
--- a/libexec/rtld-elf/ia64/rtld_machdep.h
+++ b/libexec/rtld-elf/ia64/rtld_machdep.h
@@ -55,4 +55,14 @@ Elf_Addr reloc_jmpslot(Elf_Addr *, Elf_Addr, const struct Struct_Obj_Entry *,
void *make_function_pointer(const Elf_Sym *, const struct Struct_Obj_Entry *);
void call_initfini_pointer(const struct Struct_Obj_Entry *, Elf_Addr);
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(16, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round(prev_offset + prev_size, align)
+#define calculate_tls_end(off, size) ((off) + (size))
+
+extern void *__tls_get_addr(unsigned long module, unsigned long offset);
+
#endif
diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c
index 7ad9dc5..33fb9a6 100644
--- a/libexec/rtld-elf/map_object.c
+++ b/libexec/rtld-elf/map_object.c
@@ -63,6 +63,7 @@ map_object(int fd, const char *path, const struct stat *sb)
Elf_Phdr *phdyn;
Elf_Phdr *phphdr;
Elf_Phdr *phinterp;
+ Elf_Phdr *phtls;
caddr_t mapbase;
size_t mapsize;
Elf_Off base_offset;
@@ -96,7 +97,7 @@ map_object(int fd, const char *path, const struct stat *sb)
phdr = (Elf_Phdr *) ((char *)hdr + hdr->e_phoff);
phlimit = phdr + hdr->e_phnum;
nsegs = -1;
- phdyn = phphdr = phinterp = NULL;
+ phdyn = phphdr = phinterp = phtls = NULL;
segs = alloca(sizeof(segs[0]) * hdr->e_phnum);
while (phdr < phlimit) {
switch (phdr->p_type) {
@@ -121,6 +122,10 @@ map_object(int fd, const char *path, const struct stat *sb)
case PT_DYNAMIC:
phdyn = phdr;
break;
+
+ case PT_TLS:
+ phtls = phdr;
+ break;
}
++phdr;
@@ -228,7 +233,14 @@ map_object(int fd, const char *path, const struct stat *sb)
}
if (phinterp != NULL)
obj->interp = (const char *) (obj->relocbase + phinterp->p_vaddr);
-
+ if (phtls != NULL) {
+ tls_dtv_generation++;
+ obj->tlsindex = ++tls_max_index;
+ obj->tlssize = phtls->p_memsz;
+ obj->tlsalign = phtls->p_align;
+ obj->tlsinitsize = phtls->p_filesz;
+ obj->tlsinit = mapbase + phtls->p_vaddr;
+ }
return obj;
}
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 62ca650..97ddf57 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -53,6 +53,7 @@
#include "debug.h"
#include "rtld.h"
#include "libmap.h"
+#include "rtld_tls.h"
#ifndef COMPAT_32BIT
#define PATH_RTLD "/libexec/ld-elf.so.1"
@@ -181,6 +182,12 @@ static func_ptr_type exports[] = {
(func_ptr_type) &dllockinit,
(func_ptr_type) &dlinfo,
(func_ptr_type) &_rtld_thread_init,
+#ifdef __i386__
+ (func_ptr_type) &___tls_get_addr,
+#endif
+ (func_ptr_type) &__tls_get_addr,
+ (func_ptr_type) &_rtld_allocate_tls,
+ (func_ptr_type) &_rtld_free_tls,
NULL
};
@@ -192,6 +199,15 @@ char *__progname;
char **environ;
/*
+ * Globals to control TLS allocation.
+ */
+size_t tls_last_offset; /* Static TLS offset of last module */
+size_t tls_last_size; /* Static TLS size of last module */
+size_t tls_static_space; /* Static TLS space allocated */
+int tls_dtv_generation = 1; /* Used to detect when dtv size changes */
+int tls_max_index = 1; /* Largest module index allocated */
+
+/*
* Fill in a DoneList with an allocation large enough to hold all of
* the currently-loaded objects. Keep this as a macro since it calls
* alloca and we want that to occur within the scope of the caller.
@@ -229,6 +245,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
Elf_Auxinfo *aux;
Elf_Auxinfo *auxp;
const char *argv0;
+ Objlist_Entry *entry;
Obj_Entry *obj;
Obj_Entry **preload_tail;
Objlist initlist;
@@ -393,6 +410,17 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
dbg("initializing thread locks");
lockdflt_init();
+ /* setup TLS for main thread */
+ dbg("initializing initial thread local storage");
+ STAILQ_FOREACH(entry, &list_main, link) {
+ /*
+ * Allocate all the initial objects out of the static TLS
+ * block even if they didn't ask for it.
+ */
+ allocate_tls_offset(entry->obj);
+ }
+ allocate_initial_tls(obj_list);
+
/* Make a list of init functions to call. */
objlist_init(&initlist);
initlist_add_objects(obj_list, preload_tail, &initlist);
@@ -729,6 +757,14 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path)
case PT_DYNAMIC:
obj->dynamic = (const Elf_Dyn *) ph->p_vaddr;
break;
+
+ case PT_TLS:
+ obj->tlsindex = 1;
+ obj->tlssize = ph->p_memsz;
+ obj->tlsalign = ph->p_align;
+ obj->tlsinitsize = ph->p_filesz;
+ obj->tlsinit = (void*) ph->p_vaddr;
+ break;
}
}
if (nsegs < 1) {
@@ -2441,4 +2477,310 @@ unref_dag(Obj_Entry *root)
elm->obj->refcount--;
}
+/*
+ * Common code for MD __tls_get_addr().
+ */
+void *
+tls_get_addr_common(Elf_Addr** dtvp, int index, size_t offset)
+{
+ Elf_Addr* dtv = *dtvp;
+
+ /* Check dtv generation in case new modules have arrived */
+ if (dtv[0] != tls_dtv_generation) {
+ Elf_Addr* newdtv;
+ int to_copy;
+
+ newdtv = calloc(1, (tls_max_index + 2) * sizeof(Elf_Addr));
+ to_copy = dtv[1];
+ if (to_copy > tls_max_index)
+ to_copy = tls_max_index;
+ memcpy(&newdtv[2], &dtv[2], to_copy * sizeof(Elf_Addr));
+ newdtv[0] = tls_dtv_generation;
+ newdtv[1] = tls_max_index;
+ free(dtv);
+ *dtvp = newdtv;
+ }
+
+ /* Dynamically allocate module TLS if necessary */
+ if (!dtv[index + 1])
+ dtv[index + 1] = (Elf_Addr)allocate_module_tls(index);
+
+ return (void*) (dtv[index + 1] + offset);
+}
+
+/* XXX not sure what variants to use for arm and powerpc. */
+
+#if defined(__ia64__) || defined(__alpha__)
+
+/*
+ * Allocate Static TLS using the Variant I method.
+ */
+void *
+allocate_tls(Obj_Entry *objs, void *oldtls, size_t tcbsize, size_t tcbalign)
+{
+ Obj_Entry *obj;
+ size_t size;
+ char *tls;
+ Elf_Addr *dtv, *olddtv;
+ Elf_Addr addr;
+ int i;
+
+ assert(tcbsize == 16);
+ assert(tcbalign == 16);
+
+ size = tls_static_space;
+
+ tls = malloc(size);
+ dtv = malloc((tls_max_index + 2) * sizeof(Elf_Addr));
+
+ *(Elf_Addr**) tls = dtv;
+
+ dtv[0] = tls_dtv_generation;
+ dtv[1] = tls_max_index;
+
+ if (oldtls) {
+ /*
+ * Copy the static TLS block over whole.
+ */
+ memcpy(tls + tcbsize, oldtls + tcbsize, tls_static_space - tcbsize);
+
+ /*
+ * If any dynamic TLS blocks have been created tls_get_addr(),
+ * move them over.
+ */
+ olddtv = *(Elf_Addr**) oldtls;
+ for (i = 0; i < olddtv[1]; i++) {
+ if (olddtv[i+2] < (Elf_Addr)oldtls ||
+ olddtv[i+2] > (Elf_Addr)oldtls + tls_static_space) {
+ dtv[i+2] = olddtv[i+2];
+ olddtv[i+2] = 0;
+ }
+ }
+
+ /*
+ * We assume that all tls blocks are allocated with the same
+ * size and alignment.
+ */
+ free_tls(oldtls, tcbsize, tcbalign);
+ } else {
+ for (obj = objs; obj; obj = obj->next) {
+ if (obj->tlsoffset) {
+ addr = (Elf_Addr)tls + obj->tlsoffset;
+ memset((void*) (addr + obj->tlsinitsize),
+ 0, obj->tlssize - obj->tlsinitsize);
+ if (obj->tlsinit)
+ memcpy((void*) addr, obj->tlsinit,
+ obj->tlsinitsize);
+ dtv[obj->tlsindex + 1] = addr;
+ } else if (obj->tlsindex) {
+ dtv[obj->tlsindex + 1] = 0;
+ }
+ }
+ }
+
+ return tls;
+}
+
+void
+free_tls(void *tls, size_t tcbsize, size_t tcbalign)
+{
+ size_t size;
+ Elf_Addr* dtv;
+ int dtvsize, i;
+ Elf_Addr tlsstart, tlsend;
+
+ /*
+ * Figure out the size of the initial TLS block so that we can
+ * find stuff which __tls_get_addr() allocated dynamically.
+ */
+ size = tls_static_space;
+
+ dtv = ((Elf_Addr**)tls)[0];
+ dtvsize = dtv[1];
+ tlsstart = (Elf_Addr) tls;
+ tlsend = tlsstart + size;
+ for (i = 0; i < dtvsize; i++) {
+ if (dtv[i+2] < tlsstart || dtv[i+2] > tlsend) {
+ free((void*) dtv[i+2]);
+ }
+ }
+
+ free((void*) tlsstart);
+}
+
+#endif
+
+#if defined(__i386__) || defined(__amd64__) || defined(__sparc64__)
+
+/*
+ * Allocate Static TLS using the Variant II method.
+ */
+void *
+allocate_tls(Obj_Entry *objs, void *oldtls, size_t tcbsize, size_t tcbalign)
+{
+ Obj_Entry *obj;
+ size_t size;
+ char *tls;
+ Elf_Addr *dtv, *olddtv;
+ Elf_Addr segbase, oldsegbase, addr;
+ int i;
+
+ size = round(tls_static_space, tcbalign);
+
+ assert(tcbsize >= 2*sizeof(Elf_Addr));
+ tls = malloc(size + tcbsize);
+ dtv = malloc((tls_max_index + 2) * sizeof(Elf_Addr));
+
+ segbase = (Elf_Addr)(tls + size);
+ ((Elf_Addr*)segbase)[0] = segbase;
+ ((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv;
+
+ dtv[0] = tls_dtv_generation;
+ dtv[1] = tls_max_index;
+
+ if (oldtls) {
+ /*
+ * Copy the static TLS block over whole.
+ */
+ oldsegbase = (Elf_Addr) oldtls;
+ memcpy((void *)(segbase - tls_static_space),
+ (const void *)(oldsegbase - tls_static_space),
+ tls_static_space);
+
+ /*
+ * If any dynamic TLS blocks have been created tls_get_addr(),
+ * move them over.
+ */
+ olddtv = ((Elf_Addr**)oldsegbase)[1];
+ for (i = 0; i < olddtv[1]; i++) {
+ if (olddtv[i+2] < oldsegbase - size || olddtv[i+2] > oldsegbase) {
+ dtv[i+2] = olddtv[i+2];
+ olddtv[i+2] = 0;
+ }
+ }
+
+ /*
+ * We assume that this block was the one we created with
+ * allocate_initial_tls().
+ */
+ free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
+ } else {
+ for (obj = objs; obj; obj = obj->next) {
+ if (obj->tlsoffset) {
+ addr = segbase - obj->tlsoffset;
+ memset((void*) (addr + obj->tlsinitsize),
+ 0, obj->tlssize - obj->tlsinitsize);
+ if (obj->tlsinit)
+ memcpy((void*) addr, obj->tlsinit, obj->tlsinitsize);
+ dtv[obj->tlsindex + 1] = addr;
+ } else if (obj->tlsindex) {
+ dtv[obj->tlsindex + 1] = 0;
+ }
+ }
+ }
+
+ return (void*) segbase;
+}
+
+void
+free_tls(void *tls, size_t tcbsize, size_t tcbalign)
+{
+ size_t size;
+ Elf_Addr* dtv;
+ int dtvsize, i;
+ Elf_Addr tlsstart, tlsend;
+
+ /*
+ * Figure out the size of the initial TLS block so that we can
+ * find stuff which ___tls_get_addr() allocated dynamically.
+ */
+ size = round(tls_static_space, tcbalign);
+
+ dtv = ((Elf_Addr**)tls)[1];
+ dtvsize = dtv[1];
+ tlsend = (Elf_Addr) tls;
+ tlsstart = tlsend - size;
+ for (i = 0; i < dtvsize; i++) {
+ if (dtv[i+2] < tlsstart || dtv[i+2] > tlsend) {
+ free((void*) dtv[i+2]);
+ }
+ }
+
+ free((void*) tlsstart);
+}
+
+#endif
+/*
+ * Allocate TLS block for module with given index.
+ */
+void *
+allocate_module_tls(int index)
+{
+ Obj_Entry* obj;
+ char* p;
+
+ for (obj = obj_list; obj; obj = obj->next) {
+ if (obj->tlsindex == index)
+ break;
+ }
+ if (!obj) {
+ _rtld_error("Can't find module with TLS index %d", index);
+ die();
+ }
+
+ p = malloc(obj->tlssize);
+ memcpy(p, obj->tlsinit, obj->tlsinitsize);
+ memset(p + obj->tlsinitsize, 0, obj->tlssize - obj->tlsinitsize);
+
+ return p;
+}
+
+bool
+allocate_tls_offset(Obj_Entry *obj)
+{
+ size_t off;
+
+ if (obj->tls_done)
+ return true;
+
+ if (obj->tlssize == 0) {
+ obj->tls_done = true;
+ return true;
+ }
+
+ if (obj->tlsindex == 1)
+ off = calculate_first_tls_offset(obj->tlssize, obj->tlsalign);
+ else
+ off = calculate_tls_offset(tls_last_offset, tls_last_size,
+ obj->tlssize, obj->tlsalign);
+
+ /*
+ * If we have already fixed the size of the static TLS block, we
+ * must stay within that size. When allocating the static TLS, we
+ * leave a small amount of space spare to be used for dynamically
+ * loading modules which use static TLS.
+ */
+ if (tls_static_space) {
+ if (calculate_tls_end(off, obj->tlssize) > tls_static_space)
+ return false;
+ }
+
+ tls_last_offset = obj->tlsoffset = off;
+ tls_last_size = obj->tlssize;
+ obj->tls_done = true;
+
+ return true;
+}
+
+void *
+_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
+{
+ return allocate_tls(obj_list, oldtls, tcbsize, tcbalign);
+}
+
+void
+_rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
+{
+ free_tls(tcb, tcbsize, tcbalign);
+}
diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
index 0a8dfe1..69964d6 100644
--- a/libexec/rtld-elf/rtld.h
+++ b/libexec/rtld-elf/rtld.h
@@ -63,6 +63,12 @@ typedef unsigned char bool;
#define false 0
#define true 1
+extern size_t tls_last_offset;
+extern size_t tls_last_size;
+extern size_t tls_static_space;
+extern int tls_dtv_generation;
+extern int tls_max_index;
+
struct stat;
struct Struct_Obj_Entry;
@@ -137,6 +143,14 @@ typedef struct Struct_Obj_Entry {
size_t phsize; /* Size of program header in bytes */
const char *interp; /* Pathname of the interpreter, if any */
+ /* TLS information */
+ int tlsindex; /* Index in DTV for this module */
+ void *tlsinit; /* Base address of TLS init block */
+ size_t tlsinitsize; /* Size of TLS init block for this module */
+ size_t tlssize; /* Size of TLS block for this module */
+ size_t tlsoffset; /* Offset of static TLS block for this module */
+ size_t tlsalign; /* Alignment of static TLS block */
+
/* Items from the dynamic section. */
Elf_Addr *pltgot; /* PLT or GOT, depending on architecture */
const Elf_Rel *rel; /* Relocation entries */
@@ -170,6 +184,7 @@ typedef struct Struct_Obj_Entry {
bool traced; /* Already printed in ldd trace output */
bool jmpslots_done; /* Already have relocated the jump slots */
bool init_done; /* Already have added object to init list */
+ bool tls_done; /* Already allocated offset for static TLS */
struct link_map linkmap; /* for GDB and dlinfo() */
Objlist dldags; /* Object belongs to these dlopened DAGs (%) */
@@ -182,6 +197,8 @@ typedef struct Struct_Obj_Entry {
#define RTLD_MAGIC 0xd550b87a
#define RTLD_VERSION 1
+#define RTLD_STATIC_TLS_EXTRA 64
+
/*
* Symbol cache entry used during relocation to avoid multiple lookups
* of the same symbol.
@@ -216,6 +233,11 @@ Obj_Entry *obj_new(void);
void _rtld_bind_start(void);
const Elf_Sym *symlook_obj(const char *, unsigned long,
const Obj_Entry *, bool);
+void *tls_get_addr_common(Elf_Addr** dtvp, int index, size_t offset);
+void *allocate_tls(Obj_Entry *, void *, size_t, size_t);
+void free_tls(void *, size_t, size_t);
+void *allocate_module_tls(int index);
+bool allocate_tls_offset(Obj_Entry *obj);
/*
* MD function declarations.
@@ -224,5 +246,6 @@ int do_copy_relocations(Obj_Entry *);
int reloc_non_plt(Obj_Entry *, Obj_Entry *);
int reloc_plt(Obj_Entry *);
int reloc_jmpslots(Obj_Entry *);
+void allocate_initial_tls(Obj_Entry *);
#endif /* } */
diff --git a/libexec/rtld-elf/rtld_tls.h b/libexec/rtld-elf/rtld_tls.h
new file mode 100644
index 0000000..b85db59
--- /dev/null
+++ b/libexec/rtld-elf/rtld_tls.h
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2004 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Semi-public interface from thread libraries to rtld for managing
+ * TLS.
+ */
+
+#ifndef _RTLD_TLS_H_
+#define _RTLD_TLS_H_
+
+/*
+ * Allocate a TLS block for a new thread. The memory allocated will
+ * include 'tcbsize' bytes aligned to a 'tcbalign' boundary (in bytes)
+ * for the thread library's private purposes. The location of the TCB
+ * block is returned by this function. For architectures using
+ * 'Variant I' TLS, the thread local storage follows the TCB, and for
+ * 'Variant II', the thread local storage precedes it. For
+ * architectures using the 'Variant II' model (e.g. i386, amd64,
+ * sparc64), the TCB must begin with two pointer fields which are used
+ * by rtld for its TLS implementation. For the 'Variant I' model, the
+ * TCB must begin with a single pointer field for rtld's
+ * implementation.
+ *
+ * If the value of 'oldtls' is non-NULL, the new TLS block will be
+ * initialised using the values contained in 'oldtls' and 'oldtls'
+ * will be freed. This is typically used when initialising a thread
+ * library to migrate from using the initial bootstrap TLS block
+ * created by rtld to one which contains suitable thread library
+ * private data.
+ *
+ * The value returned from this function is suitable for installing
+ * directly into the thread pointer register.
+ */
+extern void *_rtld_allocate_tls(void* oldtls, size_t tcbsize, size_t tcbalign);
+
+/*
+ * Free a TLS block allocated using _rtld_allocate_tls(). The tcbsize
+ * and tcbalign parameters must be the same as those used to allocate
+ * the block.
+ */
+extern void _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign);
+
+#endif
diff --git a/libexec/rtld-elf/sparc64/reloc.c b/libexec/rtld-elf/sparc64/reloc.c
index c083c9c..0b44bec 100644
--- a/libexec/rtld-elf/sparc64/reloc.c
+++ b/libexec/rtld-elf/sparc64/reloc.c
@@ -717,3 +717,24 @@ install_plt(Elf_Half *pltgot, Elf_Addr proc)
pltgot[7] = MOV_g1_o0;
flush(pltgot, 28);
}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+ register Elf_Addr** tp __asm__("%g7");
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+ tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA;
+ tp = allocate_tls(objs, NULL, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
+}
+
+void *__tls_get_addr(tls_index *ti)
+{
+ register Elf_Addr** tp __asm__("%g7");
+
+ return tls_get_addr_common(tp, ti->ti_module, ti->ti_offset);
+}
diff --git a/libexec/rtld-elf/sparc64/rtld_machdep.h b/libexec/rtld-elf/sparc64/rtld_machdep.h
index 27276ee..db2ce25 100644
--- a/libexec/rtld-elf/sparc64/rtld_machdep.h
+++ b/libexec/rtld-elf/sparc64/rtld_machdep.h
@@ -52,4 +52,19 @@ Elf_Addr reloc_jmpslot(Elf_Addr *, Elf_Addr,
#define call_initfini_pointer(obj, target) \
(((InitFunc)(target))())
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(size, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round((prev_offset) + (size), align)
+#define calculate_tls_end(off, size) ((off) + (size))
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *__tls_get_addr(tls_index *ti);
+
#endif
OpenPOWER on IntegriCloud