diff --git a/porting/linux/user/ldso/dynlink.c b/porting/linux/user/ldso/dynlink.c index 45c5fbde54bdf7e8bba588bc28b78b46cd30c09c..f6d789b1a65e9a9e8210e17c162dddde91e32dc7 100644 --- a/porting/linux/user/ldso/dynlink.c +++ b/porting/linux/user/ldso/dynlink.c @@ -105,7 +105,7 @@ struct reserved_address_params { #endif }; -typedef void (*stage3_func)(size_t *, size_t *); +typedef void (*stage3_func)(size_t *, size_t *, size_t *); static struct builtin_tls { char c[8]; @@ -2609,6 +2609,9 @@ hidden void __dls2(unsigned char *base, size_t *sp) } else { ldso.base = base; } + size_t aux[AUX_CNT]; + decode_vec(auxv, aux, AUX_CNT); + libc.page_size = aux[AT_PAGESZ]; Ehdr *ehdr = (void *)ldso.base; ldso.name = ldso.shortname = "libc.so"; ldso.phnum = ehdr->e_phnum; @@ -2645,8 +2648,8 @@ hidden void __dls2(unsigned char *base, size_t *sp) * symbolically as a barrier against moving the address * load across the above relocation processing. */ struct symdef dls2b_def = find_sym(&ldso, "__dls2b", 0); - if (DL_FDPIC) ((stage3_func)&ldso.funcdescs[dls2b_def.sym-ldso.syms])(sp, auxv); - else ((stage3_func)laddr(&ldso, dls2b_def.sym->st_value))(sp, auxv); + if (DL_FDPIC) ((stage3_func)&ldso.funcdescs[dls2b_def.sym-ldso.syms])(sp, auxv, aux); + else ((stage3_func)laddr(&ldso, dls2b_def.sym->st_value))(sp, auxv, aux); } /* Stage 2b sets up a valid thread pointer, which requires relocations @@ -2655,7 +2658,7 @@ hidden void __dls2(unsigned char *base, size_t *sp) * so that loads of the thread pointer and &errno can be pure/const and * thereby hoistable. */ -void __dls2b(size_t *sp, size_t *auxv) +void __dls2b(size_t *sp, size_t *auxv, size_t *aux) { /* Setup early thread pointer in builtin_tls for ldso/libc itself to * use during dynamic linking. If possible it will also serve as the @@ -2669,8 +2672,8 @@ void __dls2b(size_t *sp, size_t *auxv) } struct symdef dls3_def = find_sym(&ldso, "__dls3", 0); - if (DL_FDPIC) ((stage3_func)&ldso.funcdescs[dls3_def.sym-ldso.syms])(sp, auxv); - else ((stage3_func)laddr(&ldso, dls3_def.sym->st_value))(sp, auxv); + if (DL_FDPIC) ((stage3_func)&ldso.funcdescs[dls3_def.sym-ldso.syms])(sp, auxv, aux); + else ((stage3_func)laddr(&ldso, dls3_def.sym->st_value))(sp, auxv, aux); } /* Stage 3 of the dynamic linker is called with the dynamic linker/libc @@ -2678,10 +2681,9 @@ void __dls2b(size_t *sp, size_t *auxv) * process dependencies and relocations for the main application and * transfer control to its entry point. */ -void __dls3(size_t *sp, size_t *auxv) +void __dls3(size_t *sp, size_t *auxv, size_t *aux) { static struct dso app, vdso; - size_t aux[AUX_CNT]; size_t i; char *env_preload=0; char *replace_argv0=0; @@ -2694,10 +2696,8 @@ void __dls3(size_t *sp, size_t *auxv) /* Find aux vector just past environ[] and use it to initialize * global data that may be needed before we can make syscalls. */ __environ = envp; - decode_vec(auxv, aux, AUX_CNT); search_vec(auxv, &__sysinfo, AT_SYSINFO); __pthread_self()->sysinfo = __sysinfo; - libc.page_size = aux[AT_PAGESZ]; libc.secure = ((aux[0]&0x7800)!=0x7800 || aux[AT_UID]!=aux[AT_EUID] || aux[AT_GID]!=aux[AT_EGID] || aux[AT_SECURE]); @@ -3511,31 +3511,19 @@ void *addr2dso(size_t a) { struct dso *p; size_t i; - if (DL_FDPIC) for (p=head; p; p=p->next) { - i = count_syms(p); - if (a-(size_t)p->funcdescs < i*sizeof(*p->funcdescs)) - return p; - } for (p=head; p; p=p->next) { - if (DL_FDPIC && p->loadmap) { - for (i=0; iloadmap->nsegs; i++) { - if (a-p->loadmap->segs[i].p_vaddr - < p->loadmap->segs[i].p_memsz) - return p; - } - } else { - Phdr *ph = p->phdr; - size_t phcnt = p->phnum; - size_t entsz = p->phentsize; - size_t base = (size_t)p->base; - for (; phcnt--; ph=(void *)((char *)ph+entsz)) { - if (ph->p_type != PT_LOAD) continue; - if (a-base-ph->p_vaddr < ph->p_memsz) - return p; - } - if (a-(size_t)p->map < p->map_len) - return 0; + if (a < p->map || a - (size_t)p->map >= p->map_len) continue; + Phdr *ph = p->phdr; + size_t phcnt = p->phnum; + size_t entsz = p->phentsize; + size_t base = (size_t)p->base; + for (; phcnt--; ph=(void *)((char *)ph+entsz)) { + if (ph->p_type != PT_LOAD) continue; + if (a-base-ph->p_vaddr < ph->p_memsz) + return p; } + if (a-(size_t)p->map < p->map_len) + return 0; } return 0; } @@ -3777,15 +3765,71 @@ hidden int __dlclose(void *p) return rc; } +static inline int sym_is_matched(const Sym* sym, size_t addr_offset_so) { + return sym->st_value && + (1<<(sym->st_info&0xf) != STT_TLS) && + (addr_offset_so >= sym->st_value) && + (addr_offset_so < sym->st_value + sym->st_size); +} + +static inline Sym* find_addr_by_elf(size_t addr_offset_so, struct dso *p) { + uint32_t nsym = p->hashtab[1]; + Sym *sym = p->syms; + for (; nsym; nsym--, sym++) { + if (sym_is_matched(sym, addr_offset_so)) { + return sym; + } + } + + return NULL; +} + +static inline Sym* find_addr_by_gnu(size_t addr_offset_so, struct dso *p) { + + size_t i, nsym, first_hash_sym_index; + uint32_t *hashval; + Sym *sym_tab = p->syms; + uint32_t *buckets= p->ghashtab + 4 + (p->ghashtab[2]*sizeof(size_t)/4); + // Points to the first defined symbol, all symbols before it are undefined. + first_hash_sym_index = buckets[0]; + Sym *sym = &sym_tab[first_hash_sym_index]; + + // Get the location pointed by the last bucket. + for (i = nsym = 0; i < p->ghashtab[0]; i++) { + if (buckets[i] > nsym) + nsym = buckets[i]; + } + + for (i = first_hash_sym_index; i < nsym; i++) { + if (sym_is_matched(sym, addr_offset_so)) { + return sym; + } + sym++; + } + + // Start traversing the hash list from the position pointed to by the last bucket. + if (nsym) { + hashval = buckets + p->ghashtab[0] + (nsym - p->ghashtab[1]); + do { + nsym++; + if (sym_is_matched(sym, addr_offset_so)) { + return sym; + } + sym++; + } + while (!(*hashval++ & 1)); + } + + return NULL; +} + + int dladdr(const void *addr_arg, Dl_info *info) { size_t addr = (size_t)addr_arg; struct dso *p; - Sym *sym, *bestsym; - uint32_t nsym; + Sym *match_sym = NULL; char *strings; - size_t best = 0; - size_t besterr = -1; pthread_rwlock_rdlock(&lock); p = addr2dso(addr); @@ -3793,54 +3837,26 @@ int dladdr(const void *addr_arg, Dl_info *info) if (!p) return 0; - sym = p->syms; strings = p->strings; - nsym = count_syms(p); + size_t addr_offset_so = addr - (size_t)p->base; - if (DL_FDPIC) { - size_t idx = (addr-(size_t)p->funcdescs) - / sizeof(*p->funcdescs); - if (idx < nsym && (sym[idx].st_info&0xf) == STT_FUNC) { - best = (size_t)(p->funcdescs + idx); - bestsym = sym + idx; - besterr = 0; - } - } + info->dli_fname = p->name; + info->dli_fbase = p->map; - if (!best) for (; nsym; nsym--, sym++) { - if (sym->st_value - && (1<<(sym->st_info&0xf) & OK_TYPES) - && (1<<(sym->st_info>>4) & OK_BINDS)) { - size_t symaddr = (size_t)laddr(p, sym->st_value); - if (symaddr > addr || symaddr <= best) - continue; - best = symaddr; - bestsym = sym; - besterr = addr - symaddr; - if (addr == symaddr) - break; - } - } + if (p->ghashtab) { + match_sym = find_addr_by_gnu(addr_offset_so, p); - if (best && besterr > bestsym->st_size-1) { - best = 0; - bestsym = 0; + } else { + match_sym = find_addr_by_elf(addr_offset_so, p); } - info->dli_fname = p->name; - info->dli_fbase = p->map; - - if (!best) { + if (!match_sym) { info->dli_sname = 0; info->dli_saddr = 0; return 1; } - - if (DL_FDPIC && (bestsym->st_info&0xf) == STT_FUNC) - best = (size_t)(p->funcdescs + (bestsym - p->syms)); - info->dli_sname = strings + bestsym->st_name; - info->dli_saddr = (void *)best; - + info->dli_sname = strings + match_sym->st_name; + info->dli_saddr = (void *)laddr(p, match_sym->st_value); return 1; }