rtld: Handle IFUNC symbols at non-PLT relocations
authorJohn Marino <draco@marino.st>
Sun, 30 Nov 2014 15:48:25 +0000 (16:48 +0100)
committerJohn Marino <draco@marino.st>
Sun, 30 Nov 2014 16:50:50 +0000 (17:50 +0100)
An example of this is initializing a global variable with a pointer to
ifunc.  This adds a symble type check and call resolver for STT_GNU_IFNC
symbol types whien processing non-PLT relocations, but only after
non-IFUNC relocations are done.  The two-phase processing is required
since resolvers may reference other symbols which most be ready to use
when resolver calls are done.

This restructures reloc_non_plt() to call find_symdef() and handle IFUNC
in a single place.

Taken from: FreeBSD svn 270798 (29 Aug 2014)
FreeBSD svn 270802 (29 Aug 2014)

libexec/rtld-elf/i386/reloc.c
libexec/rtld-elf/rtld.c
libexec/rtld-elf/rtld.h
libexec/rtld-elf/x86_64/reloc.c

index 159f77d..84bb7e4 100644 (file)
@@ -127,168 +127,144 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
        const Elf_Rel *rellim;
        const Elf_Rel *rel;
        SymCache *cache;
-       int r = -1;
+       const Elf_Sym *def;
+       const Obj_Entry *defobj;
+       Elf_Addr *where, symval, add;
+       int r;
 
+       r = -1;
        /*
         * The dynamic loader may be called from a thread, we have
         * limited amounts of stack available so we cannot use alloca().
         */
        if (obj != obj_rtld) {
-           cache = calloc(obj->dynsymcount, sizeof(SymCache));
-           /* No need to check for NULL here */
+               cache = calloc(obj->dynsymcount, sizeof(SymCache));
+               /* No need to check for NULL here */
        } else
-           cache = NULL;
+               cache = NULL;
 
-       rellim = (const Elf_Rel *) ((caddr_t) obj->rel + obj->relsize);
+       rellim = (const Elf_Rel *)((caddr_t) obj->rel + obj->relsize);
        for (rel = obj->rel;  rel < rellim;  rel++) {
-           Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rel->r_offset);
-
-           switch (ELF_R_TYPE(rel->r_info)) {
-
-           case R_386_NONE:
-               break;
-
-           case R_386_32:
-               {
-                   const Elf_Sym *def;
-                   const Obj_Entry *defobj;
-
-                   def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
-                     flags, cache, lockstate);
-                   if (def == NULL)
-                       goto done;
-
-                   *where += (Elf_Addr) (defobj->relocbase + def->st_value);
-               }
-               break;
-
-           case R_386_PC32:
-               /*
-                * I don't think the dynamic linker should ever see this
-                * type of relocation.  But the binutils-2.6 tools sometimes
-                * generate it.
-                */
-               {
-                   const Elf_Sym *def;
-                   const Obj_Entry *defobj;
-
-                   def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
-                     flags, cache, lockstate);
-                   if (def == NULL)
-                       goto done;
-
-                   *where +=
-                     (Elf_Addr) (defobj->relocbase + def->st_value) -
-                     (Elf_Addr) where;
-               }
-               break;
-
-           case R_386_COPY:
-               /*
-                * These are deferred until all other relocations have
-                * been done.  All we do here is make sure that the COPY
-                * relocation is not in a shared library.  They are allowed
-                * only in executable files.
-                */
-               if (!obj->mainprog) {
-                   _rtld_error("%s: Unexpected R_386_COPY relocation"
-                     " in shared library", obj->path);
-                   goto done;
-               }
-               break;
-
-           case R_386_GLOB_DAT:
-               {
-                   const Elf_Sym *def;
-                   const Obj_Entry *defobj;
-
-                   def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
-                     flags, cache, lockstate);
-                   if (def == NULL)
-                       goto done;
-
-                   *where = (Elf_Addr) (defobj->relocbase + def->st_value);
+               switch (ELF_R_TYPE(rel->r_info)) {
+               case R_386_32:
+               case R_386_PC32:
+               case R_386_GLOB_DAT:
+               case R_386_TLS_TPOFF:
+               case R_386_TLS_TPOFF32:
+               case R_386_TLS_DTPMOD32:
+               case R_386_TLS_DTPOFF32:
+                       def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+                           flags, cache, lockstate);
+                       if (def == NULL)
+                               goto done;
+                       if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+                               switch (ELF_R_TYPE(rel->r_info)) {
+                               case R_386_32:
+                               case R_386_PC32:
+                               case R_386_GLOB_DAT:
+                                       if ((flags & SYMLOOK_IFUNC) == 0) {
+                                               obj->non_plt_gnu_ifunc = true;
+                                               continue;
+                                       }
+                                       symval = (Elf_Addr)rtld_resolve_ifunc(
+                                           defobj, def);
+                                       break;
+                               case R_386_TLS_TPOFF:
+                               case R_386_TLS_TPOFF32:
+                               case R_386_TLS_DTPMOD32:
+                               case R_386_TLS_DTPOFF32:
+                                       _rtld_error("%s: IFUNC for TLS reloc",
+                                           obj->path);
+                                       goto done;
+                               }
+                       } else {
+                               if ((flags & SYMLOOK_IFUNC) != 0)
+                                       continue;
+                               symval = (Elf_Addr)defobj->relocbase +
+                                   def->st_value;
+                       }
+                       break;
+               default:
+                       if ((flags & SYMLOOK_IFUNC) != 0)
+                               continue;
+                       break;
                }
-               break;
-
-           case R_386_RELATIVE:
-               *where += (Elf_Addr) obj->relocbase;
-               break;
-
-           case R_386_TLS_TPOFF:
-           case R_386_TLS_TPOFF32:
-               {
-                   const Elf_Sym *def;
-                   const Obj_Entry *defobj;
-                   Elf_Addr add;
-
-                   def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
-                     flags, cache, lockstate);
-                   if (def == NULL)
-                       goto done;
-
+               where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+
+               switch (ELF_R_TYPE(rel->r_info)) {
+               case R_386_NONE:
+                       break;
+               case R_386_32:
+                       *where += symval;
+                       break;
+               case R_386_PC32:
                    /*
-                    * 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.
+                    * I don't think the dynamic linker should ever
+                    * see this type of relocation.  But the
+                    * binutils-2.6 tools sometimes generate it.
                     */
-                   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 += symval - (Elf_Addr)where;
+                   break;
+               case R_386_COPY:
+                       /*
+                        * These are deferred until all other
+                        * relocations have been done.  All we do here
+                        * is make sure that the COPY relocation is
+                        * not in a shared library.  They are allowed
+                        * only in executable files.
+                        */
+                       if (!obj->mainprog) {
+                               _rtld_error("%s: Unexpected R_386_COPY "
+                                   "relocation in shared library", obj->path);
+                               goto done;
                        }
-                   }
-                   add = (Elf_Addr) (def->st_value - defobj->tlsoffset);
-                   if (ELF_R_TYPE(rel->r_info) == R_386_TLS_TPOFF)
-                       *where += add;
-                   else
-                       *where -= add;
-               }
-               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,
-                     flags, cache, lockstate);
-                   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,
-                     flags, cache, lockstate);
-                   if (def == NULL)
+                       break;
+               case R_386_GLOB_DAT:
+                       *where = symval;
+                       break;
+               case R_386_RELATIVE:
+                       *where += (Elf_Addr)obj->relocbase;
+                       break;
+               case R_386_TLS_TPOFF:
+               case R_386_TLS_TPOFF32:
+                       /*
+                        * 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;
+                               }
+                       }
+                       add = (Elf_Addr)(def->st_value - defobj->tlsoffset);
+                       if (ELF_R_TYPE(rel->r_info) == R_386_TLS_TPOFF)
+                               *where += add;
+                       else
+                               *where -= add;
+                       break;
+               case R_386_TLS_DTPMOD32:
+                       *where += (Elf_Addr)defobj->tlsindex;
+                       break;
+               case R_386_TLS_DTPOFF32:
+                       *where += (Elf_Addr) def->st_value;
+                       break;
+               default:
+                       _rtld_error("%s: Unsupported relocation type %d"
+                           " in non-PLT relocations\n", obj->path,
+                           ELF_R_TYPE(rel->r_info));
                        goto done;
-
-                   *where += (Elf_Addr) def->st_value;
                }
-               break;
-
-           default:
-               _rtld_error("%s: Unsupported relocation type %d"
-                 " in non-PLT relocations\n", obj->path,
-                 ELF_R_TYPE(rel->r_info));
-               goto done;
-           }
        }
        r = 0;
 done:
-       if (cache != NULL)
-           free(cache);
+       free(cache);
        return (r);
 }
 
index b7756ed..e8e8f9d 100644 (file)
@@ -2571,7 +2571,7 @@ relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj,
                }
        }
 
-       /* Process the non-PLT relocations. */
+       /* Process the non-PLT non-IFUNC relocations. */
        if (reloc_non_plt(obj, rtldobj, flags, lockstate))
                return (-1);
 
@@ -2590,12 +2590,12 @@ relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj,
         */
        if (obj->textrel) {
            madvise(obj->mapbase, obj->textsize, MADV_CORE);
-           if (mprotect(obj->mapbase, obj->textsize,
-             PROT_READ|PROT_EXEC) == -1) {
-               _rtld_error("%s: Cannot write-protect text segment: %s",
-                 obj->path, rtld_strerror(errno));
-               return (-1);
-           }
+               if (mprotect(obj->mapbase, obj->textsize,
+                   PROT_READ|PROT_EXEC) == -1) {
+                       _rtld_error("%s: Cannot write-protect text segment: %s",
+                           obj->path, rtld_strerror(errno));
+                       return (-1);
+               }
        }
 
        /* Set the special PLT or GOT entries. */
@@ -2609,6 +2609,16 @@ relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj,
                if (reloc_jmpslots(obj, flags, lockstate) == -1)
                        return (-1);
 
+       /*
+        * Process the non-PLT IFUNC relocations.  The relocations are
+        * processed in two phases, because IFUNC resolvers may
+        * reference other symbols, which must be readily processed
+        * before resolvers are called.
+        */
+       if (obj->non_plt_gnu_ifunc &&
+           reloc_non_plt(obj, rtldobj, flags | SYMLOOK_IFUNC, lockstate))
+               return (-1);
+
        /*
         * Set up the magic number and version in the Obj_Entry.  These
         * were checked in the crt1.o from the original ElfKit, so we
index 363e5c8..2021089 100644 (file)
@@ -158,8 +158,6 @@ typedef struct Struct_Obj_Entry {
     const Elf_Phdr *phdr;      /* Program header if it is mapped, else NULL */
     size_t phsize;             /* Size of program header in bytes */
     const char *interp;                /* Pathname of the interpreter, if any */
-    caddr_t relro_page;        /* Address of first page of read-only data */
-    size_t relro_size;         /* Size of relro page(s) in bytes */
     Elf_Word stack_flags;
 
     /* TLS information */
@@ -170,6 +168,9 @@ typedef struct Struct_Obj_Entry {
     size_t tlsoffset;          /* Offset of static TLS block for this module */
     size_t tlsalign;           /* Alignment of static TLS block */
 
+    caddr_t relro_page;        /* Address of first page of read-only data */
+    size_t relro_size;         /* Size of relro page(s) in bytes */
+
     /* Items from the dynamic section. */
     Elf_Addr *pltgot;          /* PLT or GOT, depending on architecture */
     const Elf_Rel *rel;                /* Relocation entries */
@@ -243,7 +244,7 @@ typedef struct Struct_Obj_Entry {
     bool z_noopen : 1;         /* Do not load on dlopen */
     bool z_loadfltr : 1;       /* Immediately load filtees */
     bool z_interpose : 1;      /* Interpose all objects but main */
-    bool z_nodeflib : 1;       /* Don't search default /usr/lib path */
+    bool z_nodeflib : 1;       /* Don't search default library path */
     bool ref_nodel : 1;                /* Refcount increased to prevent dlclose */
     bool init_scanned: 1;      /* Object is already on init list. */
     bool on_fini_list: 1;      /* Object is already on fini list. */
@@ -251,6 +252,7 @@ typedef struct Struct_Obj_Entry {
     bool filtees_loaded : 1;   /* Filtees loaded */
     bool irelative : 1;                /* Object has R_MACHDEP_IRELATIVE relocs */
     bool gnu_ifunc : 1;                /* Object has references to STT_GNU_IFUNC */
+    bool non_plt_gnu_ifunc : 1;        /* Object has non-plt IFUNC references */
     bool crt_no_init : 1;      /* Object's crt does not call _init/_fini */
     bool note_present : 1;     /* True if at least one PT_NOTE header found */
     bool valid_hash_sysv : 1;  /* A valid System V hash hash tag is available */
@@ -274,6 +276,8 @@ typedef struct Struct_Obj_Entry {
 #define SYMLOOK_DLSYM  0x02    /* Return newest versioned symbol. Used by
                                   dlsym. */
 #define        SYMLOOK_EARLY   0x04    /* Symlook is done during initialization. */
+#define        SYMLOOK_IFUNC   0x08    /* Allow IFUNC processing in
+                                  reloc_non_plt(). */
 
 /* Flags for load_object(). */
 #define        RTLD_LO_NOLOAD  0x01    /* dlopen() specified RTLD_NOLOAD. */
index ca216f6..0356dff 100644 (file)
@@ -128,213 +128,188 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
        const Elf_Rela *relalim;
        const Elf_Rela *rela;
        SymCache *cache;
-       int r = -1;
+       const Elf_Sym *def;
+       const Obj_Entry *defobj;
+       Elf_Addr *where, symval;
+       Elf32_Addr *where32;
+       int r;
 
+       r = -1;
        /*
         * The dynamic loader may be called from a thread, we have
         * limited amounts of stack available so we cannot use alloca().
         */
        if (obj != obj_rtld) {
-           cache = calloc(obj->dynsymcount, sizeof(SymCache));
-           /* No need to check for NULL here */
+               cache = calloc(obj->dynsymcount, sizeof(SymCache));
+               /* No need to check for NULL here */
        } else
-           cache = NULL;
+               cache = NULL;
 
-       relalim = (const Elf_Rela *) ((caddr_t) obj->rela + obj->relasize);
+       relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize);
        for (rela = obj->rela;  rela < relalim;  rela++) {
-           Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rela->r_offset);
-           Elf32_Addr *where32 = (Elf32_Addr *)where;
-
-           switch (ELF_R_TYPE(rela->r_info)) {
-
-           case R_X86_64_NONE:
-               break;
-
-           case R_X86_64_64:
-               {
-                   const Elf_Sym *def;
-                   const Obj_Entry *defobj;
-
-                   def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
-                     flags, cache, lockstate);
-                   if (def == NULL)
-                       goto done;
-
-                   *where = (Elf_Addr) (defobj->relocbase + def->st_value + rela->r_addend);
-               }
-               break;
-
-           case R_X86_64_PC32:
-               /*
-                * I don't think the dynamic linker should ever see this
-                * type of relocation.  But the binutils-2.6 tools sometimes
-                * generate it.
-                */
-               {
-                   const Elf_Sym *def;
-                   const Obj_Entry *defobj;
-
-                   def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
-                     flags, cache, lockstate);
-                   if (def == NULL)
-                       goto done;
-
-                   *where32 = (Elf32_Addr) (unsigned long) (defobj->relocbase +
-                       def->st_value + rela->r_addend - (Elf_Addr) where);
-               }
-               break;
-       /* missing: R_X86_64_GOT32 R_X86_64_PLT32 */
-
-           case R_X86_64_COPY:
                /*
-                * These are deferred until all other relocations have
-                * been done.  All we do here is make sure that the COPY
-                * relocation is not in a shared library.  They are allowed
-                * only in executable files.
+                * First, resolve symbol for relocations which
+                * reference symbols.
                 */
-               if (!obj->mainprog) {
-                   _rtld_error("%s: Unexpected R_X86_64_COPY relocation"
-                     " in shared library", obj->path);
-                   goto done;
-               }
-               break;
-
-           case R_X86_64_GLOB_DAT:
-               {
-                   const Elf_Sym *def;
-                   const Obj_Entry *defobj;
-
-                   def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
-                     flags, cache, lockstate);
-                   if (def == NULL)
-                       goto done;
-
-                   *where = (Elf_Addr) (defobj->relocbase + def->st_value);
-               }
-               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,
-                     flags, cache, lockstate);
-                   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;
+               switch (ELF_R_TYPE(rela->r_info)) {
+               case R_X86_64_64:
+               case R_X86_64_PC32:
+               case R_X86_64_GLOB_DAT:
+               case R_X86_64_TPOFF64:
+               case R_X86_64_TPOFF32:
+               case R_X86_64_DTPMOD64:
+               case R_X86_64_DTPOFF64:
+               case R_X86_64_DTPOFF32:
+                       def = find_symdef(ELF_R_SYM(rela->r_info), obj,
+                           &defobj, flags, cache, lockstate);
+                       if (def == NULL)
+                               goto done;
+                       /*
+                        * If symbol is IFUNC, only perform relocation
+                        * when caller allowed it by passing
+                        * SYMLOOK_IFUNC flag.  Skip the relocations
+                        * otherwise.
+                        *
+                        * Also error out in case IFUNC relocations
+                        * are specified for TLS, which cannot be
+                        * usefully interpreted.
+                        */
+                       if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+                               switch (ELF_R_TYPE(rela->r_info)) {
+                               case R_X86_64_64:
+                               case R_X86_64_PC32:
+                               case R_X86_64_GLOB_DAT:
+                                       if ((flags & SYMLOOK_IFUNC) == 0) {
+                                               obj->non_plt_gnu_ifunc = true;
+                                               continue;
+                                       }
+                                       symval = (Elf_Addr)rtld_resolve_ifunc(
+                                           defobj, def);
+                                       break;
+                               case R_X86_64_TPOFF64:
+                               case R_X86_64_TPOFF32:
+                               case R_X86_64_DTPMOD64:
+                               case R_X86_64_DTPOFF64:
+                               case R_X86_64_DTPOFF32:
+                                       _rtld_error("%s: IFUNC for TLS reloc",
+                                           obj->path);
+                                       goto done;
+                               }
+                       } else {
+                               if ((flags & SYMLOOK_IFUNC) != 0)
+                                       continue;
+                               symval = (Elf_Addr)defobj->relocbase +
+                                   def->st_value;
                        }
-                   }
-
-                   *where = (Elf_Addr) (def->st_value - defobj->tlsoffset +
-                                        rela->r_addend);
+                       break;
+               default:
+                       if ((flags & SYMLOOK_IFUNC) != 0)
+                               continue;
+                       break;
                }
-               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,
-                     flags, cache, lockstate);
-                   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 *)(obj->relocbase + rela->r_offset);
+               where32 = (Elf32_Addr *)where;
+
+               switch (ELF_R_TYPE(rela->r_info)) {
+               case R_X86_64_NONE:
+                       break;
+               case R_X86_64_64:
+                       *where = symval + rela->r_addend;
+                       break;
+               case R_X86_64_PC32:
+                       /*
+                        * I don't think the dynamic linker should
+                        * ever see this type of relocation.  But the
+                        * binutils-2.6 tools sometimes generate it.
+                        */
+                       *where32 = (Elf32_Addr)(unsigned long)(symval +
+                           rela->r_addend - (Elf_Addr)where);
+                       break;
+               /* missing: R_X86_64_GOT32 R_X86_64_PLT32 */
+               case R_X86_64_COPY:
+                       /*
+                        * These are deferred until all other relocations have
+                        * been done.  All we do here is make sure that the COPY
+                        * relocation is not in a shared library.  They are allowed
+                        * only in executable files.
+                        */
+                       if (!obj->mainprog) {
+                               _rtld_error("%s: Unexpected R_X86_64_COPY "
+                                   "relocation in shared library", 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,
-                     flags, cache, lockstate);
-                   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,
-                     flags, cache, lockstate);
-                   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,
-                     flags, cache, lockstate);
-                   if (def == NULL)
+                       break;
+               case R_X86_64_GLOB_DAT:
+                       *where = symval;
+                       break;
+               case R_X86_64_TPOFF64:
+                       /*
+                        * 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:
+                       /*
+                        * 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:
+                       *where += (Elf_Addr)defobj->tlsindex;
+                       break;
+               case R_X86_64_DTPOFF64:
+                       *where += (Elf_Addr)(def->st_value + rela->r_addend);
+                       break;
+               case R_X86_64_DTPOFF32:
+                       *where32 += (Elf32_Addr)(def->st_value +
+                           rela->r_addend);
+                       break;
+               case R_X86_64_RELATIVE:
+                       *where = (Elf_Addr)(obj->relocbase + rela->r_addend);
+                       break;
+               /*
+                * missing:
+                * R_X86_64_GOTPCREL, R_X86_64_32, R_X86_64_32S, R_X86_64_16,
+                * R_X86_64_PC16, R_X86_64_8, R_X86_64_PC8
+                */
+               default:
+                       _rtld_error("%s: Unsupported relocation type %u"
+                           " in non-PLT relocations\n", obj->path,
+                           (unsigned int)ELF_R_TYPE(rela->r_info));
                        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;
-
-       /* missing: R_X86_64_GOTPCREL, R_X86_64_32, R_X86_64_32S, R_X86_64_16, R_X86_64_PC16, R_X86_64_8, R_X86_64_PC8 */
-
-           default:
-               _rtld_error("%s: Unsupported relocation type %u"
-                 " in non-PLT relocations\n", obj->path,
-                 (unsigned int)ELF_R_TYPE(rela->r_info));
-               goto done;
-           }
        }
        r = 0;
 done:
-       if (cache != NULL)
-           free(cache);
+       free(cache);
        return (r);
 }