diff options
| -rw-r--r-- | include/dlfcn.h | 10 | ||||
| -rw-r--r-- | src/ldso/dladdr.c | 9 | ||||
| -rw-r--r-- | src/ldso/dynlink.c | 86 | 
3 files changed, 105 insertions, 0 deletions
diff --git a/include/dlfcn.h b/include/dlfcn.h index dea74c7d..e98c8ca6 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -18,6 +18,16 @@ char  *dlerror(void);  void  *dlopen(const char *, int);  void  *dlsym(void *, const char *); +#ifdef _GNU_SOURCE +typedef struct { +	const char *dli_fname; +	void *dli_fbase; +	const char *dli_sname; +	void *dli_saddr; +} Dl_info; +int dladdr(void *, Dl_info *); +#endif +  #ifdef __cplusplus  }  #endif diff --git a/src/ldso/dladdr.c b/src/ldso/dladdr.c new file mode 100644 index 00000000..265bb681 --- /dev/null +++ b/src/ldso/dladdr.c @@ -0,0 +1,9 @@ +#define _GNU_SOURCE +#include <dlfcn.h> + +int __dladdr(void *, Dl_info *); + +int dladdr(void *addr, Dl_info *info) +{ +	return __dladdr(addr, info); +} diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index c733dc5d..b8c26ace 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -1,3 +1,4 @@ +#define _GNU_SOURCE  #include <stdio.h>  #include <stdlib.h>  #include <string.h> @@ -584,6 +585,22 @@ static size_t find_dyn(Phdr *ph, size_t cnt, size_t stride)  	return 0;  } +static void find_map_range(Phdr *ph, size_t cnt, size_t stride, struct dso *p) +{ +	size_t min_addr = -1, max_addr = 0; +	for (; cnt--; ph = (void *)((char *)ph + stride)) { +		if (ph->p_type != PT_LOAD) continue; +		if (ph->p_vaddr < min_addr) +			min_addr = ph->p_vaddr; +		if (ph->p_vaddr+ph->p_memsz > max_addr) +			max_addr = ph->p_vaddr+ph->p_memsz; +	} +	min_addr &= -PAGE_SIZE; +	max_addr = (max_addr + PAGE_SIZE-1) & -PAGE_SIZE; +	p->map = p->base + min_addr; +	p->map_len = max_addr - min_addr; +} +  static void do_init_fini(struct dso *p)  {  	size_t dyn[DYN_CNT] = {0}; @@ -647,6 +664,8 @@ void *__dynlink(int argc, char **argv)  	lib->name = lib->shortname = "libc.so";  	lib->global = 1;  	ehdr = (void *)lib->base; +	find_map_range((void *)(aux[AT_BASE]+ehdr->e_phoff), +		ehdr->e_phnum, ehdr->e_phentsize, lib);  	lib->dynv = (void *)(lib->base + find_dyn(  		(void *)(aux[AT_BASE]+ehdr->e_phoff),  		ehdr->e_phnum, ehdr->e_phentsize)); @@ -666,6 +685,8 @@ void *__dynlink(int argc, char **argv)  		app->name = argv[0];  		app->dynv = (void *)(app->base + find_dyn(  			(void *)aux[AT_PHDR], aux[AT_PHNUM], aux[AT_PHENT])); +		find_map_range((void *)aux[AT_PHDR], +			aux[AT_PHNUM], aux[AT_PHENT], app);  	} else {  		int fd;  		char *ldname = argv[0]; @@ -891,6 +912,67 @@ failed:  	return 0;  } +int __dladdr(void *addr, Dl_info *info) +{ +	struct dso *p; +	Sym *sym; +	uint32_t nsym; +	char *strings; +	size_t i; +	void *best = 0; +	char *bestname; + +	pthread_rwlock_rdlock(&lock); +	for (p=head; p && (unsigned char *)addr-p->map>p->map_len; p=p->next); +	pthread_rwlock_unlock(&lock); + +	if (!p) return 0; + +	sym = p->syms; +	strings = p->strings; +	if (p->hashtab) { +		nsym = p->hashtab[1]; +	} else { +		uint32_t *buckets; +		uint32_t *hashval; +		buckets = p->ghashtab + 4 + (p->ghashtab[2]*sizeof(size_t)/4); +		sym += p->ghashtab[1]; +		for (i = 0; i < p->ghashtab[0]; i++) { +			if (buckets[i] > nsym) +				nsym = buckets[i]; +		} +		if (nsym) { +			nsym -= p->ghashtab[1]; +			hashval = buckets + p->ghashtab[0] + nsym; +			do nsym++; +			while (!(*hashval++ & 1)); +		} +	} + +	for (; nsym; nsym--, sym++) { +		if (sym->st_shndx && sym->st_value +		 && (1<<(sym->st_info&0xf) & OK_TYPES) +		 && (1<<(sym->st_info>>4) & OK_BINDS)) { +			void *symaddr = p->base + sym->st_value; +			if (symaddr > addr || symaddr < best) +				continue; +			best = symaddr; +			bestname = strings + sym->st_name; +			if (addr == symaddr) +				break; +		} +	} + +	if (!best) return 0; + +	info->dli_fname = p->name; +	info->dli_fbase = p->base; +	info->dli_sname = bestname; +	info->dli_saddr = best; + +	return 1; +} +  void *__dlsym(void *p, const char *s, void *ra)  {  	void *res; @@ -908,6 +990,10 @@ void *__dlsym(void *p, const char *s, void *ra)  {  	return 0;  } +int __dladdr (void *addr, Dl_info *info) +{ +	return 0; +}  #endif  char *dlerror()  | 
