math: remove *_WORD64 macros from libm.h
[musl] / src / ldso / dynlink.c
index 0ae7177..6d3d08e 100644 (file)
@@ -63,6 +63,7 @@ struct dso {
        Sym *syms;
        uint32_t *hashtab;
        uint32_t *ghashtab;
+       int16_t *versym;
        char *strings;
        unsigned char *map;
        size_t map_len;
@@ -71,7 +72,9 @@ struct dso {
        signed char global;
        char relocated;
        char constructed;
-       struct dso **deps;
+       char kernel_mapped;
+       struct dso **deps, *needed_by;
+       char *rpath_orig, *rpath;
        void *tls_image;
        size_t tls_len, tls_size, tls_align, tls_id, tls_offset;
        void **new_dtv;
@@ -94,7 +97,7 @@ void *__install_initial_tls(void *);
 void __init_libc(char **, char *);
 
 static struct dso *head, *tail, *ldso, *fini_head;
-static char *env_path, *sys_path, *r_path;
+static char *env_path, *sys_path;
 static unsigned long long gencnt;
 static int ssp_used;
 static int runtime;
@@ -156,7 +159,8 @@ static Sym *sysv_lookup(const char *s, uint32_t h, struct dso *dso)
        uint32_t *hashtab = dso->hashtab;
        char *strings = dso->strings;
        for (i=hashtab[2+h%hashtab[0]]; i; i=hashtab[2+hashtab[0]+i]) {
-               if (!strcmp(s, strings+syms[i].st_name))
+               if ((!dso->versym || dso->versym[i] >= 0)
+                   && (!strcmp(s, strings+syms[i].st_name)))
                        return syms+i;
        }
        return 0;
@@ -164,25 +168,24 @@ static Sym *sysv_lookup(const char *s, uint32_t h, struct dso *dso)
 
 static Sym *gnu_lookup(const char *s, uint32_t h1, struct dso *dso)
 {
-       Sym *sym;
-       char *strings;
+       Sym *syms = dso->syms;
+       char *strings = dso->strings;
        uint32_t *hashtab = dso->ghashtab;
        uint32_t nbuckets = hashtab[0];
        uint32_t *buckets = hashtab + 4 + hashtab[2]*(sizeof(size_t)/4);
        uint32_t h2;
        uint32_t *hashval;
-       uint32_t n = buckets[h1 % nbuckets];
+       uint32_t i = buckets[h1 % nbuckets];
 
-       if (!n) return 0;
+       if (!i) return 0;
 
-       strings = dso->strings;
-       sym = dso->syms + n;
-       hashval = buckets + nbuckets + (n - hashtab[1]);
+       hashval = buckets + nbuckets + (i - hashtab[1]);
 
-       for (h1 |= 1; ; sym++) {
+       for (h1 |= 1; ; i++) {
                h2 = *hashval++;
-               if ((h1 == (h2|1)) && !strcmp(s, strings + sym->st_name))
-                       return sym;
+               if ((!dso->versym || dso->versym[i] >= 0)
+                   && (h1 == (h2|1)) && !strcmp(s, strings + syms[i].st_name))
+                       return syms+i;
                if (h2 & 1) break;
        }
 
@@ -421,6 +424,7 @@ static void *map_library(int fd, struct dso *dso)
        dso->base = base;
        dso->dynv = (void *)(base+dyn);
        if (dso->tls_size) dso->tls_image = (void *)(base+tls_image);
+       free(allocated_buf);
        return map;
 noexec:
        errno = ENOEXEC;
@@ -445,6 +449,64 @@ static int path_open(const char *name, const char *s, char *buf, size_t buf_size
        }
 }
 
+static int fixup_rpath(struct dso *p, char *buf, size_t buf_size)
+{
+       size_t n, l;
+       const char *s, *t, *origin;
+       char *d;
+       if (p->rpath) return 0;
+       if (!p->rpath_orig) return -1;
+       if (!strchr(p->rpath_orig, '$')) {
+               p->rpath = p->rpath_orig;
+               return 0;
+       }
+       n = 0;
+       s = p->rpath_orig;
+       while ((t=strchr(s, '$'))) {
+               if (strncmp(t, "$ORIGIN", 7) && strncmp(t, "${ORIGIN}", 9))
+                       return -1;
+               s = t+1;
+               n++;
+       }
+       if (n > SSIZE_MAX/PATH_MAX) return -1;
+
+       if (p->kernel_mapped) {
+               /* $ORIGIN searches cannot be performed for the main program
+                * when it is suid/sgid/AT_SECURE. This is because the
+                * pathname is under the control of the caller of execve.
+                * For libraries, however, $ORIGIN can be processed safely
+                * since the library's pathname came from a trusted source
+                * (either system paths or a call to dlopen). */
+               if (libc.secure)
+                       return -1;
+               l = readlink("/proc/self/exe", buf, buf_size);
+               if (l >= buf_size)
+                       return -1;
+               buf[l] = 0;
+               origin = buf;
+       } else {
+               origin = p->name;
+       }
+       t = strrchr(origin, '/');
+       l = t ? t-origin : 0;
+       p->rpath = malloc(strlen(p->rpath_orig) + n*l + 1);
+       if (!p->rpath) return -1;
+
+       d = p->rpath;
+       s = p->rpath_orig;
+       while ((t=strchr(s, '$'))) {
+               memcpy(d, s, t-s);
+               d += t-s;
+               memcpy(d, origin, l);
+               d += l;
+               /* It was determined previously that the '$' is followed
+                * either by "ORIGIN" or "{ORIGIN}". */
+               s = t + 7 + 2*(t[1]=='{');
+       }
+       strcpy(d, s);
+       return 0;
+}
+
 static void decode_dyn(struct dso *p)
 {
        size_t dyn[DYN_CNT] = {0};
@@ -453,11 +515,15 @@ static void decode_dyn(struct dso *p)
        p->strings = (void *)(p->base + dyn[DT_STRTAB]);
        if (dyn[0]&(1<<DT_HASH))
                p->hashtab = (void *)(p->base + dyn[DT_HASH]);
+       if (dyn[0]&(1<<DT_RPATH))
+               p->rpath_orig = (void *)(p->strings + dyn[DT_RPATH]);
        if (search_vec(p->dynv, dyn, DT_GNU_HASH))
                p->ghashtab = (void *)(p->base + *dyn);
+       if (search_vec(p->dynv, dyn, DT_VERSYM))
+               p->versym = (void *)(p->base + *dyn);
 }
 
-static struct dso *load_library(const char *name)
+static struct dso *load_library(const char *name, struct dso *needed_by)
 {
        char buf[2*NAME_MAX+2];
        const char *pathname;
@@ -516,8 +582,10 @@ static struct dso *load_library(const char *name)
                }
                if (strlen(name) > NAME_MAX) return 0;
                fd = -1;
-               if (r_path) fd = path_open(name, r_path, buf, sizeof buf);
-               if (fd < 0 && env_path) fd = path_open(name, env_path, buf, sizeof buf);
+               if (env_path) fd = path_open(name, env_path, buf, sizeof buf);
+               for (p=needed_by; fd < 0 && p; p=p->needed_by)
+                       if (!fixup_rpath(p, buf, sizeof buf))
+                               fd = path_open(name, p->rpath, buf, sizeof buf);
                if (fd < 0) {
                        if (!sys_path) {
                                char *prefix = 0;
@@ -597,6 +665,7 @@ static struct dso *load_library(const char *name)
        p->dev = st.st_dev;
        p->ino = st.st_ino;
        p->refcnt = 1;
+       p->needed_by = needed_by;
        p->name = p->buf;
        strcpy(p->name, pathname);
        /* Add a shortname only if name arg was not an explicit pathname. */
@@ -638,13 +707,9 @@ static void load_deps(struct dso *p)
        size_t i, ndeps=0;
        struct dso ***deps = &p->deps, **tmp, *dep;
        for (; p; p=p->next) {
-               for (i=0; p->dynv[i]; i+=2) {
-                       if (p->dynv[i] != DT_RPATH) continue;
-                       r_path = (void *)(p->strings + p->dynv[i+1]);
-               }
                for (i=0; p->dynv[i]; i+=2) {
                        if (p->dynv[i] != DT_NEEDED) continue;
-                       dep = load_library(p->strings + p->dynv[i+1]);
+                       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)",
@@ -662,7 +727,6 @@ static void load_deps(struct dso *p)
                                *deps = tmp;
                        }
                }
-               r_path = 0;
        }
 }
 
@@ -675,7 +739,7 @@ static void load_preload(char *s)
                for (z=s; *z && !isspace(*z); z++);
                tmp = *z;
                *z = 0;
-               load_library(s);
+               load_library(s, 0);
                *z = tmp;
        }
 }
@@ -782,6 +846,19 @@ void _dl_debug_state(void)
 {
 }
 
+void __reset_tls()
+{
+       pthread_t self = __pthread_self();
+       struct dso *p;
+       for (p=head; p; p=p->next) {
+               if (!p->tls_id || !self->dtv[p->tls_id]) continue;
+               memcpy(self->dtv[p->tls_id], p->tls_image, p->tls_len);
+               memset((char *)self->dtv[p->tls_id]+p->tls_len, 0,
+                       p->tls_size - p->tls_len);
+               if (p->tls_id == (size_t)self->dtv[0]) break;
+       }
+}
+
 void *__copy_tls(unsigned char *mem)
 {
        pthread_t td;
@@ -899,6 +976,7 @@ void *__dynlink(int argc, char **argv)
          || aux[AT_GID]!=aux[AT_EGID] || aux[AT_SECURE]) {
                env_path = 0;
                env_preload = 0;
+               libc.secure = 1;
        }
 
        /* If the dynamic linker was invoked as a program itself, AT_BASE
@@ -915,6 +993,7 @@ void *__dynlink(int argc, char **argv)
        lib->base = (void *)aux[AT_BASE];
        lib->name = lib->shortname = "libc.so";
        lib->global = 1;
+       lib->kernel_mapped = 1;
        ehdr = (void *)lib->base;
        lib->phnum = ehdr->e_phnum;
        lib->phdr = (void *)(aux[AT_BASE]+ehdr->e_phoff);
@@ -943,7 +1022,12 @@ void *__dynlink(int argc, char **argv)
                }
                if (app->tls_size) app->tls_image = (char *)app->base + tls_image;
                if (interp_off) lib->name = (char *)app->base + interp_off;
-               app->name = argv[0];
+               if ((aux[0] & (1UL<<AT_EXECFN))
+                   && strncmp((char *)aux[AT_EXECFN], "/proc/", 6))
+                       app->name = (char *)aux[AT_EXECFN];
+               else
+                       app->name = argv[0];
+               app->kernel_mapped = 1;
                app->dynv = (void *)(app->base + find_dyn(
                        (void *)aux[AT_PHDR], aux[AT_PHNUM], aux[AT_PHENT]));
                find_map_range((void *)aux[AT_PHDR],
@@ -1136,7 +1220,7 @@ void *dlopen(const char *file, int mode)
                p = 0;
                errflag = 1;
                goto end;
-       } else p = load_library(file);
+       } else p = load_library(file, head);
 
        if (!p) {
                snprintf(errbuf, sizeof errbuf, noload ?