diff options
author | Matt Fischer <matt.fischer@garmin.com> | 2009-12-31 12:17:40 -0600 |
---|---|---|
committer | Garmin Android technology group <android@garmin.com> | 2010-03-17 16:11:37 -0500 |
commit | e2a8b1fd19fb3a8ead2ba28ddba27be19fa978b5 (patch) | |
tree | e5448ec45187a37dbade76fcf10115f91135a8bd | |
parent | ede2e75f4942293342dfb8dfd4751369600ed8f1 (diff) | |
download | bionic-e2a8b1fd19fb3a8ead2ba28ddba27be19fa978b5.zip bionic-e2a8b1fd19fb3a8ead2ba28ddba27be19fa978b5.tar.gz bionic-e2a8b1fd19fb3a8ead2ba28ddba27be19fa978b5.tar.bz2 |
Added support for dladdr()
dladdr() is a GNU extension function, which allows the caller to retrieve
symbol information for a specified memory address. It is useful for things
like generating backtrace information at runtime.
Change-Id: I3a1def1a6c9c666d93e1e97b7d260dfa5b9b79a9
-rw-r--r-- | libc/include/dlfcn.h | 12 | ||||
-rw-r--r-- | libdl/libdl.c | 4 | ||||
-rw-r--r-- | linker/dlfcn.c | 64 | ||||
-rw-r--r-- | linker/linker.c | 34 | ||||
-rw-r--r-- | linker/linker.h | 2 |
5 files changed, 101 insertions, 15 deletions
diff --git a/libc/include/dlfcn.h b/libc/include/dlfcn.h index 9582796..f84d1d1 100644 --- a/libc/include/dlfcn.h +++ b/libc/include/dlfcn.h @@ -32,10 +32,22 @@ __BEGIN_DECLS +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; + extern void* dlopen(const char* filename, int flag); extern int dlclose(void* handle); extern const char* dlerror(void); extern void* dlsym(void* handle, const char* symbol); +extern int dladdr(void* addr, Dl_info *info); enum { RTLD_NOW = 0, diff --git a/libdl/libdl.c b/libdl/libdl.c index 7971942..b36af16 100644 --- a/libdl/libdl.c +++ b/libdl/libdl.c @@ -14,12 +14,14 @@ * limitations under the License. */ +#include <dlfcn.h> /* These are stubs for functions that are actually defined * in the dynamic linker (dlfcn.c), and hijacked at runtime. */ void *dlopen(const char *filename, int flag) { return 0; } -char *dlerror(void) { return 0; } +const char *dlerror(void) { return 0; } void *dlsym(void *handle, const char *symbol) { return 0; } +int dladdr(void *addr, Dl_info *info) { return 0; } int dlclose(void *handle) { return 0; } #ifdef __arm__ diff --git a/linker/dlfcn.c b/linker/dlfcn.c index 039926c..dcadce5 100644 --- a/linker/dlfcn.c +++ b/linker/dlfcn.c @@ -117,6 +117,37 @@ err: return 0; } +int dladdr(void *addr, Dl_info *info) +{ + int ret = 0; + + pthread_mutex_lock(&dl_lock); + + /* Determine if this address can be found in any library currently mapped */ + soinfo *si = find_containing_library(addr); + + if(si) { + memset(info, 0, sizeof(Dl_info)); + + info->dli_fname = si->name; + info->dli_fbase = (void*)si->base; + + /* Determine if any symbol in the library contains the specified address */ + Elf32_Sym *sym = find_containing_symbol(addr, si); + + if(sym != NULL) { + info->dli_sname = si->strtab + sym->st_name; + info->dli_saddr = (void*)(si->base + sym->st_value); + } + + ret = 1; + } + + pthread_mutex_unlock(&dl_lock); + + return ret; +} + int dlclose(void *handle) { pthread_mutex_lock(&dl_lock); @@ -126,22 +157,22 @@ int dlclose(void *handle) } #if defined(ANDROID_ARM_LINKER) -// 0000000 00011111 111112 22222222 233333333334444444444 -// 0123456 78901234 567890 12345678 901234567890123456789 +// 0000000 00011111 111112 22222222 2333333 333344444444445555555 +// 0123456 78901234 567890 12345678 9012345 678901234567890123456 #define ANDROID_LIBDL_STRTAB \ - "dlopen\0dlclose\0dlsym\0dlerror\0dl_unwind_find_exidx\0" + "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_unwind_find_exidx\0" #elif defined(ANDROID_X86_LINKER) -// 0000000 00011111 111112 22222222 2333333333344444 -// 0123456 78901234 567890 12345678 9012345678901234 +// 0000000 00011111 111112 22222222 2333333 3333444444444455 +// 0123456 78901234 567890 12345678 9012345 6789012345678901 #define ANDROID_LIBDL_STRTAB \ - "dlopen\0dlclose\0dlsym\0dlerror\0dl_iterate_phdr\0" + "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_iterate_phdr\0" #elif defined(ANDROID_SH_LINKER) -// 0000000 00011111 111112 22222222 2333333333344444 -// 0123456 78901234 567890 12345678 9012345678901234 +// 0000000 00011111 111112 22222222 2333333 3333444444444455 +// 0123456 78901234 567890 12345678 9012345 6789012345678901 #define ANDROID_LIBDL_STRTAB \ - "dlopen\0dlclose\0dlsym\0dlerror\0dl_iterate_phdr\0" + "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_iterate_phdr\0" #else /* !defined(ANDROID_ARM_LINKER) && !defined(ANDROID_X86_LINKER) */ #error Unsupported architecture. Only ARM and x86 are presently supported. @@ -175,20 +206,25 @@ static Elf32_Sym libdl_symtab[] = { st_info: STB_GLOBAL << 4, st_shndx: 1, }, -#ifdef ANDROID_ARM_LINKER { st_name: 29, + st_value: (Elf32_Addr) &dladdr, + st_info: STB_GLOBAL << 4, + st_shndx: 1, + }, +#ifdef ANDROID_ARM_LINKER + { st_name: 36, st_value: (Elf32_Addr) &dl_unwind_find_exidx, st_info: STB_GLOBAL << 4, st_shndx: 1, }, #elif defined(ANDROID_X86_LINKER) - { st_name: 29, + { st_name: 36, st_value: (Elf32_Addr) &dl_iterate_phdr, st_info: STB_GLOBAL << 4, st_shndx: 1, }, #elif defined(ANDROID_SH_LINKER) - { st_name: 29, + { st_name: 36, st_value: (Elf32_Addr) &dl_iterate_phdr, st_info: STB_GLOBAL << 4, st_shndx: 1, @@ -216,7 +252,7 @@ static Elf32_Sym libdl_symtab[] = { * stubbing them out in libdl. */ static unsigned libdl_buckets[1] = { 1 }; -static unsigned libdl_chains[6] = { 0, 2, 3, 4, 5, 0 }; +static unsigned libdl_chains[7] = { 0, 2, 3, 4, 5, 6, 0 }; soinfo libdl_info = { name: "libdl.so", @@ -226,7 +262,7 @@ soinfo libdl_info = { symtab: libdl_symtab, nbucket: 1, - nchain: 6, + nchain: 7, bucket: libdl_buckets, chain: libdl_chains, }; diff --git a/linker/linker.c b/linker/linker.c index 6d57cbc..f5294d9 100644 --- a/linker/linker.c +++ b/linker/linker.c @@ -538,6 +538,40 @@ Elf32_Sym *lookup(const char *name, soinfo **found) return 0; } +soinfo *find_containing_library(void *addr) +{ + soinfo *si; + + for(si = solist; si != NULL; si = si->next) + { + if((unsigned)addr >= si->base && (unsigned)addr - si->base < si->size) { + return si; + } + } + + return NULL; +} + +Elf32_Sym *find_containing_symbol(void *addr, soinfo *si) +{ + unsigned int i; + unsigned soaddr = (unsigned)addr - si->base; + + /* Search the library's symbol table for any defined symbol which + * contains this address */ + for(i=0; i<si->nchain; i++) { + Elf32_Sym *sym = &si->symtab[i]; + + if(sym->st_shndx != SHN_UNDEF && + soaddr >= sym->st_value && + soaddr < sym->st_value + sym->st_size) { + return sym; + } + } + + return NULL; +} + #if 0 static void dump(soinfo *si) { diff --git a/linker/linker.h b/linker/linker.h index ec01489..8cd56b0 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -225,6 +225,8 @@ soinfo *find_library(const char *name); unsigned unload_library(soinfo *si); Elf32_Sym *lookup_in_library(soinfo *si, const char *name); Elf32_Sym *lookup(const char *name, soinfo **found); +soinfo *find_containing_library(void *addr); +Elf32_Sym *find_containing_symbol(void *addr, soinfo *si); const char *linker_get_error(void); #ifdef ANDROID_ARM_LINKER |