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.
This commit is contained in:
Rich Felker 2015-10-15 22:51:56 -04:00
parent bc9b6ea0df
commit bde0b4b92e

View File

@ -1744,17 +1744,19 @@ static int invalid_dso_handle(void *h)
static void *addr2dso(size_t a) static void *addr2dso(size_t a)
{ {
struct dso *p; 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) { for (p=head; p; p=p->next) {
if (DL_FDPIC && p->loadmap) { if (DL_FDPIC && p->loadmap) {
size_t i;
for (i=0; i<p->loadmap->nsegs; i++) { for (i=0; i<p->loadmap->nsegs; i++) {
if (a-p->loadmap->segs[i].p_vaddr if (a-p->loadmap->segs[i].p_vaddr
< p->loadmap->segs[i].p_memsz) < p->loadmap->segs[i].p_memsz)
return p; return p;
} }
i = count_syms(p);
if (a-(size_t)p->funcdescs < i*sizeof(*p->funcdescs))
return p;
} else { } else {
if (a-(size_t)p->map < p->map_len) if (a-(size_t)p->map < p->map_len)
return p; return p;
@ -1824,11 +1826,10 @@ failed:
int __dladdr(const void *addr, Dl_info *info) int __dladdr(const void *addr, Dl_info *info)
{ {
struct dso *p; struct dso *p;
Sym *sym; Sym *sym, *bestsym;
uint32_t nsym; uint32_t nsym;
char *strings; char *strings;
void *best = 0; void *best = 0;
char *bestname;
pthread_rwlock_rdlock(&lock); pthread_rwlock_rdlock(&lock);
p = addr2dso((size_t)addr); p = addr2dso((size_t)addr);
@ -1840,7 +1841,16 @@ int __dladdr(const void *addr, Dl_info *info)
strings = p->strings; strings = p->strings;
nsym = count_syms(p); 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 if (sym->st_value
&& (1<<(sym->st_info&0xf) & OK_TYPES) && (1<<(sym->st_info&0xf) & OK_TYPES)
&& (1<<(sym->st_info>>4) & OK_BINDS)) { && (1<<(sym->st_info>>4) & OK_BINDS)) {
@ -1848,7 +1858,7 @@ int __dladdr(const void *addr, Dl_info *info)
if (symaddr > addr || symaddr < best) if (symaddr > addr || symaddr < best)
continue; continue;
best = symaddr; best = symaddr;
bestname = strings + sym->st_name; bestsym = sym;
if (addr == symaddr) if (addr == symaddr)
break; break;
} }
@ -1856,9 +1866,12 @@ int __dladdr(const void *addr, Dl_info *info)
if (!best) return 0; 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_fname = p->name;
info->dli_fbase = p->base; info->dli_fbase = p->base;
info->dli_sname = bestname; info->dli_sname = strings + bestsym->st_name;
info->dli_saddr = best; info->dli_saddr = best;
return 1; return 1;