further refactoring of dynamic linker load address computations
[musl] / src / ldso / dynlink.c
index 4e6a5c8..6e79a77 100644 (file)
@@ -122,6 +122,9 @@ static int dl_strcmp(const char *l, const char *r)
 }
 #define strcmp(l,r) dl_strcmp(l,r)
 
+/* Compute load address for a virtual address in a given dso. */
+#define laddr(p, v) (void *)((p)->base + (v))
+
 static void decode_vec(size_t *v, size_t *a, size_t cnt)
 {
        size_t i;
@@ -174,35 +177,29 @@ static Sym *sysv_lookup(const char *s, uint32_t h, struct dso *dso)
        return 0;
 }
 
-static Sym *gnu_lookup(const char *s, uint32_t h1, struct dso *dso)
+static Sym *gnu_lookup(uint32_t h1, uint32_t *hashtab, struct dso *dso, const char *s)
 {
-       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 i = buckets[h1 % nbuckets];
 
        if (!i) return 0;
 
-       hashval = buckets + nbuckets + (i - hashtab[1]);
+       uint32_t *hashval = buckets + nbuckets + (i - hashtab[1]);
 
        for (h1 |= 1; ; i++) {
-               h2 = *hashval++;
-               if ((!dso->versym || dso->versym[i] >= 0)
-                   && (h1 == (h2|1)) && !strcmp(s, strings + syms[i].st_name))
-                       return syms+i;
+               uint32_t h2 = *hashval++;
+               if ((h1 == (h2|1)) && (!dso->versym || dso->versym[i] >= 0)
+                   && !strcmp(s, dso->strings + dso->syms[i].st_name))
+                       return dso->syms+i;
                if (h2 & 1) break;
        }
 
        return 0;
 }
 
-static Sym *gnu_lookup_filtered(const char *s, uint32_t h1, struct dso *dso, uint32_t fofs, size_t fmask)
+static Sym *gnu_lookup_filtered(uint32_t h1, uint32_t *hashtab, struct dso *dso, const char *s, uint32_t fofs, size_t fmask)
 {
-       uint32_t *hashtab = dso->ghashtab;
        const size_t *bloomwords = (const void *)(hashtab+4);
        size_t f = bloomwords[fofs & (hashtab[2]-1)];
        if (!(f & fmask)) return 0;
@@ -210,7 +207,7 @@ static Sym *gnu_lookup_filtered(const char *s, uint32_t h1, struct dso *dso, uin
        f >>= (h1 >> hashtab[3]) % (8 * sizeof f);
        if (!(f & 1)) return 0;
 
-       return gnu_lookup(s, h1, dso);
+       return gnu_lookup(h1, hashtab, dso, s);
 }
 
 #define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON | 1<<STT_TLS)
@@ -222,20 +219,20 @@ static Sym *gnu_lookup_filtered(const char *s, uint32_t h1, struct dso *dso, uin
 
 static struct symdef find_sym(struct dso *dso, const char *s, int need_def)
 {
-       uint32_t h = 0, gh, gho;
+       uint32_t h = 0, gh, gho, *ght;
        size_t ghm = 0;
        struct symdef def = {0};
        for (; dso; dso=dso->next) {
                Sym *sym;
                if (!dso->global) continue;
-               if (dso->ghashtab) {
+               if ((ght = dso->ghashtab)) {
                        if (!ghm) {
                                gh = gnu_hash(s);
                                int maskbits = 8 * sizeof ghm;
                                gho = gh / maskbits;
                                ghm = 1ul << gh % maskbits;
                        }
-                       sym = gnu_lookup_filtered(s, gh, dso, gho, ghm);
+                       sym = gnu_lookup_filtered(gh, ght, dso, s, gho, ghm);
                } else {
                        if (!h) h = sysv_hash(s);
                        sym = sysv_lookup(s, h, dso);
@@ -291,7 +288,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
                type = R_TYPE(rel[1]);
                if (type == REL_NONE) continue;
                sym_index = R_SYM(rel[1]);
-               reloc_addr = (void *)(base + rel[0]);
+               reloc_addr = laddr(dso, rel[0]);
                if (sym_index) {
                        sym = syms + sym_index;
                        name = strings + sym->st_name;
@@ -325,7 +322,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
                        addend = *reloc_addr;
                }
 
-               sym_val = def.sym ? (size_t)def.dso->base+def.sym->st_value : 0;
+               sym_val = def.sym ? (size_t)laddr(def.dso, def.sym->st_value) : 0;
                tls_val = def.sym ? def.sym->st_value : 0;
 
                switch(type) {
@@ -420,8 +417,8 @@ static void reclaim(struct dso *dso, size_t start, size_t end)
        start = start + 6*sizeof(size_t)-1 & -4*sizeof(size_t);
        end = (end & -4*sizeof(size_t)) - 2*sizeof(size_t);
        if (start>end || end-start < 4*sizeof(size_t)) return;
-       a = (size_t *)(dso->base + start);
-       z = (size_t *)(dso->base + end);
+       a = laddr(dso, start);
+       z = laddr(dso, end);
        a[-2] = 1;
        a[-1] = z[0] = end-start + 2*sizeof(size_t) | 1;
        z[1] = 1;
@@ -693,18 +690,18 @@ static void decode_dyn(struct dso *p)
 {
        size_t dyn[DYN_CNT];
        decode_vec(p->dynv, dyn, DYN_CNT);
-       p->syms = (void *)(p->base + dyn[DT_SYMTAB]);
-       p->strings = (void *)(p->base + dyn[DT_STRTAB]);
+       p->syms = laddr(p, dyn[DT_SYMTAB]);
+       p->strings = laddr(p, dyn[DT_STRTAB]);
        if (dyn[0]&(1<<DT_HASH))
-               p->hashtab = (void *)(p->base + dyn[DT_HASH]);
+               p->hashtab = laddr(p, dyn[DT_HASH]);
        if (dyn[0]&(1<<DT_RPATH))
                p->rpath_orig = (void *)(p->strings + dyn[DT_RPATH]);
        if (dyn[0]&(1<<DT_RUNPATH))
                p->rpath_orig = (void *)(p->strings + dyn[DT_RUNPATH]);
        if (search_vec(p->dynv, dyn, DT_GNU_HASH))
-               p->ghashtab = (void *)(p->base + *dyn);
+               p->ghashtab = laddr(p, *dyn);
        if (search_vec(p->dynv, dyn, DT_VERSYM))
-               p->versym = (void *)(p->base + *dyn);
+               p->versym = laddr(p, *dyn);
 }
 
 static struct dso *load_library(const char *name, struct dso *needed_by)
@@ -986,7 +983,7 @@ static void kernel_mapped_dso(struct dso *p)
        Phdr *ph = p->phdr;
        for (cnt = p->phnum; cnt--; ph = (void *)((char *)ph + p->phentsize)) {
                if (ph->p_type == PT_DYNAMIC) {
-                       p->dynv = (void *)(p->base + ph->p_vaddr);
+                       p->dynv = laddr(p, ph->p_vaddr);
                } else if (ph->p_type == PT_GNU_RELRO) {
                        p->relro_start = ph->p_vaddr & -PAGE_SIZE;
                        p->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE;
@@ -1013,12 +1010,12 @@ static void do_fini()
                decode_vec(p->dynv, dyn, DYN_CNT);
                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;
+                       size_t *fn = (size_t *)laddr(p, dyn[DT_FINI_ARRAY])+n;
                        while (n--) ((void (*)(void))*--fn)();
                }
 #ifndef NO_LEGACY_INITFINI
                if ((dyn[0] & (1<<DT_FINI)) && dyn[DT_FINI])
-                       ((void (*)(void))(p->base + dyn[DT_FINI]))();
+                       ((void (*)(void))laddr(p, dyn[DT_FINI]))();
 #endif
        }
 }
@@ -1041,11 +1038,11 @@ static void do_init_fini(struct dso *p)
                }
 #ifndef NO_LEGACY_INITFINI
                if ((dyn[0] & (1<<DT_INIT)) && dyn[DT_INIT])
-                       ((void (*)(void))(p->base + dyn[DT_INIT]))();
+                       ((void (*)(void))laddr(p, dyn[DT_INIT]))();
 #endif
                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]);
+                       size_t *fn = laddr(p, dyn[DT_INIT_ARRAY]);
                        while (n--) ((void (*)(void))*fn++)();
                }
                if (!need_locking && libc.threads_minus_1) {
@@ -1282,8 +1279,8 @@ _Noreturn void __dls3(size_t *sp)
                                app.tls_align = phdr->p_align;
                        }
                }
-               if (app.tls_size) app.tls_image = (char *)app.base + tls_image;
-               if (interp_off) ldso.name = (char *)app.base + interp_off;
+               if (app.tls_size) app.tls_image = laddr(&app, tls_image);
+               if (interp_off) ldso.name = laddr(&app, interp_off);
                if ((aux[0] & (1UL<<AT_EXECFN))
                    && strncmp((char *)aux[AT_EXECFN], "/proc/", 6))
                        app.name = (char *)aux[AT_EXECFN];
@@ -1340,7 +1337,7 @@ _Noreturn void __dls3(size_t *sp)
                close(fd);
                ldso.name = ldname;
                app.name = argv[0];
-               aux[AT_ENTRY] = (size_t)app.base + ehdr->e_entry;
+               aux[AT_ENTRY] = (size_t)laddr(&app, ehdr->e_entry);
                /* Find the name that would have been used for the dynamic
                 * linker had ldd not taken its place. */
                if (ldd_mode) {
@@ -1563,7 +1560,7 @@ void *__tls_get_addr(size_t *);
 static void *do_dlsym(struct dso *p, const char *s, void *ra)
 {
        size_t i;
-       uint32_t h = 0, gh = 0;
+       uint32_t h = 0, gh = 0, *ght;
        Sym *sym;
        if (p == head || p == RTLD_DEFAULT || p == RTLD_NEXT) {
                if (p == RTLD_DEFAULT) {
@@ -1577,13 +1574,13 @@ static void *do_dlsym(struct dso *p, const char *s, void *ra)
                if (!def.sym) goto failed;
                if ((def.sym->st_info&0xf) == STT_TLS)
                        return __tls_get_addr((size_t []){def.dso->tls_id, def.sym->st_value});
-               return def.dso->base + def.sym->st_value;
+               return laddr(def.dso, def.sym->st_value);
        }
        if (invalid_dso_handle(p))
                return 0;
-       if (p->ghashtab) {
+       if ((ght = p->ghashtab)) {
                gh = gnu_hash(s);
-               sym = gnu_lookup(s, gh, p);
+               sym = gnu_lookup(gh, ght, p, s);
        } else {
                h = sysv_hash(s);
                sym = sysv_lookup(s, h, p);
@@ -1591,11 +1588,11 @@ static void *do_dlsym(struct dso *p, const char *s, void *ra)
        if (sym && (sym->st_info&0xf) == STT_TLS)
                return __tls_get_addr((size_t []){p->tls_id, sym->st_value});
        if (sym && sym->st_value && (1<<(sym->st_info&0xf) & OK_TYPES))
-               return p->base + sym->st_value;
+               return laddr(p, sym->st_value);
        if (p->deps) for (i=0; p->deps[i]; i++) {
-               if (p->deps[i]->ghashtab) {
+               if ((ght = p->deps[i]->ghashtab)) {
                        if (!gh) gh = gnu_hash(s);
-                       sym = gnu_lookup(s, gh, p->deps[i]);
+                       sym = gnu_lookup(gh, ght, p->deps[i], s);
                } else {
                        if (!h) h = sysv_hash(s);
                        sym = sysv_lookup(s, h, p->deps[i]);
@@ -1603,7 +1600,7 @@ static void *do_dlsym(struct dso *p, const char *s, void *ra)
                if (sym && (sym->st_info&0xf) == STT_TLS)
                        return __tls_get_addr((size_t []){p->deps[i]->tls_id, sym->st_value});
                if (sym && sym->st_value && (1<<(sym->st_info&0xf) & OK_TYPES))
-                       return p->deps[i]->base + sym->st_value;
+                       return laddr(p->deps[i], sym->st_value);
        }
 failed:
        error("Symbol not found: %s", s);
@@ -1651,7 +1648,7 @@ int __dladdr(const void *addr, Dl_info *info)
                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;
+                       void *symaddr = laddr(p, sym->st_value);
                        if (symaddr > addr || symaddr < best)
                                continue;
                        best = symaddr;