change sigset_t functions to restrict to _NSIG
[musl] / src / ldso / dynlink.c
index ff41683..ad987b8 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;
@@ -156,7 +157,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 +166,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;
        }
 
@@ -305,6 +306,7 @@ static void reclaim_gaps(unsigned char *base, Phdr *ph, size_t phent, size_t phc
 static void *map_library(int fd, struct dso *dso)
 {
        Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
+       void *allocated_buf=0;
        size_t phsize;
        size_t addr_min=SIZE_MAX, addr_max=0, map_len;
        size_t this_min, this_max;
@@ -312,19 +314,28 @@ static void *map_library(int fd, struct dso *dso)
        Ehdr *eh;
        Phdr *ph, *ph0;
        unsigned prot;
-       unsigned char *map, *base;
-       size_t dyn;
+       unsigned char *map=MAP_FAILED, *base;
+       size_t dyn=0;
        size_t tls_image=0;
        size_t i;
 
        ssize_t l = read(fd, buf, sizeof buf);
-       if (l<sizeof *eh) return 0;
        eh = buf;
+       if (l<0) return 0;
+       if (l<sizeof *eh || (eh->e_type != ET_DYN && eh->e_type != ET_EXEC))
+               goto noexec;
        phsize = eh->e_phentsize * eh->e_phnum;
-       if (phsize + sizeof *eh > l) return 0;
-       if (eh->e_phoff + phsize > l) {
+       if (phsize > sizeof buf - sizeof *eh) {
+               allocated_buf = malloc(phsize);
+               if (!allocated_buf) return 0;
+               l = pread(fd, allocated_buf, phsize, eh->e_phoff);
+               if (l < 0) goto error;
+               if (l != phsize) goto noexec;
+               ph = ph0 = allocated_buf;
+       } else if (eh->e_phoff + phsize > l) {
                l = pread(fd, buf+1, phsize, eh->e_phoff);
-               if (l != phsize) return 0;
+               if (l < 0) goto error;
+               if (l != phsize) goto noexec;
                ph = ph0 = (void *)(buf + 1);
        } else {
                ph = ph0 = (void *)((char *)buf + eh->e_phoff);
@@ -350,7 +361,7 @@ static void *map_library(int fd, struct dso *dso)
                        addr_max = ph->p_vaddr+ph->p_memsz;
                }
        }
-       if (!dyn) return 0;
+       if (!dyn) goto noexec;
        addr_max += PAGE_SIZE-1;
        addr_max &= -PAGE_SIZE;
        addr_min &= -PAGE_SIZE;
@@ -361,7 +372,13 @@ static void *map_library(int fd, struct dso *dso)
         * use the invalid part; we just need to reserve the right
         * amount of virtual address space to map over later. */
        map = mmap((void *)addr_min, map_len, prot, MAP_PRIVATE, fd, off_start);
-       if (map==MAP_FAILED) return 0;
+       if (map==MAP_FAILED) goto error;
+       /* If the loaded file is not relocatable and the requested address is
+        * not available, then the load operation must fail. */
+       if (eh->e_type != ET_DYN && addr_min && map!=(void *)addr_min) {
+               errno = EBUSY;
+               goto error;
+       }
        base = map - addr_min;
        dso->phdr = 0;
        dso->phnum = 0;
@@ -405,9 +422,13 @@ 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;
 error:
-       munmap(map, map_len);
+       if (map!=MAP_FAILED) munmap(map, map_len);
+       free(allocated_buf);
        return 0;
 }
 
@@ -436,6 +457,8 @@ static void decode_dyn(struct dso *p)
                p->hashtab = (void *)(p->base + dyn[DT_HASH]);
        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)
@@ -448,6 +471,7 @@ static struct dso *load_library(const char *name)
        struct stat st;
        size_t alloc_size;
        int n_th = 0;
+       int is_self = 0;
 
        /* Catch and block attempts to reload the implementation itself */
        if (name[0]=='l' && name[1]=='i' && name[2]=='b') {
@@ -456,17 +480,33 @@ static struct dso *load_library(const char *name)
                char *z = strchr(name, '.');
                if (z) {
                        size_t l = z-name;
-                       for (rp=reserved; *rp && memcmp(name+3, rp, l-3); rp+=strlen(rp)+1);
+                       for (rp=reserved; *rp && strncmp(name+3, rp, l-3); rp+=strlen(rp)+1);
                        if (*rp) {
-                               if (!ldso->prev) {
-                                       tail->next = ldso;
-                                       ldso->prev = tail;
-                                       tail = ldso->next ? ldso->next : ldso;
+                               if (ldd_mode) {
+                                       /* Track which names have been resolved
+                                        * and only report each one once. */
+                                       static unsigned reported;
+                                       unsigned mask = 1U<<(rp-reserved);
+                                       if (!(reported & mask)) {
+                                               reported |= mask;
+                                               dprintf(1, "\t%s => %s (%p)\n",
+                                                       name, ldso->name,
+                                                       ldso->base);
+                                       }
                                }
-                               return ldso;
+                               is_self = 1;
                        }
                }
        }
+       if (!strcmp(name, ldso->name)) is_self = 1;
+       if (is_self) {
+               if (!ldso->prev) {
+                       tail->next = ldso;
+                       ldso->prev = tail;
+                       tail = ldso->next ? ldso->next : ldso;
+               }
+               return ldso;
+       }
        if (strchr(name, '/')) {
                pathname = name;
                fd = open(name, O_RDONLY|O_CLOEXEC);
@@ -480,8 +520,8 @@ 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);
+               if (fd < 0 && r_path) fd = path_open(name, r_path, buf, sizeof buf);
                if (fd < 0) {
                        if (!sys_path) {
                                char *prefix = 0;
@@ -703,7 +743,7 @@ static void do_fini()
                        while (n--) ((void (*)(void))*--fn)();
                }
 #ifndef NO_LEGACY_INITFINI
-               if (dyn[0] & (1<<DT_FINI))
+               if ((dyn[0] & (1<<DT_FINI)) && dyn[DT_FINI])
                        ((void (*)(void))(p->base + dyn[DT_FINI]))();
 #endif
        }
@@ -726,7 +766,7 @@ static void do_init_fini(struct dso *p)
                        fini_head = p;
                }
 #ifndef NO_LEGACY_INITFINI
-               if (dyn[0] & (1<<DT_INIT))
+               if ((dyn[0] & (1<<DT_INIT)) && dyn[DT_INIT])
                        ((void (*)(void))(p->base + dyn[DT_INIT]))();
 #endif
                if (dyn[0] & (1<<DT_INIT_ARRAY)) {
@@ -746,6 +786,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;
@@ -884,7 +937,7 @@ void *__dynlink(int argc, char **argv)
        lib->phdr = (void *)(aux[AT_BASE]+ehdr->e_phoff);
        find_map_range(lib->phdr, ehdr->e_phnum, ehdr->e_phentsize, lib);
        lib->dynv = (void *)(lib->base + find_dyn(lib->phdr,
-                    ehdr->e_phnum, ehdr->e_phentsize));
+               ehdr->e_phnum, ehdr->e_phentsize));
        decode_dyn(lib);
 
        if (aux[AT_PHDR]) {
@@ -941,6 +994,16 @@ void *__dynlink(int argc, char **argv)
                lib->name = ldname;
                app->name = argv[0];
                aux[AT_ENTRY] = (size_t)app->base + ehdr->e_entry;
+               /* Find the name that would have been used for the dynamic
+                * linker had ldd not taken its place. */
+               if (ldd_mode) {
+                       for (i=0; i<app->phnum; i++) {
+                               if (app->phdr[i].p_type == PT_INTERP)
+                                       lib->name = (void *)(app->base
+                                               + app->phdr[i].p_vaddr);
+                       }
+                       dprintf(1, "\t%s (%p)\n", lib->name, lib->base);
+               }
        }
        if (app->tls_size) {
                app->tls_id = tls_cnt = 1;