diff options
Diffstat (limited to 'ldso/libdl')
-rw-r--r-- | ldso/libdl/libdl.c | 39 |
1 files changed, 35 insertions, 4 deletions
diff --git a/ldso/libdl/libdl.c b/ldso/libdl/libdl.c index edf336684..6dc60587b 100644 --- a/ldso/libdl/libdl.c +++ b/ldso/libdl/libdl.c @@ -739,13 +739,40 @@ int dladdr(const void *__address, Dl_info * __info) char *strtab; ElfW(Sym) *symtab; unsigned int hn, si, sn, sf; - ElfW(Addr) sa; + ElfW(Addr) sa = 0; + + /* Set the info for the object the address lies in */ + __info->dli_fname = pelf->libname; + __info->dli_fbase = (void *) DL_LOADADDR_BASE(pelf->mapaddr); - sa = 0; symtab = (ElfW(Sym) *) (pelf->dynamic_info[DT_SYMTAB]); strtab = (char *) (pelf->dynamic_info[DT_STRTAB]); sf = sn = 0; + +#ifdef __LDSO_GNU_HASH_SUPPORT__ + if (pelf->l_gnu_bitmask) { + for (hn = 0; hn < pelf->nbucket; hn++) { + si = pelf->l_gnu_buckets[hn]; + if (!si) + continue; + + const Elf32_Word *hasharr = &pelf->l_gnu_chain_zero[si]; + do { + ElfW(Addr) symbol_addr; + + symbol_addr = (ElfW(Addr)) pelf->loadaddr + symtab[si].st_value; + if (symbol_addr <= (ElfW(Addr))__address && (!sf || sa < symbol_addr)) { + sa = symbol_addr; + sn = si; + sf = 1; + } + _dl_if_debug_print("Symbol \"%s\" at %p\n", strtab + symtab[si].st_name, symbol_addr); + ++si; + } while ((*hasharr++ & 1u) == 0); + } + } else +#endif for (hn = 0; hn < pelf->nbucket; hn++) { for (si = pelf->elf_buckets[hn]; si; si = pelf->chains[si]) { ElfW(Addr) symbol_addr; @@ -763,10 +790,14 @@ int dladdr(const void *__address, Dl_info * __info) } if (sf) { - __info->dli_fname = pelf->libname; - __info->dli_fbase = (void *) DL_LOADADDR_BASE(pelf->loadaddr); + /* A nearest symbol has been found; fill the entries */ __info->dli_sname = strtab + symtab[sn].st_name; __info->dli_saddr = (void *)sa; + } else { + /* No symbol found, fill entries with NULL value, + only the containing object will be returned. */ + __info->dli_sname = NULL; + __info->dli_saddr = NULL; } return 1; } |