fix regression in mips dynamic linker
[musl] / src / ldso / dynlink.c
index 94531b3..55124ff 100644 (file)
@@ -1,6 +1,8 @@
 #define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdarg.h>
+#include <stddef.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdint.h>
@@ -48,6 +50,11 @@ struct debug {
        void *base;
 };
 
+struct td_index {
+       size_t args[2];
+       struct td_index *next;
+};
+
 struct dso {
        unsigned char *base;
        char *name;
@@ -79,6 +86,7 @@ struct dso {
        void **new_dtv;
        unsigned char *new_tls;
        int new_dtv_idx, new_tls_idx;
+       struct td_index *td_index;
        struct dso *fini_next;
        char *shortname;
        char buf[];
@@ -104,6 +112,7 @@ enum {
        REL_DTPOFF,
        REL_TPOFF,
        REL_TPOFF_NEG,
+       REL_TLSDESC,
 };
 
 #include "reloc.h"
@@ -124,6 +133,7 @@ static jmp_buf *rtld_fail;
 static pthread_rwlock_t lock;
 static struct debug debug;
 static size_t tls_cnt, tls_offset, tls_align = 4*sizeof(size_t);
+static size_t static_tls_cnt;
 static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE };
 static long long builtin_tls[(sizeof(struct pthread) + 64)/sizeof(long long)];
 
@@ -149,6 +159,17 @@ static int search_vec(size_t *v, size_t *r, size_t key)
        return 1;
 }
 
+static void error(const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       vsnprintf(errbuf, sizeof errbuf, fmt, ap);
+       va_end(ap);
+       if (runtime) longjmp(*rtld_fail, 1);
+       dprintf(2, "%s\n", errbuf);
+       ldso_fail = 1;
+}
+
 static uint32_t sysv_hash(const char *s0)
 {
        const unsigned char *s = (void *)s0;
@@ -212,6 +233,10 @@ static Sym *gnu_lookup(const char *s, uint32_t h1, struct dso *dso)
 #define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON | 1<<STT_TLS)
 #define OK_BINDS (1<<STB_GLOBAL | 1<<STB_WEAK | 1<<STB_GNU_UNIQUE)
 
+#ifndef ARCH_SYM_REJECT_UND
+#define ARCH_SYM_REJECT_UND(s) 0
+#endif
+
 static struct symdef find_sym(struct dso *dso, const char *s, int need_def)
 {
        uint32_t h = 0, gh = 0;
@@ -228,7 +253,8 @@ static struct symdef find_sym(struct dso *dso, const char *s, int need_def)
                }
                if (!sym) continue;
                if (!sym->st_shndx)
-                       if (need_def || (sym->st_info&0xf) == STT_TLS)
+                       if (need_def || (sym->st_info&0xf) == STT_TLS
+                           || ARCH_SYM_REJECT_UND(sym))
                                continue;
                if (!sym->st_value)
                        if ((sym->st_info&0xf) != STT_TLS)
@@ -246,6 +272,8 @@ static struct symdef find_sym(struct dso *dso, const char *s, int need_def)
 
 #define NO_INLINE_ADDEND (1<<REL_COPY | 1<<REL_GOT | 1<<REL_PLT)
 
+ptrdiff_t __tlsdesc_static(), __tlsdesc_dynamic();
+
 static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stride)
 {
        unsigned char *base = dso->base;
@@ -267,12 +295,8 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
                if (!astype) continue;
                type = remap_rel(astype);
                if (!type) {
-                       snprintf(errbuf, sizeof errbuf,
-                               "Error relocating %s: unsupported relocation type %d",
+                       error("Error relocating %s: unsupported relocation type %d",
                                dso->name, astype);
-                       if (runtime) longjmp(*rtld_fail, 1);
-                       dprintf(2, "%s\n", errbuf);
-                       ldso_fail = 1;
                        continue;
                }
                sym_index = R_SYM(rel[1]);
@@ -284,12 +308,8 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
                        def = find_sym(ctx, name, type==REL_PLT);
                        if (!def.sym && (sym->st_shndx != SHN_UNDEF
                            || sym->st_info>>4 != STB_WEAK)) {
-                               snprintf(errbuf, sizeof errbuf,
-                                       "Error relocating %s: %s: symbol not found",
+                               error("Error relocating %s: %s: symbol not found",
                                        dso->name, name);
-                               if (runtime) longjmp(*rtld_fail, 1);
-                               dprintf(2, "%s\n", errbuf);
-                               ldso_fail = 1;
                                continue;
                        }
                } else {
@@ -345,6 +365,30 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
                        *reloc_addr = def.dso->tls_offset - tls_val + addend;
                        break;
 #endif
+               case REL_TLSDESC:
+                       if (stride<3) addend = reloc_addr[1];
+                       if (runtime && def.dso->tls_id >= static_tls_cnt) {
+                               struct td_index *new = malloc(sizeof *new);
+                               if (!new) error(
+                                       "Error relocating %s: cannot allocate TLSDESC for %s",
+                                       dso->name, sym ? name : "(local)" );
+                               new->next = dso->td_index;
+                               dso->td_index = new;
+                               new->args[0] = def.dso->tls_id;
+                               new->args[1] = tls_val + addend;
+                               reloc_addr[0] = (size_t)__tlsdesc_dynamic;
+                               reloc_addr[1] = (size_t)new;
+                       } else {
+                               reloc_addr[0] = (size_t)__tlsdesc_static;
+#ifdef TLS_ABOVE_TP
+                               reloc_addr[1] = tls_val + def.dso->tls_offset
+                                       + TPOFF_K + addend;
+#else
+                               reloc_addr[1] = tls_val - def.dso->tls_offset
+                                       + addend;
+#endif
+                       }
+                       break;
                }
        }
 }
@@ -798,12 +842,8 @@ static void load_deps(struct dso *p)
                        if (p->dynv[i] != DT_NEEDED) continue;
                        dep = load_library(p->strings + p->dynv[i+1], p);
                        if (!dep) {
-                               snprintf(errbuf, sizeof errbuf,
-                                       "Error loading shared library %s: %m (needed by %s)",
+                               error("Error loading shared library %s: %m (needed by %s)",
                                        p->strings + p->dynv[i+1], p->name);
-                               if (runtime) longjmp(*rtld_fail, 1);
-                               dprintf(2, "%s\n", errbuf);
-                               ldso_fail = 1;
                                continue;
                        }
                        if (runtime) {
@@ -852,12 +892,8 @@ static void reloc_all(struct dso *p)
 
                if (p->relro_start != p->relro_end &&
                    mprotect(p->base+p->relro_start, p->relro_end-p->relro_start, PROT_READ) < 0) {
-                       snprintf(errbuf, sizeof errbuf,
-                               "Error relocating %s: RELRO protection failed: %m",
+                       error("Error relocating %s: RELRO protection failed: %m",
                                p->name);
-                       if (runtime) longjmp(*rtld_fail, 1);
-                       dprintf(2, "%s\n", errbuf);
-                       ldso_fail = 1;
                }
 
                p->relocated = 1;
@@ -996,17 +1032,15 @@ void *__copy_tls(unsigned char *mem)
        return td;
 }
 
-void *__tls_get_addr(size_t *v)
+void *__tls_get_new(size_t *v)
 {
        pthread_t self = __pthread_self();
-       if (v[0]<=(size_t)self->dtv[0] && self->dtv[v[0]])
-               return (char *)self->dtv[v[0]]+v[1];
 
        /* Block signals to make accessing new TLS async-signal-safe */
        sigset_t set;
-       pthread_sigmask(SIG_BLOCK, SIGALL_SET, &set);
-       if (v[0]<=(size_t)self->dtv[0] && self->dtv[v[0]]) {
-               pthread_sigmask(SIG_SETMASK, &set, 0);
+       __block_all_sigs(&set);
+       if (v[0]<=(size_t)self->dtv[0]) {
+               __restore_sigs(&set);
                return (char *)self->dtv[v[0]]+v[1];
        }
 
@@ -1027,13 +1061,19 @@ void *__tls_get_addr(size_t *v)
                self->dtv = newdtv;
        }
 
-       /* Get new TLS memory from new DSO */
-       unsigned char *mem = p->new_tls +
-               (p->tls_size + p->tls_align) * a_fetch_add(&p->new_tls_idx,1);
-       mem += ((uintptr_t)p->tls_image - (uintptr_t)mem) & (p->tls_align-1);
-       self->dtv[v[0]] = mem;
-       memcpy(mem, p->tls_image, p->tls_len);
-       pthread_sigmask(SIG_SETMASK, &set, 0);
+       /* Get new TLS memory from all new DSOs up to the requested one */
+       unsigned char *mem;
+       for (p=head; ; p=p->next) {
+               if (!p->tls_id || self->dtv[p->tls_id]) continue;
+               mem = p->new_tls + (p->tls_size + p->tls_align)
+                       * a_fetch_add(&p->new_tls_idx,1);
+               mem += ((uintptr_t)p->tls_image - (uintptr_t)mem)
+                       & (p->tls_align-1);
+               self->dtv[p->tls_id] = mem;
+               memcpy(mem, p->tls_image, p->tls_len);
+               if (p->tls_id == v[0]) break;
+       }
+       __restore_sigs(&set);
        return mem + v[1];
 }
 
@@ -1277,6 +1317,7 @@ void *__dynlink(int argc, char **argv)
                dprintf(2, "%s: Thread-local storage not supported by kernel.\n", argv[0]);
                _exit(127);
        }
+       static_tls_cnt = tls_cnt;
 
        if (ldso_fail) _exit(127);
        if (ldd_mode) _exit(0);
@@ -1332,6 +1373,11 @@ void *dlopen(const char *file, int mode)
                for (p=orig_tail->next; p; p=next) {
                        next = p->next;
                        munmap(p->map, p->map_len);
+                       while (p->td_index) {
+                               void *tmp = p->td_index->next;
+                               free(p->td_index);
+                               p->td_index = tmp;
+                       }
                        free(p->deps);
                        free(p);
                }
@@ -1395,6 +1441,8 @@ static int invalid_dso_handle(void *h)
        return 1;
 }
 
+void *__tls_get_addr(size_t *);
+
 static void *do_dlsym(struct dso *p, const char *s, void *ra)
 {
        size_t i;