about summary refs log tree commit diff
diff options
context:
space:
mode:
-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;