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
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 @@
     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 @@
 }
 
 #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 @@
       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 @@
  * 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 @@
     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 @@
     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 @@
 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