fix order of fini_array execution for shared libs
[musl] / src / ldso / dynlink.c
index 782e979..829696f 100644 (file)
@@ -99,6 +99,7 @@ static int ssp_used;
 static int runtime;
 static int ldd_mode;
 static int ldso_fail;
+static int noload;
 static jmp_buf rtld_fail;
 static pthread_rwlock_t lock;
 static struct debug debug;
@@ -308,7 +309,7 @@ static void *map_library(int fd, struct dso *dso)
        size_t this_min, this_max;
        off_t off_start;
        Ehdr *eh;
-       Phdr *ph;
+       Phdr *ph, *ph0;
        unsigned prot;
        unsigned char *map, *base;
        size_t dyn;
@@ -323,11 +324,10 @@ static void *map_library(int fd, struct dso *dso)
        if (eh->e_phoff + phsize > l) {
                l = pread(fd, buf+1, phsize, eh->e_phoff);
                if (l != phsize) return 0;
-               eh->e_phoff = sizeof *eh;
+               ph = ph0 = (void *)(buf + 1);
+       } else {
+               ph = ph0 = (void *)((char *)buf + eh->e_phoff);
        }
-       ph = (void *)((char *)buf + eh->e_phoff);
-       dso->phdr = ph;
-       dso->phnum = eh->e_phnum;
        for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
                if (ph->p_type == PT_DYNAMIC)
                        dyn = ph->p_vaddr;
@@ -362,9 +362,18 @@ static void *map_library(int fd, struct dso *dso)
        map = mmap((void *)addr_min, map_len, prot, MAP_PRIVATE, fd, off_start);
        if (map==MAP_FAILED) return 0;
        base = map - addr_min;
-       ph = (void *)((char *)buf + eh->e_phoff);
-       for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
+       dso->phdr = 0;
+       dso->phnum = 0;
+       for (ph=ph0, i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
                if (ph->p_type != PT_LOAD) continue;
+               /* Check if the programs headers are in this load segment, and
+                * if so, record the address for use by dl_iterate_phdr. */
+               if (!dso->phdr && eh->e_phoff >= ph->p_offset
+                   && eh->e_phoff+phsize <= ph->p_offset+ph->p_filesz) {
+                       dso->phdr = (void *)(base + ph->p_vaddr
+                               + (eh->e_phoff-ph->p_offset));
+                       dso->phnum = eh->e_phnum;
+               }
                /* Reuse the existing mapping for the lowest-address LOAD */
                if ((ph->p_vaddr & -PAGE_SIZE) == addr_min) continue;
                this_min = ph->p_vaddr & -PAGE_SIZE;
@@ -389,8 +398,7 @@ static void *map_library(int fd, struct dso *dso)
                                goto error;
                        break;
                }
-       if (!runtime) reclaim_gaps(base, (void *)((char *)buf + eh->e_phoff),
-               eh->e_phentsize, eh->e_phnum);
+       if (!runtime) reclaim_gaps(base, ph0, eh->e_phentsize, eh->e_phnum);
        dso->map = map;
        dso->map_len = map_len;
        dso->base = base;
@@ -402,16 +410,16 @@ error:
        return 0;
 }
 
-static int path_open(const char *name, const char *search, char *buf, size_t buf_size)
+static int path_open(const char *name, const char *s, char *buf, size_t buf_size)
 {
-       const char *s=search, *z;
-       int l, fd;
+       size_t l;
+       int fd;
        for (;;) {
-               while (*s==':') s++;
-               if (!*s) return -1;
-               z = strchr(s, ':');
-               l = z ? z-s : strlen(s);
-               snprintf(buf, buf_size, "%.*s/%s", l, s, name);
+               s += strspn(s, ":\n");
+               l = strcspn(s, ":\n");
+               if (l-1 >= INT_MAX) return -1;
+               if (snprintf(buf, buf_size, "%.*s/%s", (int)l, s, name) >= buf_size)
+                       continue;
                if ((fd = open(buf, O_RDONLY|O_CLOEXEC))>=0) return fd;
                s += l;
        }
@@ -434,7 +442,6 @@ static struct dso *load_library(const char *name)
        char buf[2*NAME_MAX+2];
        const char *pathname;
        unsigned char *map;
-       size_t map_len;
        struct dso *p, temp_dso = {0};
        int fd;
        struct stat st;
@@ -476,12 +483,30 @@ static struct dso *load_library(const char *name)
                if (fd < 0 && env_path) fd = path_open(name, env_path, buf, sizeof buf);
                if (fd < 0) {
                        if (!sys_path) {
-                               FILE *f = fopen(ETC_LDSO_PATH, "rbe");
+                               char *prefix = 0;
+                               size_t prefix_len;
+                               if (ldso->name[0]=='/') {
+                                       char *s, *t, *z;
+                                       for (s=t=z=ldso->name; *s; s++)
+                                               if (*s=='/') z=t, t=s;
+                                       prefix_len = z-ldso->name;
+                                       if (prefix_len < PATH_MAX)
+                                               prefix = ldso->name;
+                               }
+                               if (!prefix) {
+                                       prefix = "";
+                                       prefix_len = 0;
+                               }
+                               char etc_ldso_path[prefix_len + 1
+                                       + sizeof "/etc/ld-musl-" LDSO_ARCH ".path"];
+                               snprintf(etc_ldso_path, sizeof etc_ldso_path,
+                                       "%.*s/etc/ld-musl-" LDSO_ARCH ".path",
+                                       (int)prefix_len, prefix);
+                               FILE *f = fopen(etc_ldso_path, "rbe");
                                if (f) {
-                                       if (getline(&sys_path, (size_t[1]){0}, f) > 0) {
-                                               size_t l = strlen(sys_path);
-                                               if (l && sys_path[l-1]=='\n')
-                                                       sys_path[l-1] = 0;
+                                       if (getdelim(&sys_path, (size_t[1]){0}, 0, f) <= 0) {
+                                               free(sys_path);
+                                               sys_path = "";
                                        }
                                        fclose(f);
                                }
@@ -508,7 +533,7 @@ static struct dso *load_library(const char *name)
                        return p;
                }
        }
-       map = map_library(fd, &temp_dso);
+       map = noload ? 0 : map_library(fd, &temp_dso);
        close(fd);
        if (!map) return 0;
 
@@ -527,7 +552,7 @@ static struct dso *load_library(const char *name)
        }
        p = calloc(1, alloc_size);
        if (!p) {
-               munmap(map, map_len);
+               munmap(map, temp_dso.map_len);
                return 0;
        }
        memcpy(p, &temp_dso, sizeof temp_dso);
@@ -541,8 +566,8 @@ static struct dso *load_library(const char *name)
        if (pathname != name) p->shortname = strrchr(p->name, '/')+1;
        if (p->tls_image) {
                if (runtime && !__pthread_self_init()) {
+                       munmap(map, p->map_len);
                        free(p);
-                       munmap(map, map_len);
                        return 0;
                }
                p->tls_id = ++tls_cnt;
@@ -671,7 +696,13 @@ static void do_fini()
        for (p=fini_head; p; p=p->fini_next) {
                if (!p->constructed) continue;
                decode_vec(p->dynv, dyn, DYN_CNT);
-               ((void (*)(void))(p->base + dyn[DT_FINI]))();
+               if (dyn[0] & (1<<DT_FINI_ARRAY)) {
+                       size_t n = dyn[DT_FINI_ARRAYSZ]/sizeof(size_t);
+                       size_t *fn = (size_t *)(p->base + dyn[DT_FINI_ARRAY])+n;
+                       while (n--) ((void (*)(void))*--fn)();
+               }
+               if (dyn[0] & (1<<DT_FINI))
+                       ((void (*)(void))(p->base + dyn[DT_FINI]))();
        }
 }
 
@@ -687,12 +718,21 @@ static void do_init_fini(struct dso *p)
                if (p->constructed) continue;
                p->constructed = 1;
                decode_vec(p->dynv, dyn, DYN_CNT);
-               if (dyn[0] & (1<<DT_FINI)) {
+               if (dyn[0] & ((1<<DT_FINI) | (1<<DT_FINI_ARRAY))) {
                        p->fini_next = fini_head;
                        fini_head = p;
                }
                if (dyn[0] & (1<<DT_INIT))
                        ((void (*)(void))(p->base + dyn[DT_INIT]))();
+               if (dyn[0] & (1<<DT_INIT_ARRAY)) {
+                       size_t n = dyn[DT_INIT_ARRAYSZ]/sizeof(size_t);
+                       size_t *fn = (void *)(p->base + dyn[DT_INIT_ARRAY]);
+                       while (n--) ((void (*)(void))*fn++)();
+               }
+               if (!need_locking && libc.threads_minus_1) {
+                       need_locking = 1;
+                       pthread_mutex_lock(&init_fini_lock);
+               }
        }
        if (need_locking) pthread_mutex_unlock(&init_fini_lock);
 }
@@ -740,13 +780,13 @@ void *__copy_tls(unsigned char *mem)
 void *__tls_get_addr(size_t *v)
 {
        pthread_t self = __pthread_self();
-       if (self->dtv && v[0]<=(size_t)self->dtv[0] && self->dtv[v[0]])
+       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 (self->dtv && v[0]<=(size_t)self->dtv[0] && self->dtv[v[0]]) {
+       if (v[0]<=(size_t)self->dtv[0] && self->dtv[v[0]]) {
                pthread_sigmask(SIG_SETMASK, &set, 0);
                return (char *)self->dtv[v[0]]+v[1];
        }
@@ -759,10 +799,10 @@ void *__tls_get_addr(size_t *v)
        for (p=head; p->tls_id != v[0]; p=p->next);
 
        /* Get new DTV space from new DSO if needed */
-       if (!self->dtv || v[0] > (size_t)self->dtv[0]) {
+       if (v[0] > (size_t)self->dtv[0]) {
                void **newdtv = p->new_dtv +
                        (v[0]+1)*sizeof(void *)*a_fetch_add(&p->new_dtv_idx,1);
-               if (self->dtv) memcpy(newdtv, self->dtv,
+               memcpy(newdtv, self->dtv,
                        ((size_t)self->dtv[0]+1) * sizeof(void *));
                newdtv[0] = (void *)v[0];
                self->dtv = newdtv;
@@ -1027,6 +1067,7 @@ void *dlopen(const char *file, int mode)
        orig_tls_offset = tls_offset;
        orig_tls_align = tls_align;
        orig_tail = tail;
+       noload = mode & RTLD_NOLOAD;
 
        if (setjmp(rtld_fail)) {
                /* Clean up anything new that was (partially) loaded */
@@ -1050,8 +1091,10 @@ void *dlopen(const char *file, int mode)
        } else p = load_library(file);
 
        if (!p) {
-               snprintf(errbuf, sizeof errbuf,
-                       "Error loading shared library %s: %m", file);
+               snprintf(errbuf, sizeof errbuf, noload ?
+                       "Library %s is not already loaded" :
+                       "Error loading shared library %s: %m",
+                       file);
                errflag = 1;
                goto end;
        }
@@ -1091,7 +1134,7 @@ end:
        return p;
 }
 
-static int invalid_dso_handle(struct dso *h)
+static int invalid_dso_handle(void *h)
 {
        struct dso *p;
        for (p=head; p; p=p->next) if (h==p) return 0;
@@ -1119,7 +1162,8 @@ static void *do_dlsym(struct dso *p, const char *s, void *ra)
                        return __tls_get_addr((size_t []){def.dso->tls_id, def.sym->st_value});
                return def.dso->base + def.sym->st_value;
        }
-       if (invalid_dso_handle(p)) return 0;
+       if (p != RTLD_DEFAULT && p != RTLD_NEXT && invalid_dso_handle(p))
+               return 0;
        if (p->ghashtab) {
                gh = gnu_hash(s);
                sym = gnu_lookup(s, gh, p);
@@ -1188,7 +1232,7 @@ int __dladdr(void *addr, Dl_info *info)
        }
 
        for (; nsym; nsym--, sym++) {
-               if (sym->st_shndx && sym->st_value
+               if (sym->st_value
                 && (1<<(sym->st_info&0xf) & OK_TYPES)
                 && (1<<(sym->st_info>>4) & OK_BINDS)) {
                        void *symaddr = p->base + sym->st_value;
@@ -1246,7 +1290,7 @@ int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void
        return ret;
 }
 #else
-static int invalid_dso_handle(struct dso *h)
+static int invalid_dso_handle(void *h)
 {
        snprintf(errbuf, sizeof errbuf, "Invalid library handle %p", (void *)h);
        errflag = 1;
@@ -1266,6 +1310,18 @@ int __dladdr (void *addr, Dl_info *info)
 }
 #endif
 
+int __dlinfo(void *dso, int req, void *res)
+{
+       if (invalid_dso_handle(dso)) return -1;
+       if (req != RTLD_DI_LINKMAP) {
+               snprintf(errbuf, sizeof errbuf, "Unsupported request %d", req);
+               errflag = 1;
+               return -1;
+       }
+       *(struct link_map **)res = dso;
+       return 0;
+}
+
 char *dlerror()
 {
        if (!errflag) return 0;