summaryrefslogtreecommitdiff
path: root/src/ldso/dynlink.c
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2015-10-15 22:51:56 -0400
committerRich Felker <dalias@aerifal.cx>2015-10-15 22:51:56 -0400
commitbde0b4b92e2c3f8d26d28e8478f196233ce51618 (patch)
tree8ed82eb56e52190a1dddae326a6d0b0ffed0e75b /src/ldso/dynlink.c
parentbc9b6ea0df6a820878d9ac538b2b852fec367d41 (diff)
downloadmusl-bde0b4b92e2c3f8d26d28e8478f196233ce51618.tar.gz
fix dladdr treatment of function descriptors for fdpic
when determining which module an address belongs to, all function descriptor ranges must be checked first, in case the allocated memory falls inside another module's memory range. dladdr itself must also check addresses against function descriptors before doing a best-match search against the symbol table. even when doing the latter (e.g. for code addresses obtained from mcontext_t), also check whether the best-match was a function, and if so, replace the result with a function descriptor address. which is the nominal "base address" of the function and which the caller needs if it intends to subsequently call the matching function.
Diffstat (limited to 'src/ldso/dynlink.c')
-rw-r--r--src/ldso/dynlink.c31
1 files changed, 22 insertions, 9 deletions
diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c
index 8025116f..642ecc30 100644
--- a/src/ldso/dynlink.c
+++ b/src/ldso/dynlink.c
@@ -1744,17 +1744,19 @@ static int invalid_dso_handle(void *h)
static 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) {
- size_t i;
for (i=0; i<p->loadmap->nsegs; i++) {
if (a-p->loadmap->segs[i].p_vaddr
< p->loadmap->segs[i].p_memsz)
return p;
}
- i = count_syms(p);
- if (a-(size_t)p->funcdescs < i*sizeof(*p->funcdescs))
- return p;
} else {
if (a-(size_t)p->map < p->map_len)
return p;
@@ -1824,11 +1826,10 @@ failed:
int __dladdr(const void *addr, Dl_info *info)
{
struct dso *p;
- Sym *sym;
+ Sym *sym, *bestsym;
uint32_t nsym;
char *strings;
void *best = 0;
- char *bestname;
pthread_rwlock_rdlock(&lock);
p = addr2dso((size_t)addr);
@@ -1840,7 +1841,16 @@ int __dladdr(const void *addr, Dl_info *info)
strings = p->strings;
nsym = count_syms(p);
- for (; nsym; nsym--, sym++) {
+ if (DL_FDPIC) {
+ size_t idx = ((size_t)addr-(size_t)p->funcdescs)
+ / sizeof(*p->funcdescs);
+ if (idx < nsym && (sym[idx].st_info&0xf) == STT_FUNC) {
+ best = p->funcdescs + idx;
+ bestsym = sym + idx;
+ }
+ }
+
+ if (!best) for (; nsym; nsym--, sym++) {
if (sym->st_value
&& (1<<(sym->st_info&0xf) & OK_TYPES)
&& (1<<(sym->st_info>>4) & OK_BINDS)) {
@@ -1848,7 +1858,7 @@ int __dladdr(const void *addr, Dl_info *info)
if (symaddr > addr || symaddr < best)
continue;
best = symaddr;
- bestname = strings + sym->st_name;
+ bestsym = sym;
if (addr == symaddr)
break;
}
@@ -1856,9 +1866,12 @@ int __dladdr(const void *addr, Dl_info *info)
if (!best) return 0;
+ if (DL_FDPIC && (bestsym->st_info&0xf) == STT_FUNC)
+ best = p->funcdescs + (bestsym - p->syms);
+
info->dli_fname = p->name;
info->dli_fbase = p->base;
- info->dli_sname = bestname;
+ info->dli_sname = strings + bestsym->st_name;
info->dli_saddr = best;
return 1;