Add support for TLS.
authorDavid Xu <davidxu@dragonflybsd.org>
Tue, 22 Mar 2005 22:56:36 +0000 (22:56 +0000)
committerDavid Xu <davidxu@dragonflybsd.org>
Tue, 22 Mar 2005 22:56:36 +0000 (22:56 +0000)
Obtained from : FreeBSD

libexec/rtld-elf/i386/reloc.c
libexec/rtld-elf/i386/rtld_machdep.h
libexec/rtld-elf/map_object.c
libexec/rtld-elf/rtld.c
libexec/rtld-elf/rtld.h
libexec/rtld-elf/rtld_tls.h [new file with mode: 0644]

index c6b5057..52d40cd 100644 (file)
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/libexec/rtld-elf/i386/reloc.c,v 1.6.2.2 2002/06/16 20:02:09 dillon Exp $
- * $DragonFly: src/libexec/rtld-elf/i386/reloc.c,v 1.3 2005/02/04 00:24:23 joerg Exp $
+ * $DragonFly: src/libexec/rtld-elf/i386/reloc.c,v 1.4 2005/03/22 22:56:36 davidxu Exp $
  */
 
 /*
@@ -34,6 +34,7 @@
 
 #include <sys/param.h>
 #include <sys/mman.h>
+#include <sys/tls.h>
 
 #include <dlfcn.h>
 #include <err.h>
@@ -203,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 +321,48 @@ reloc_jmpslots(Obj_Entry *obj)
     obj->jmpslots_done = true;
     return 0;
 }
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+    struct tls_info ti;
+    void* tls;
+    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));
+    ti.base = tls;
+    ti.size = 2 * sizeof(Elf_Addr);
+    sel = sys_set_tls_area(0, &ti, sizeof(ti));
+    __asm __volatile("movl %0,%%gs" : : "rm" (sel));
+}
+
+/* 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);
+}
index 43e755b..18b1083 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/libexec/rtld-elf/i386/rtld_machdep.h,v 1.3.2.2 2002/07/02 04:10:51 jdp Exp $
- * $DragonFly: src/libexec/rtld-elf/i386/rtld_machdep.h,v 1.5 2004/01/20 21:32:47 dillon Exp $
+ * $DragonFly: src/libexec/rtld-elf/i386/rtld_machdep.h,v 1.6 2005/03/22 22:56:36 davidxu Exp $
  */
 
 #ifndef RTLD_MACHDEP_H
@@ -63,4 +63,20 @@ atomic_add_int(volatile int *p, int val)
        : "cc");
 }
 
+#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
index 6726869..6a9513e 100644 (file)
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/libexec/rtld-elf/map_object.c,v 1.7.2.2 2002/12/28 19:49:41 dillon Exp $
- * $DragonFly: src/libexec/rtld-elf/map_object.c,v 1.5 2005/02/05 22:54:49 joerg Exp $
+ * $DragonFly: src/libexec/rtld-elf/map_object.c,v 1.6 2005/03/22 22:56:36 davidxu Exp $
  */
 
 #include <sys/param.h>
@@ -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,9 @@ map_object(int fd, const char *path, const struct stat *sb)
        case PT_DYNAMIC:
            phdyn = phdr;
            break;
+       case PT_TLS:
+           phtls = phdr;
+           break;
        }
 
        ++phdr;
@@ -234,7 +238,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;
 }
 
@@ -299,6 +310,9 @@ obj_free(Obj_Entry *obj)
 {
     Objlist_Entry *elm;
 
+    if (obj->tls_done) {
+       free_tls_offset(obj);
+    }
     free(obj->origin_path);
     free(obj->path);
     while (obj->needed != NULL) {
index 9553a68..d2916f9 100644 (file)
@@ -24,7 +24,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/libexec/rtld-elf/rtld.c,v 1.43.2.15 2003/02/20 20:42:46 kan Exp $
- * $DragonFly: src/libexec/rtld-elf/rtld.c,v 1.16 2005/02/24 16:05:22 joerg Exp $
+ * $DragonFly: src/libexec/rtld-elf/rtld.c,v 1.17 2005/03/22 22:56:36 davidxu Exp $
  */
 
 /*
@@ -54,6 +54,7 @@
 
 #include "debug.h"
 #include "rtld.h"
+#include "rtld_tls.h"
 
 #define PATH_RTLD      "/usr/libexec/ld-elf.so.1"
 #define LD_ARY_CACHE   16
@@ -182,6 +183,12 @@ static func_ptr_type exports[] = {
     (func_ptr_type) &dladdr,
     (func_ptr_type) &dllockinit,
     (func_ptr_type) &dlinfo,
+#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 @@ static func_ptr_type exports[] = {
 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
@@ -260,6 +276,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;
     Objlist initlist;
 
@@ -420,6 +437,17 @@ resident_skip1:
        exit (0);
     }
 
+    /* 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);
+
     if (relocate_objects(obj_main,
        ld_bind_now != NULL && *ld_bind_now != '\0') == -1)
        die();
@@ -788,6 +816,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) {
@@ -2525,3 +2561,351 @@ unref_dag(Obj_Entry *root)
            if (needed->obj != NULL)
                unref_dag(needed->obj);
 }
+
+/*
+ * 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;
+
+       wlock_acquire();
+
+       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);
+
+       wlock_release();
+
+       *dtvp = newdtv;
+    }
+
+    /* Dynamically allocate module TLS if necessary */
+    if (!dtv[index + 1]) {
+       /* XXX
+        * here we should avoid to be re-entered by signal handler
+        * code, I assume wlock_acquire will masked all signals,
+        * otherwise there is race and dead lock thread itself.
+        */
+       wlock_acquire();
+       if (!dtv[index + 1])
+           dtv[index + 1] = (Elf_Addr)allocate_module_tls(index);
+       wlock_release();
+    }
+
+    return (void*) (dtv[index + 1] + offset);
+}
+
+/* XXX not sure what variants to use for arm. */
+
+#if defined(__ia64__) || defined(__powerpc__)
+
+/*
+ * 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;
+
+    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__) || \
+    defined(__arm__)
+
+/*
+ * 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
+free_tls_offset(Obj_Entry *obj)
+{
+#if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \
+    defined(__arm__)
+    /*
+     * If we were the last thing to allocate out of the static TLS
+     * block, we give our space back to the 'allocator'. This is a
+     * simplistic workaround to allow libGL.so.1 to be loaded and
+     * unloaded multiple times. We only handle the Variant II
+     * mechanism for now - this really needs a proper allocator.  
+     */
+    if (calculate_tls_end(obj->tlsoffset, obj->tlssize)
+       == calculate_tls_end(tls_last_offset, tls_last_size)) {
+       tls_last_offset -= obj->tlssize;
+       tls_last_size = 0;
+    }
+#endif
+}
+
+void *
+_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
+{
+    void *ret;
+
+    wlock_acquire();
+    ret = allocate_tls(obj_list, oldtls, tcbsize, tcbalign);
+    wlock_release();
+
+    return (ret);
+}
+
+void
+_rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
+{
+    wlock_acquire();
+    free_tls(tcb, tcbsize, tcbalign);
+    wlock_release();
+}
index 65cbd09..7884274 100644 (file)
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * $FreeBSD: src/libexec/rtld-elf/rtld.h,v 1.15.2.6 2003/02/20 20:42:46 kan Exp $
- * $DragonFly: src/libexec/rtld-elf/rtld.h,v 1.6 2005/02/05 22:54:49 joerg Exp $
+ * $DragonFly: src/libexec/rtld-elf/rtld.h,v 1.7 2005/03/22 22:56:36 davidxu Exp $
  */
 
 #ifndef RTLD_H /* { */
@@ -51,6 +51,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;
 
@@ -125,6 +131,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 */
@@ -158,6 +172,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 (%) */
@@ -169,6 +184,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.
@@ -210,4 +227,11 @@ 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);
+void free_tls_offset(Obj_Entry *obj);
+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 (file)
index 0000000..438d4fe
--- /dev/null
@@ -0,0 +1,70 @@
+/*-
+ * 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: src/libexec/rtld-elf/rtld_tls.h,v 1.1 2004/08/03 08:50:58 dfr Exp $
+ * $DragonFly: src/libexec/rtld-elf/Attic/rtld_tls.h,v 1.1 2005/03/22 22:56:36 davidxu Exp $
+ */
+
+/*
+ * 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