>From 1ac52fe3a7e934223ec887b6d95bb74ecc0477b1 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 7 Aug 2012 10:57:26 +0200 Subject: [PATCH] ldso : dladdr support --- include/dlfcn.h | 15 ++++++++ src/ldso/dynlink.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 1 deletion(-) diff --git a/include/dlfcn.h b/include/dlfcn.h index dea74c7..8c45822 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -18,6 +18,21 @@ char *dlerror(void); void *dlopen(const char *, int); void *dlsym(void *, const char *); +#ifdef _GNU_SOURCE +typedef struct { + const char *dli_fname; /* Pathname of shared object that + contains address */ + void *dli_fbase; /* Address at which shared object + is loaded */ + const char *dli_sname; /* Name of nearest symbol with address + lower than addr */ + void *dli_saddr; /* Exact address of symbol named + in dli_sname */ +} Dl_info; + +int dladdr (void *addr, Dl_info *info); +#endif + #ifdef __cplusplus } #endif diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index 0c8d75a..acf8e56 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -1,3 +1,4 @@ +#define _GNU_SOURCE #include #include #include @@ -28,12 +29,14 @@ typedef Elf32_Phdr Phdr; typedef Elf32_Sym Sym; #define R_TYPE(x) ((x)&255) #define R_SYM(x) ((x)>>8) +#define ELF_ST_TYPE ELF32_ST_TYPE #else typedef Elf64_Ehdr Ehdr; typedef Elf64_Phdr Phdr; typedef Elf64_Sym Sym; #define R_TYPE(x) ((x)&0xffffffff) #define R_SYM(x) ((x)>>32) +#define ELF_ST_TYPE ELF64_ST_TYPE #endif struct debug { @@ -69,7 +72,8 @@ struct dso { struct hash_algo { uint32_t (*hash) (const char *); - Sym *(*lookup) (const char *s, uint32_t h, struct dso *dso); + Sym *(*lookup) (const char *, uint32_t, struct dso *); + void (*iterate) (struct dso *, int (*) (struct dso *, Sym *, void *), void *); }; #define SYSV_HASH_ALG_IDX 0 @@ -178,14 +182,50 @@ static Sym *gnu_lookup(const char *s, uint32_t h1, struct dso *dso) return 0; } +static void sysv_iterate (struct dso *dso, int (*func) (struct dso *, Sym *, void *), void *data) +{ + size_t i; + Sym *syms = dso->syms; + uint32_t *hashtab = dso->hashtab; + uint32_t nbucket = hashtab[0]; + uint32_t nchain = hashtab[1]; + for (i = 0; i < hashtab[1]; i++) { + if (!func (dso, syms + i, data)) + return; + } +} + +static void gnu_iterate (struct dso *dso, int (*func) (struct dso *, Sym *, void *), void *data) +{ + uint32_t i; + Sym *syms = dso->syms; + uint32_t *hashtab = dso->hashtab; + uint32_t *buckets = hashtab + 4 + (hashtab[2] * (sizeof(size_t) / sizeof(uint32_t))); + uint32_t nbuckets = hashtab[0]; + uint32_t *hashvals = buckets + nbuckets; + uint32_t symndx = hashtab[1]; + + for (i = 0; i < nbuckets; ++i) { + uint32_t n = buckets[i]; + Sym *sym = syms + n; + uint32_t *hashval = hashvals + n - symndx; + do { + if (!func (dso, sym++, data)) + return; + }while (!(*hashval++ & 1)); + } +} + static struct hash_algo hashalgs[] = { { .hash = sysv_hash, .lookup = sysv_lookup, + .iterate = sysv_iterate, }, { .hash = gnu_hash, .lookup = gnu_lookup, + .iterate = gnu_iterate, }, }; @@ -918,6 +958,69 @@ failed: return 0; } +struct sym_search { + void *addr; + Dl_info *info; +}; + +static int find_closest_sym (struct dso *dso, Sym *sym, void *data) +{ + struct sym_search *search = data; + void *symaddr = dso->base + sym->st_value; + char *strings = dso->strings; + Dl_info *info = search->info; + void *addr = search->addr; + void *prevaddr = info->dli_saddr; + + if (sym->st_value == 0 && sym->st_shndx == SHN_UNDEF) + return 1; + + if (ELF_ST_TYPE(sym->st_info) == STT_TLS) + return 1; + + if (addr < symaddr) + return 1; + + if (prevaddr && (addr - symaddr) > (addr - prevaddr)) + return 1; + + info->dli_saddr = symaddr; + info->dli_sname = strings + sym->st_name; + + if (addr == symaddr) + return 0; + + return 1; + +} + +static int do_dladdr (void *addr, Dl_info *info) +{ + struct sym_search search; + struct dso *p; + memset (info, 0, sizeof (*info)); + search.info = info; + search.addr = addr; + for (p=head; p; p=p->next) { + if ((unsigned char *)addr >= p->map && (unsigned char *)addr < p->map + p->map_len) { + info->dli_fname = p->name; + info->dli_fbase = p->base; + hashalgs[p->hashalg].iterate (p, find_closest_sym, &search); + return 1; + } + } + return 0; +} + +int dladdr (void *addr, Dl_info *info) +{ + int res; + pthread_rwlock_rdlock(&lock); + res = do_dladdr (addr, info); + pthread_rwlock_unlock(&lock); + return res; +} + void *__dlsym(void *p, const char *s, void *ra) { void *res; -- 1.7.9.5