Implement method to read elf data from memory.

I rewrote the entirety of elfxx.h/elfxx.c to support this.

For the rewrite, I modified the way that the symbol tables are searched.
The searched every single entry in the symbol table, and all symbol tables.
The new method finds the first entry that actually has a name and stops.

I also added some quick checks to make sure that a malformed elf would
not cause a crash when reading the symbol names from the symbol tables.

I had to implement the functionality of dwarf_find_unwind_table to handle
reading from memory, instead of from a file. I didn't implement debug frame
support since that likelihood of that appearing in memory seems low.

I also modified the dwarf_find_unwind_table to remove an abort and just
fail if that particular path is encountered. In addition, I added a quick
check to make sure that a malformed dynamic section doesn't cause a crash.

Bug: 19517541

Change-Id: I6075d87035dca41fa5e5dfcbe287a680a7671836
diff --git a/include/dwarf.h b/include/dwarf.h
index 1e34376..c66a206 100644
--- a/include/dwarf.h
+++ b/include/dwarf.h
@@ -380,9 +380,6 @@
 #define dwarf_find_proc_info		UNW_OBJ (dwarf_find_proc_info)
 #define dwarf_find_debug_frame		UNW_OBJ (dwarf_find_debug_frame)
 #define dwarf_search_unwind_table	UNW_OBJ (dwarf_search_unwind_table)
-/* ANDROID support update. */
-#define dwarf_get_load_base		UNW_OBJ (dwarf_get_load_base)
-/* End of ANDROID update. */
 #define dwarf_find_unwind_table		UNW_OBJ (dwarf_find_unwind_table)
 #define dwarf_put_unwind_info		UNW_OBJ (dwarf_put_unwind_info)
 #define dwarf_put_unwind_info		UNW_OBJ (dwarf_put_unwind_info)
@@ -412,8 +409,6 @@
 				      unw_proc_info_t *pi,
 				      int need_unwind_info, void *arg);
 /* ANDROID support update. */
-extern int dwarf_get_load_base (struct elf_image *ei, unw_word_t mapoff,
-				unw_word_t *load_base);
 extern int dwarf_find_unwind_table (struct elf_dyn_info *edi, struct elf_image *ei,
 				    unw_addr_space_t as, char *path,
 				    unw_word_t segbase, unw_word_t mapoff,
diff --git a/include/libunwind_i.h b/include/libunwind_i.h
index 1621de5..132f7ef 100644
--- a/include/libunwind_i.h
+++ b/include/libunwind_i.h
@@ -52,6 +52,7 @@
 #include <libunwind.h>
 #include <pthread.h>
 #include <signal.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -325,8 +326,26 @@
 
 struct elf_image
   {
-    void *image;		/* pointer to mmap'd image */
-    size_t size;		/* (file-) size of the image */
+    bool valid;			/* true if the image is a valid elf image */
+    bool load_attempted;	/* true if we've already attempted to load the elf */
+    bool mapped;		/* true if the elf image was mmap'd in */
+    union
+      {
+        struct
+          {
+            void *image;		/* pointer to mmap'd image */
+            size_t size;		/* (file-) size of the image */
+          }
+        mapped;
+        struct
+          {
+            unw_addr_space_t as;	/* address space containing the access_mem function */
+            void *as_arg;		/* arg used with access_mem */
+            struct map_info *map;	/* map data associated with the elf data */
+          }
+        memory;
+      }
+    u;
   };
 
 struct elf_dyn_info
diff --git a/include/tdep-aarch64/libunwind_i.h b/include/tdep-aarch64/libunwind_i.h
index 36250e3..0690b1b 100644
--- a/include/tdep-aarch64/libunwind_i.h
+++ b/include/tdep-aarch64/libunwind_i.h
@@ -305,7 +305,7 @@
 extern int tdep_get_elf_image (unw_addr_space_t as, struct elf_image *ei,
 			       pid_t pid, unw_word_t ip,
 			       unsigned long *segbase, unsigned long *mapoff,
-			       char **path);
+			       char **path, void *as_arg);
 /* End of ANDROID update. */
 extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg,
 			    unw_word_t *valp, int write);
diff --git a/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h
index 1fed4bc..d782cc4 100644
--- a/include/tdep-arm/libunwind_i.h
+++ b/include/tdep-arm/libunwind_i.h
@@ -298,7 +298,7 @@
 extern int tdep_get_elf_image (unw_addr_space_t as, struct elf_image *ei,
 			       pid_t pid, unw_word_t ip,
 			       unsigned long *segbase, unsigned long *mapoff,
-			       char **path);
+			       char **path, void *as_arg);
 /* End of ANDROID update. */
 extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg,
 			    unw_word_t *valp, int write);
diff --git a/include/tdep-hppa/libunwind_i.h b/include/tdep-hppa/libunwind_i.h
index e8bb4f2..a5cbe1b 100644
--- a/include/tdep-hppa/libunwind_i.h
+++ b/include/tdep-hppa/libunwind_i.h
@@ -288,7 +288,7 @@
 extern int tdep_get_elf_image (unw_addr_space_t as, struct elf_image *ei,
 			       pid_t pid, unw_word_t ip,
 			       unsigned long *segbase, unsigned long *mapoff,
-			       char **path);
+			       char **path, void *as_arg);
 /* End of ANDROID update. */
 extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg,
 			    unw_word_t *valp, int write);
diff --git a/include/tdep-ia64/libunwind_i.h b/include/tdep-ia64/libunwind_i.h
index 95a9bee..41056c0 100644
--- a/include/tdep-ia64/libunwind_i.h
+++ b/include/tdep-ia64/libunwind_i.h
@@ -271,7 +271,7 @@
 extern int tdep_get_elf_image (unw_addr_space_t as, struct elf_image *ei,
 			       pid_t pid, unw_word_t ip,
 			       unsigned long *segbase, unsigned long *mapoff,
-			       char **path);
+			       char **path, void *as_arg);
 /* End of ANDROID update. */
 extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg,
 			    unw_word_t *valp, int write);
diff --git a/include/tdep-mips/libunwind_i.h b/include/tdep-mips/libunwind_i.h
index 0c53226..41cc83f 100644
--- a/include/tdep-mips/libunwind_i.h
+++ b/include/tdep-mips/libunwind_i.h
@@ -344,7 +344,7 @@
 extern int tdep_get_elf_image (unw_addr_space_t as, struct elf_image *ei,
 			       pid_t pid, unw_word_t ip,
 			       unsigned long *segbase, unsigned long *mapoff,
-			       char **path);
+			       char **path, void *as_arg);
 /* End of ANDROID update. */
 extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg,
 			    unw_word_t *valp, int write);
diff --git a/include/tdep-ppc32/libunwind_i.h b/include/tdep-ppc32/libunwind_i.h
index 56af153..c375b9b 100644
--- a/include/tdep-ppc32/libunwind_i.h
+++ b/include/tdep-ppc32/libunwind_i.h
@@ -309,7 +309,7 @@
 extern int tdep_get_elf_image (unw_addr_space_t as, struct elf_image *ei,
 			       pid_t pid, unw_word_t ip,
 			       unsigned long *segbase, unsigned long *mapoff,
-			       char **path);
+			       char **path, void *as_arg);
 /* End of ANDROID update. */
 extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg,
 			    unw_word_t * valp, int write);
diff --git a/include/tdep-ppc64/libunwind_i.h b/include/tdep-ppc64/libunwind_i.h
index b6f2224..cbeb16a 100644
--- a/include/tdep-ppc64/libunwind_i.h
+++ b/include/tdep-ppc64/libunwind_i.h
@@ -309,7 +309,7 @@
 extern int tdep_get_elf_image (unw_addr_space_t as, struct elf_image *ei,
 			       pid_t pid, unw_word_t ip,
 			       unsigned long *segbase, unsigned long *mapoff,
-			       char **path);
+			       char **path, void *as_arg);
 /* End of ANDROID update. */
 extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg,
 			    unw_word_t * valp, int write);
diff --git a/include/tdep-sh/libunwind_i.h b/include/tdep-sh/libunwind_i.h
index 29b162b..6752f5f 100644
--- a/include/tdep-sh/libunwind_i.h
+++ b/include/tdep-sh/libunwind_i.h
@@ -289,7 +289,7 @@
 extern int tdep_get_elf_image (unw_addr_space_t as, struct elf_image *ei,
 			       pid_t pid, unw_word_t ip,
 			       unsigned long *segbase, unsigned long *mapoff,
-			       char **path);
+			       char **path, void *as_arg);
 /* End of ANDROID update. */
 extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg,
 			    unw_word_t *valp, int write);
diff --git a/include/tdep-x86/libunwind_i.h b/include/tdep-x86/libunwind_i.h
index d0fdd65..f87bd71 100644
--- a/include/tdep-x86/libunwind_i.h
+++ b/include/tdep-x86/libunwind_i.h
@@ -292,7 +292,7 @@
 extern int tdep_get_elf_image (unw_addr_space_t as, struct elf_image *ei,
 			       pid_t pid, unw_word_t ip,
 			       unsigned long *segbase, unsigned long *mapoff,
-			       char **path);
+			       char **path, void *as_arg);
 /* End of ANDROID update. */
 extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg,
 			    unw_word_t *valp, int write);
diff --git a/include/tdep-x86_64/libunwind_i.h b/include/tdep-x86_64/libunwind_i.h
index 90b2ae3..184989e 100644
--- a/include/tdep-x86_64/libunwind_i.h
+++ b/include/tdep-x86_64/libunwind_i.h
@@ -238,7 +238,7 @@
 extern int tdep_get_elf_image (unw_addr_space_t as, struct elf_image *ei,
 			       pid_t pid, unw_word_t ip,
 			       unsigned long *segbase, unsigned long *mapoff,
-			       char **path);
+			       char **path, void *as_arg);
 /* End of ANDROID update. */
 extern int tdep_access_reg (struct cursor *c, unw_regnum_t reg,
 			    unw_word_t *valp, int write);
diff --git a/src/Los-common.c b/src/Los-common.c
index d8f57e3..c11cb7b 100644
--- a/src/Los-common.c
+++ b/src/Los-common.c
@@ -49,7 +49,7 @@
 {
   while (old_list)
     {
-      if (old_list->ei.image == NULL)
+      if (!old_list->ei.valid)
         {
           old_list = old_list->next;
           continue;
@@ -65,10 +65,9 @@
             {
               /* No need to do any lock, the entire local_map_list is locked
                  at this point. */
-              new_list->ei.size = old_list->ei.size;
-              new_list->ei.image = old_list->ei.image;
-              old_list->ei.size = 0;
-              old_list->ei.image = NULL;
+              new_list->ei = old_list->ei;
+              /* If it was mapped before, make sure to mark it unmapped now. */
+              old_list->ei.mapped = false;
               /* Don't bother breaking out of the loop, the next while check
                  is guaranteed to fail, causing us to break out of the loop
                  after advancing to the next map element. */
@@ -164,8 +163,8 @@
 }
 
 PROTECTED int
-local_get_elf_image (struct elf_image *ei, unw_word_t ip,
-                     unsigned long *segbase, unsigned long *mapoff, char **path)
+local_get_elf_image (unw_addr_space_t as, struct elf_image *ei, unw_word_t ip,
+                     unsigned long *segbase, unsigned long *mapoff, char **path, void *as_arg)
 {
   struct map_info *map;
   intrmask_t saved_mask;
@@ -183,7 +182,7 @@
       map = map_find_from_addr (local_map_list, ip);
     }
 
-  if (map && elf_map_cached_image (map, ip) == 0)
+  if (map && elf_map_cached_image (as, as_arg, map, ip))
     {
       *ei = map->ei;
       *segbase = map->start;
diff --git a/src/aarch64/Ginit.c b/src/aarch64/Ginit.c
index 88eecaf..82be025 100644
--- a/src/aarch64/Ginit.c
+++ b/src/aarch64/Ginit.c
@@ -193,7 +193,7 @@
 		      char *buf, size_t buf_len, unw_word_t *offp,
 		      void *arg)
 {
-  return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
+  return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp, arg);
 }
 
 HIDDEN void
diff --git a/src/arm/Ginit.c b/src/arm/Ginit.c
index 03eef53..13629e7 100644
--- a/src/arm/Ginit.c
+++ b/src/arm/Ginit.c
@@ -186,7 +186,7 @@
 		      char *buf, size_t buf_len, unw_word_t *offp,
 		      void *arg)
 {
-  return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
+  return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp, arg);
 }
 
 HIDDEN void
diff --git a/src/dwarf/Gfind_unwind_table.c b/src/dwarf/Gfind_unwind_table.c
index e3b33b0..52ded19 100644
--- a/src/dwarf/Gfind_unwind_table.c
+++ b/src/dwarf/Gfind_unwind_table.c
@@ -33,48 +33,220 @@
 #include "dwarf-eh.h"
 #include "dwarf_i.h"
 
-/* ANDROID support update. */
-int
-dwarf_get_load_base (struct elf_image *ei, unw_word_t mapoff, unw_word_t *load_base)
-{
-  Elf_W(Ehdr) *ehdr;
-  Elf_W(Phdr) *phdr, *ptxt = NULL;
+static bool get_dyn_gp(struct elf_image* ei, Elf_W(Off) dyn_phdr_offset, unw_word_t* gp) {
+  Elf_W(Phdr) phdr;
+  GET_PHDR_FIELD(ei, dyn_phdr_offset, &phdr, p_offset);
+  Elf_W(Dyn) dyn;
+  Elf_W(Off) dyn_offset = phdr.p_offset;
+  unw_word_t map_size = ei->u.memory.map->end - ei->u.memory.map->start;
+  while (dyn_offset + sizeof(dyn) < map_size) {
+    GET_DYN_FIELD(ei, dyn_offset, &dyn, d_tag);
+    if (dyn.d_tag == DT_NULL) {
+      break;
+    }
+    if (dyn.d_tag == DT_PLTGOT) {
+      // Assume that _DYNAMIC is writable and GLIBC has
+      // relocated it (true for x86 at least).
+      GET_DYN_FIELD(ei, dyn_offset, &dyn, d_un.d_ptr);
+      *gp = dyn.d_un.d_ptr;
+      return true;
+    }
+    dyn_offset += sizeof(dyn);
+  }
+  Debug(1, "DT_PLTGOT not found in dynamic header\n");
+  return false;
+}
+
+static bool get_eh_frame_info(
+    struct elf_image* ei, unw_word_t phdr_offset, unw_word_t load_base, unw_dyn_info_t* di_cache) {
+  Elf_W(Phdr) phdr;
+  GET_PHDR_FIELD(ei, phdr_offset, &phdr, p_offset);
+  unw_word_t hdr_offset = phdr.p_offset;
+  struct dwarf_eh_frame_hdr hdr;
+  // Read the entire hdr since we are going to use every value in the struct.
+  if (sizeof(hdr) != elf_w (memory_read) (ei, ei->u.memory.map->start +phdr.p_offset,
+                            (uint8_t*) &hdr, sizeof(hdr), false)) {
+    Debug(1, "Failed to read dwarf_eh_frame_hdr from in memory elf image.\n");
+    return false;
+  }
+
+  if (hdr.version != DW_EH_VERSION) {
+    Debug (1, "table has unexpected version %d\n", hdr.version);
+    return false;
+  }
+
+  // Fill in a dummy proc_info structure.  We just need to fill in
+  // enough to ensure that dwarf_read_encoded_pointer() can do its
+  // job.  Since we don't have a procedure-context at this point, all
+  // we have to do is fill in the global-pointer.
+  unw_proc_info_t pi;
+  memset (&pi, 0, sizeof (pi));
+  pi.gp = di_cache->gp;
+
+  unw_accessors_t* a = unw_get_accessors (ei->u.memory.as);
+  unw_word_t addr = (unw_word_t) (uintptr_t) (hdr_offset + sizeof(struct dwarf_eh_frame_hdr));
+  addr += ei->u.memory.map->start;
+
+  unw_word_t eh_frame_start;
+  if (dwarf_read_encoded_pointer (ei->u.memory.as, a, &addr, hdr.eh_frame_ptr_enc, &pi,
+                                  &eh_frame_start, ei->u.memory.as_arg) < 0) {
+    Debug(1, "Failed to read encoded frame start.\n");
+    return false;
+  }
+
+  unw_word_t fde_count;
+  if (dwarf_read_encoded_pointer (ei->u.memory.as, a, &addr, hdr.fde_count_enc, &pi,
+                                  &fde_count, ei->u.memory.as_arg) < 0) {
+    Debug(1, "Failed to read fde count.\n");
+    return false;
+  }
+
+  if (hdr.table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
+    // Unsupported table format.
+    Debug(1, "Unsupported header table format %d\n", hdr.table_enc);
+    return false;
+  }
+
+  di_cache->u.rti.name_ptr = 0;
+  // two 32-bit values (ip_offset/fde_offset) per table-entry:
+  di_cache->u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
+
+  GET_PHDR_FIELD(ei, phdr_offset, &phdr, p_vaddr);
+  GET_PHDR_FIELD(ei, phdr_offset, &phdr, p_offset);
+  di_cache->u.rti.table_data =
+      load_base + phdr.p_vaddr + addr - (uintptr_t) ei->u.memory.map->start - phdr.p_offset;
+
+  // For the binary-search table in the eh_frame_hdr, data-relative
+  // means relative to the start of that section...
+  di_cache->u.rti.segbase = ((load_base + phdr.p_vaddr) + (hdr_offset - phdr.p_offset));
+
+  return true;
+}
+
+static bool dwarf_find_unwind_table_memory (
+    struct elf_dyn_info *edi, struct elf_image *ei, unw_addr_space_t as, char *path,
+    unw_word_t segbase, unw_word_t mapoff, unw_word_t ip) {
+  Elf_W(Ehdr) ehdr;
+  GET_EHDR_FIELD(ei, &ehdr, e_phoff, false);
+  GET_EHDR_FIELD(ei, &ehdr, e_phnum, false);
+
+  Elf_W(Phdr) phdr;
+  Elf_W(Off) offset = ehdr.e_phoff;
+  Elf_W(Off) txt_phdr_offset = 0;
+  Elf_W(Addr) txt_pvaddr = 0;
+  Elf_W(Off) dyn_phdr_offset = 0;
+#if UNW_TARGET_ARM
+  Elf_W(Off) arm_exidx_phdr_offset = 0;
+#endif
   int i;
-
-  /* XXX: Much of this code is Linux/LSB-specific.  */
-
-  if (!elf_w(valid_object) (ei))
-    return 0;
-
-  ehdr = ei->image;
-  phdr = (Elf_W(Phdr) *) ((char *) ei->image + ehdr->e_phoff);
-  for (i = 0; i < ehdr->e_phnum; ++i)
-    {
-      if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff)
-        {
-          ptxt = phdr + i;
-          break;
+  unw_word_t start_ip = (unw_word_t) -1;
+  unw_word_t end_ip = 0;
+  Elf_W(Off) eh_frame_phdr_offset = 0;
+  for (i = 0; i < ehdr.e_phnum; ++i) {
+    Elf_W(Phdr) phdr;
+    GET_PHDR_FIELD(ei, offset, &phdr, p_type);
+    switch (phdr.p_type) {
+      case PT_LOAD:
+        GET_PHDR_FIELD(ei, offset, &phdr, p_vaddr);
+        if (phdr.p_vaddr < start_ip) {
+          start_ip = phdr.p_vaddr;
         }
+
+        GET_PHDR_FIELD(ei, offset, &phdr, p_memsz);
+        if (phdr.p_vaddr + phdr.p_memsz > end_ip) {
+          end_ip = phdr.p_vaddr + phdr.p_memsz;
+        }
+
+        GET_PHDR_FIELD(ei, offset, &phdr, p_offset);
+        if (phdr.p_offset == mapoff) {
+          txt_phdr_offset = offset;
+          txt_pvaddr = phdr.p_vaddr;
+        }
+        break;
+
+      case PT_GNU_EH_FRAME:
+        eh_frame_phdr_offset = offset;
+        break;
+
+      case PT_DYNAMIC:
+        dyn_phdr_offset = offset;
+        break;
+
+#if UNW_TARGET_ARM
+      case PT_ARM_EXIDX:
+        arm_exidx_phdr_offset = offset;
+        break;
+#endif
+
+      default:
+        break;
+    }
+    offset += sizeof(phdr);
+  }
+
+  if (txt_phdr_offset == 0) {
+    Debug(1, "PT_LOAD section not found.\n");
+    return false;
+  }
+
+  unw_word_t load_base = segbase - txt_pvaddr;
+  start_ip += load_base;
+  end_ip += load_base;
+
+  bool found = false;
+  if (eh_frame_phdr_offset) {
+    // For dynamicly linked executables and shared libraries,
+    // DT_PLTGOT is the value that data-relative addresses are
+    // relative to for that object.  We call this the "gp".
+    // Otherwise this is a static executable with no _DYNAMIC.  Assume
+    // that data-relative addresses are relative to 0, i.e.,
+    // absolute.
+    edi->di_cache.gp = 0;
+    if (dyn_phdr_offset) {
+      // Ignore failures, we'll attempt to keep going with a zero gp.
+      get_dyn_gp(ei, dyn_phdr_offset, &edi->di_cache.gp);
     }
 
-  if (!ptxt)
-    return 0;
+    found = get_eh_frame_info(ei, eh_frame_phdr_offset, load_base, &edi->di_cache);
+    if (found) {
+      edi->di_cache.start_ip = start_ip;
+      edi->di_cache.end_ip = end_ip;
+      edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
+    }
+  }
 
-  *load_base = ptxt->p_vaddr;
-  return 1;
+#if UNW_TARGET_ARM
+  // Verify that the map contains enough space for the arm unwind data.
+  if (arm_exidx_phdr_offset &&
+    arm_exidx_phdr_offset + sizeof(phdr) < ei->u.memory.map->end - ei->u.memory.map->start) {
+    Elf_W(Phdr) phdr;
+    GET_PHDR_FIELD(ei, arm_exidx_phdr_offset, &phdr, p_vaddr);
+    GET_PHDR_FIELD(ei, arm_exidx_phdr_offset, &phdr, p_memsz);
+    edi->di_arm.u.rti.table_data = load_base + phdr.p_vaddr;
+    edi->di_arm.u.rti.table_len = phdr.p_memsz;
+
+    edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
+    edi->di_arm.start_ip = start_ip;
+    edi->di_arm.end_ip = end_ip;
+    edi->di_arm.u.rti.name_ptr = (unw_word_t) path;
+    found = true;
+  }
+#endif
+
+  return found;
 }
-/* End of ANDROID update. */
 
-/* ANDROID support update. */
 int
 dwarf_find_unwind_table (struct elf_dyn_info *edi, struct elf_image *ei,
 			 unw_addr_space_t as, char *path,
 			 unw_word_t segbase, unw_word_t mapoff, unw_word_t ip)
-/* End of ANDROID update. */
 {
   Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL;
   unw_word_t addr, eh_frame_start, fde_count, load_base;
+#if 0
+  // Not currently used.
   unw_word_t max_load_addr = 0;
+#endif
   unw_word_t start_ip = (unw_word_t) -1;
   unw_word_t end_ip = 0;
   struct dwarf_eh_frame_hdr *hdr;
@@ -88,14 +260,19 @@
 
   /* XXX: Much of this code is Linux/LSB-specific.  */
 
-  /* ANDROID support update. */
-  if (!elf_w(valid_object) (ei))
-  /* End of ANDROID update. */
+  if (!ei->valid)
     return -UNW_ENOINFO;
 
+  if (!ei->mapped) {
+    if (dwarf_find_unwind_table_memory (edi, ei, as, path, segbase, mapoff, ip)) {
+      return 1;
+    }
+    return -UNW_ENOINFO;
+  }
+
   /* ANDROID support update. */
-  ehdr = ei->image;
-  phdr = (Elf_W(Phdr) *) ((char *) ei->image + ehdr->e_phoff);
+  ehdr = ei->u.mapped.image;
+  phdr = (Elf_W(Phdr) *) ((char *) ei->u.mapped.image + ehdr->e_phoff);
   /* End of ANDROID update. */
 
   for (i = 0; i < ehdr->e_phnum; ++i)
@@ -112,11 +289,12 @@
 	  if (phdr[i].p_offset == mapoff)
 	    ptxt = phdr + i;
 
-          /* ANDROID support update. */
-	  if ((uintptr_t) ei->image + phdr->p_filesz > max_load_addr)
-	    max_load_addr = (uintptr_t) ei->image + phdr->p_filesz;
+#if 0
+	  // Not currently used.
+	  if ((uintptr_t) ei->u.mapped.image + phdr->p_filesz > max_load_addr)
+	    max_load_addr = (uintptr_t) ei->u.mapped.image + phdr->p_filesz;
+#endif
 	  break;
-          /* End of ANDROID update. */
 
 	case PT_GNU_EH_FRAME:
 	  peh_hdr = phdr + i;
@@ -146,33 +324,30 @@
 
   if (peh_hdr)
     {
-      if (pdyn)
-	{
-	  /* For dynamicly linked executables and shared libraries,
-	     DT_PLTGOT is the value that data-relative addresses are
-	     relative to for that object.  We call this the "gp".  */
-		/* ANDROID support update. */
-		Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset
-						 + (char *) ei->image);
-		/* End of ANDROID update. */
-	  for (; dyn->d_tag != DT_NULL; ++dyn)
-	    if (dyn->d_tag == DT_PLTGOT)
-	      {
-		/* Assume that _DYNAMIC is writable and GLIBC has
-		   relocated it (true for x86 at least).  */
-		edi->di_cache.gp = dyn->d_un.d_ptr;
-		break;
-	      }
-	}
-      else
-	/* Otherwise this is a static executable with no _DYNAMIC.  Assume
-	   that data-relative addresses are relative to 0, i.e.,
-	   absolute.  */
-	edi->di_cache.gp = 0;
+      // For dynamicly linked executables and shared libraries,
+      // DT_PLTGOT is the value that data-relative addresses are
+      // relative to for that object.  We call this the "gp".
+      // Otherwise this is a static executable with no _DYNAMIC.  Assume
+      // that data-relative addresses are relative to 0, i.e.,
+      // absolute.
+      edi->di_cache.gp = 0;
+      if (pdyn) {
+        Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset + (char *) ei->u.mapped.image);
+        while ((char*) dyn - (char*) ei->u.mapped.image + sizeof(Elf_W(Dyn)) < ei->u.mapped.size
+               && dyn->d_tag != DT_NULL) {
+          if (dyn->d_tag == DT_PLTGOT) {
+            // Assume that _DYNAMIC is writable and GLIBC has
+            // relocated it (true for x86 at least).
+            edi->di_cache.gp = dyn->d_un.d_ptr;
+            break;
+          }
+          dyn++;
+        }
+      }
 
       /* ANDROID support update. */
       hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset
-					   + (char *) ei->image);
+					   + (char *) ei->u.mapped.image);
       /* End of ANDROID update. */
       if (hdr->version != DW_EH_VERSION)
 	{
@@ -187,19 +362,17 @@
       /* End of ANDROID update. */
 
       /* Fill in a dummy proc_info structure.  We just need to fill in
-	 enough to ensure that dwarf_read_encoded_pointer() can do it's
+	 enough to ensure that dwarf_read_encoded_pointer() can do its
 	 job.  Since we don't have a procedure-context at this point, all
 	 we have to do is fill in the global-pointer.  */
       memset (&pi, 0, sizeof (pi));
       pi.gp = edi->di_cache.gp;
 
-      /* (Optionally) read eh_frame_ptr: */
       if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
 					     &addr, hdr->eh_frame_ptr_enc, &pi,
 					     &eh_frame_start, NULL)) < 0)
 	return -UNW_ENOINFO;
 
-      /* (Optionally) read fde_count: */
       if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
 					     &addr, hdr->fde_count_enc, &pi,
 					     &fde_count, NULL)) < 0)
@@ -207,9 +380,10 @@
 
       if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
 	{
-    #if 1
-	  abort ();
-    #else
+#if 1
+          // Right now do nothing.
+	  //abort ();
+#else
 	  unw_word_t eh_frame_end;
 
 	  /* If there is no search table or it has an unsupported
@@ -230,30 +404,32 @@
 	  return linear_search (unw_local_addr_space, ip,
 				eh_frame_start, eh_frame_end, fde_count,
 				pi, need_unwind_info, NULL);
-    #endif
+#endif
 	}
+      else
+        {
+          edi->di_cache.start_ip = start_ip;
+          edi->di_cache.end_ip = end_ip;
+          edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
+          edi->di_cache.u.rti.name_ptr = 0;
+          /* two 32-bit values (ip_offset/fde_offset) per table-entry: */
+          edi->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
+          /* ANDROID support update. */
+          edi->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
+                                           + (addr - (uintptr_t) ei->u.mapped.image
+                                           - peh_hdr->p_offset));
+          /* End of ANDROID update. */
 
-      edi->di_cache.start_ip = start_ip;
-      edi->di_cache.end_ip = end_ip;
-      edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
-      edi->di_cache.u.rti.name_ptr = 0;
-      /* two 32-bit values (ip_offset/fde_offset) per table-entry: */
-      edi->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
-      /* ANDROID support update. */
-      edi->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
-				       + (addr - (uintptr_t) ei->image
-					  - peh_hdr->p_offset));
-      /* End of ANDROID update. */
+          /* For the binary-search table in the eh_frame_hdr, data-relative
+             means relative to the start of that section... */
 
-      /* For the binary-search table in the eh_frame_hdr, data-relative
-	 means relative to the start of that section... */
-
-      /* ANDROID support update. */
-      edi->di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
-				    + ((uintptr_t) hdr - (uintptr_t) ei->image
-				       - peh_hdr->p_offset));
-      /* End of ANDROID update. */
-      found = 1;
+          /* ANDROID support update. */
+          edi->di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
+                                        + ((uintptr_t) hdr - (uintptr_t) ei->u.mapped.image
+                                        - peh_hdr->p_offset));
+          /* End of ANDROID update. */
+          found = 1;
+        }
     }
 
 #if UNW_TARGET_ARM
diff --git a/src/elfxx.c b/src/elfxx.c
index a3be163..8f185a8 100644
--- a/src/elfxx.c
+++ b/src/elfxx.c
@@ -33,331 +33,598 @@
 #include <lzma.h>
 #endif /* HAVE_LZMA */
 
-static Elf_W (Shdr)*
-elf_w (section_table) (struct elf_image *ei)
-{
-  Elf_W (Ehdr) *ehdr = ei->image;
-  Elf_W (Off) soff;
-
-  soff = ehdr->e_shoff;
-  if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->size)
-    {
-      Debug (1, "section table outside of image? (%lu > %lu)\n",
-	     (unsigned long) (soff + ehdr->e_shnum * ehdr->e_shentsize),
-	     (unsigned long) ei->size);
-      return NULL;
+// --------------------------------------------------------------------------
+// Functions to read elf data from memory.
+// --------------------------------------------------------------------------
+extern size_t elf_w (memory_read) (
+    struct elf_image* ei, unw_word_t addr, uint8_t* buffer, size_t bytes, bool string_read) {
+  struct map_info* map = ei->u.memory.map;
+  unw_accessors_t* a = unw_get_accessors (ei->u.memory.as);
+  if (map->end - addr < bytes) {
+    bytes = map->end - addr;
+  }
+  size_t bytes_read = 0;
+  unw_word_t data_word;
+  size_t align_bytes = addr & (sizeof(unw_word_t) - 1);
+  if (align_bytes != 0) {
+    if ((*a->access_mem) (ei->u.memory.as, addr & ~(sizeof(unw_word_t) - 1), &data_word,
+                          0, ei->u.memory.as_arg) != 0) {
+      return 0;
+    }
+    size_t copy_bytes = MIN(sizeof(unw_word_t) - align_bytes, bytes);
+    memcpy (buffer, (uint8_t*) (&data_word) + align_bytes, copy_bytes);
+    if (string_read) {
+      // Check for nul terminator.
+      uint8_t* nul_terminator = memchr (buffer, '\0', copy_bytes);
+      if (nul_terminator != NULL) {
+        return nul_terminator - buffer;
+      }
     }
 
-  return (Elf_W (Shdr) *) ((char *) ei->image + soff);
+    addr += copy_bytes;
+    bytes_read += copy_bytes;
+    bytes -= copy_bytes;
+    buffer += copy_bytes;
+  }
+
+  size_t num_words = bytes / sizeof(unw_word_t);
+  size_t i;
+  for (i = 0; i < num_words; i++) {
+    if ((*a->access_mem) (ei->u.memory.as, addr, &data_word, 0, ei->u.memory.as_arg) != 0) {
+      return bytes_read;
+    }
+
+    memcpy (buffer, &data_word, sizeof(unw_word_t));
+    if (string_read) {
+      // Check for nul terminator.
+      uint8_t* nul_terminator = memchr (buffer, '\0', sizeof(unw_word_t));
+      if (nul_terminator != NULL) {
+        return nul_terminator - buffer + bytes_read;
+      }
+    }
+
+    addr += sizeof(unw_word_t);
+    bytes_read += sizeof(unw_word_t);
+    buffer += sizeof(unw_word_t);
+  }
+
+  size_t left_over = bytes & (sizeof(unw_word_t) - 1);
+  if (left_over) {
+    if ((*a->access_mem) (ei->u.memory.as, addr, &data_word, 0, ei->u.memory.as_arg) != 0) {
+      return bytes_read;
+    }
+
+    memcpy (buffer, &data_word, left_over);
+    if (string_read) {
+      // Check for nul terminator.
+      uint8_t* nul_terminator = memchr (buffer, '\0', sizeof(unw_word_t));
+      if (nul_terminator != NULL) {
+        return nul_terminator - buffer + bytes_read;
+      }
+    }
+
+    bytes_read += left_over;
+  }
+  return bytes_read;
 }
 
-static char*
-elf_w (string_table) (struct elf_image *ei, int section)
-{
-  Elf_W (Ehdr) *ehdr = ei->image;
-  Elf_W (Off) soff, str_soff;
-  Elf_W (Shdr) *str_shdr;
+static bool elf_w (section_table_offset) (struct elf_image* ei, Elf_W(Ehdr)* ehdr, Elf_W(Off)* offset) {
+  GET_EHDR_FIELD(ei, ehdr, e_shoff, true);
+  GET_EHDR_FIELD(ei, ehdr, e_shentsize, true);
+  GET_EHDR_FIELD(ei, ehdr, e_shnum, true);
 
-  /* this offset is assumed to be OK */
-  soff = ehdr->e_shoff;
+  uintptr_t size = ei->u.memory.map->end - ei->u.memory.map->start;
+  if (ehdr->e_shoff + ehdr->e_shnum * ehdr->e_shentsize > size) {
+    Debug (1, "section table outside of image? (%lu > %lu)\n",
+           (unsigned long) (ehdr->e_shoff + ehdr->e_shnum * ehdr->e_shentsize),
+           (unsigned long) size);
+    return false;
+  }
 
-  str_soff = soff + (section * ehdr->e_shentsize);
-  if (str_soff + ehdr->e_shentsize > ei->size)
-    {
-      Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
-	     (unsigned long) (str_soff + ehdr->e_shentsize),
-	     (unsigned long) ei->size);
-      return NULL;
-    }
-  str_shdr = (Elf_W (Shdr) *) ((char *) ei->image + str_soff);
-
-  if (str_shdr->sh_offset + str_shdr->sh_size > ei->size)
-    {
-      Debug (1, "string table outside of image? (%lu > %lu)\n",
-	     (unsigned long) (str_shdr->sh_offset + str_shdr->sh_size),
-	     (unsigned long) ei->size);
-      return NULL;
-    }
-
-  Debug (16, "strtab=0x%lx\n", (long) str_shdr->sh_offset);
-  /* End of ANDROID update. */
-  return (char*) ((uintptr_t) ei->image + str_shdr->sh_offset);
-  /* End of ANDROID update. */
+  *offset = ehdr->e_shoff;
+  return true;
 }
 
-static int
-elf_w (lookup_symbol) (unw_addr_space_t as,
-		       unw_word_t ip, struct elf_image *ei,
-		       Elf_W (Addr) load_offset,
-		       char *buf, size_t buf_len, Elf_W (Addr) *min_dist)
-{
-  size_t syment_size;
-  Elf_W (Ehdr) *ehdr = ei->image;
-  Elf_W (Sym) *sym, *symtab, *symtab_end;
-  Elf_W (Shdr) *shdr;
-  Elf_W (Addr) val;
-  int i, ret = -UNW_ENOINFO;
-  char *strtab;
+static bool elf_w (string_table_offset) (
+    struct elf_image* ei, int section, Elf_W(Ehdr)* ehdr, Elf_W(Off)* offset) {
+  GET_EHDR_FIELD(ei, ehdr, e_shoff, true);
+  GET_EHDR_FIELD(ei, ehdr, e_shentsize, true);
+  unw_word_t str_soff = ehdr->e_shoff + (section * ehdr->e_shentsize);
+  uintptr_t size = ei->u.memory.map->end - ei->u.memory.map->start;
+  if (str_soff + ehdr->e_shentsize > size) {
+    Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
+           (unsigned long) (str_soff + ehdr->e_shentsize),
+           (unsigned long) size);
+    return false;
+  }
 
-  if (!elf_w (valid_object) (ei))
-    return -UNW_ENOINFO;
+  Elf_W(Shdr) shdr;
+  GET_SHDR_FIELD(ei, str_soff, &shdr, sh_offset);
+  GET_SHDR_FIELD(ei, str_soff, &shdr, sh_size);
+  if (shdr.sh_offset + shdr.sh_size > size) {
+    Debug (1, "string table outside of image? (%lu > %lu)\n",
+           (unsigned long) (shdr.sh_offset + shdr.sh_size),
+           (unsigned long) size);
+    return false;
+  }
 
-  shdr = elf_w (section_table) (ei);
-  if (!shdr)
-    return -UNW_ENOINFO;
-
-  for (i = 0; i < ehdr->e_shnum; ++i)
-    {
-      switch (shdr->sh_type)
-	{
-	case SHT_SYMTAB:
-	case SHT_DYNSYM:
-	  symtab = (Elf_W (Sym) *) ((char *) ei->image + shdr->sh_offset);
-	  symtab_end = (Elf_W (Sym) *) ((char *) symtab + shdr->sh_size);
-	  syment_size = shdr->sh_entsize;
-
-	  strtab = elf_w (string_table) (ei, shdr->sh_link);
-	  if (!strtab)
-	    break;
-
-	  Debug (16, "symtab=0x%lx[%d]\n",
-		 (long) shdr->sh_offset, shdr->sh_type);
-
-	  for (sym = symtab;
-	       sym < symtab_end;
-	       sym = (Elf_W (Sym) *) ((char *) sym + syment_size))
-	    {
-	      if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC
-		  && sym->st_shndx != SHN_UNDEF)
-		{
-		  if (tdep_get_func_addr (as, sym->st_value, &val) < 0)
-		    continue;
-		  if (sym->st_shndx != SHN_ABS)
-		    val += load_offset;
-		  Debug (16, "0x%016lx info=0x%02x %s\n",
-			 (long) val, sym->st_info, strtab + sym->st_name);
-
-                  /* ANDROID support update */
-                  if ((Elf_W (Addr)) (ip - val) < *min_dist
-                      && (Elf_W (Addr)) (ip - val) < sym->st_size)
-                  /* End of ANDROID update */
-		    {
-		      *min_dist = (Elf_W (Addr)) (ip - val);
-		      strncpy (buf, strtab + sym->st_name, buf_len);
-		      buf[buf_len - 1] = '\0';
-		      ret = (strlen (strtab + sym->st_name) >= buf_len
-			     ? -UNW_ENOMEM : 0);
-		    }
-		}
-	    }
-	  break;
-
-	default:
-	  break;
-	}
-      shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
-    }
-  return ret;
+  Debug (16, "strtab=0x%lx\n", (long) shdr.sh_offset);
+  *offset = shdr.sh_offset;
+  return true;
 }
 
-static Elf_W (Addr)
-elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase,
-			 unsigned long mapoff)
-{
-  Elf_W (Addr) offset = 0;
-  Elf_W (Ehdr) *ehdr;
-  Elf_W (Phdr) *phdr;
+static bool elf_w (lookup_symbol_memory) (
+    unw_addr_space_t as, unw_word_t ip, struct elf_image* ei, Elf_W(Addr) load_offset,
+    char* buf, size_t buf_len, unw_word_t* offp, Elf_W(Ehdr)* ehdr) {
+  Elf_W(Off) shdr_offset;
+  if (!elf_w (section_table_offset) (ei, ehdr, &shdr_offset)) {
+    return false;
+  }
+
+  GET_EHDR_FIELD(ei, ehdr, e_shnum, true);
+  GET_EHDR_FIELD(ei, ehdr, e_shentsize, true);
   int i;
-
-  ehdr = ei->image;
-  phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
-
-  for (i = 0; i < ehdr->e_phnum; ++i)
-    if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff)
+  for (i = 0; i < ehdr->e_shnum; ++i) {
+    Elf_W(Shdr) shdr;
+    GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_type);
+    switch (shdr.sh_type) {
+      case SHT_SYMTAB:
+      case SHT_DYNSYM:
       {
-	offset = segbase - phdr[i].p_vaddr;
-	break;
+        GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_link);
+
+        Elf_W(Off) strtab_offset;
+        if (!elf_w (string_table_offset) (ei, shdr.sh_link, ehdr, &strtab_offset)) {
+          continue;
+        }
+
+        GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_offset);
+        GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_size);
+        GET_SHDR_FIELD(ei, shdr_offset, &shdr, sh_entsize);
+
+        Debug (16, "symtab=0x%lx[%d]\n", (long) shdr.sh_offset, shdr.sh_type);
+
+        unw_word_t sym_offset;
+        unw_word_t symtab_end = shdr.sh_offset + shdr.sh_size;
+        for (sym_offset = shdr.sh_offset;
+             sym_offset < symtab_end;
+             sym_offset += shdr.sh_entsize) {
+          Elf_W(Sym) sym;
+          GET_SYM_FIELD(ei, sym_offset, &sym, st_info);
+          GET_SYM_FIELD(ei, sym_offset, &sym, st_shndx);
+
+          if (ELF_W (ST_TYPE) (sym.st_info) == STT_FUNC && sym.st_shndx != SHN_UNDEF) {
+            GET_SYM_FIELD(ei, sym_offset, &sym, st_value);
+            Elf_W(Addr) val;
+            if (tdep_get_func_addr (as, sym.st_value, &val) < 0) {
+              continue;
+            }
+            if (sym.st_shndx != SHN_ABS) {
+              val += load_offset;
+            }
+            Debug (16, "0x%016lx info=0x%02x\n", (long) val, sym.st_info);
+
+            GET_SYM_FIELD(ei, sym_offset, &sym, st_size);
+            if (ip >= val && (Elf_W(Addr)) (ip - val) < sym.st_size) {
+              GET_SYM_FIELD(ei, sym_offset, &sym, st_name);
+              uintptr_t size = ei->u.memory.map->end - ei->u.memory.map->start;
+              if (strtab_offset + sym.st_name > size) {
+                // Malformed elf symbol table.
+                break;
+              }
+
+              size_t bytes_read = elf_w (memory_read) (
+                  ei, ei->u.memory.map->start + strtab_offset + sym.st_name,
+                  (uint8_t*) buf, buf_len, true);
+              if (bytes_read == 0) {
+                // Empty name, so keep checking the other symbol tables
+                // for a possible match.
+                break;
+              }
+              if (offp != NULL) {
+                *offp = ip - val;
+              }
+              // Ensure the string is nul terminated.
+              buf[buf_len] = '\0';
+              return true;
+            }
+          }
+        }
+        break;
       }
 
-  return offset;
+      default:
+        break;
+    }
+    shdr_offset += ehdr->e_shentsize;
+  }
+  return false;
+}
+
+static bool elf_w (get_load_offset_memory) (
+    struct elf_image* ei, unsigned long segbase, unsigned long mapoff,
+    Elf_W(Ehdr)* ehdr, Elf_W(Addr)* load_offset) {
+  GET_EHDR_FIELD(ei, ehdr, e_phoff, true);
+  GET_EHDR_FIELD(ei, ehdr, e_phnum, true);
+
+  unw_word_t offset = ehdr->e_phoff;
+  int i;
+  for (i = 0; i < ehdr->e_phnum; ++i) {
+    Elf_W(Phdr) phdr;
+    GET_PHDR_FIELD(ei, offset, &phdr, p_type);
+    if (phdr.p_type == PT_LOAD) {
+      GET_PHDR_FIELD(ei, offset, &phdr, p_offset);
+      if (phdr.p_offset == mapoff) {
+        GET_PHDR_FIELD(ei, offset, &phdr, p_vaddr);
+        *load_offset = segbase - phdr.p_vaddr;
+        return true;
+      }
+    }
+    offset += sizeof(Elf_W(Phdr));
+  }
+  return false;
+}
+
+// --------------------------------------------------------------------------
+// Functions to read elf data from the mapped elf image.
+// --------------------------------------------------------------------------
+static Elf_W(Shdr)* elf_w (section_table) (struct elf_image* ei) {
+  Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
+  Elf_W(Off) soff = ehdr->e_shoff;
+  if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->u.mapped.size) {
+    Debug (1, "section table outside of image? (%lu > %lu)\n",
+           (unsigned long) (soff + ehdr->e_shnum * ehdr->e_shentsize),
+           (unsigned long) ei->u.mapped.size);
+    return NULL;
+  }
+
+  return (Elf_W(Shdr) *) ((char *) ei->u.mapped.image + soff);
+}
+
+static char* elf_w (string_table) (struct elf_image* ei, int section) {
+  Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
+  Elf_W(Off) str_soff = ehdr->e_shoff + (section * ehdr->e_shentsize);
+  if (str_soff + ehdr->e_shentsize > ei->u.mapped.size) {
+    Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
+           (unsigned long) (str_soff + ehdr->e_shentsize),
+           (unsigned long) ei->u.mapped.size);
+    return NULL;
+  }
+  Elf_W(Shdr)* str_shdr = (Elf_W(Shdr) *) ((char *) ei->u.mapped.image + str_soff);
+
+  if (str_shdr->sh_offset + str_shdr->sh_size > ei->u.mapped.size) {
+    Debug (1, "string table outside of image? (%lu > %lu)\n",
+           (unsigned long) (str_shdr->sh_offset + str_shdr->sh_size),
+           (unsigned long) ei->u.mapped.size);
+    return NULL;
+  }
+
+  Debug (16, "strtab=0x%lx\n", (long) str_shdr->sh_offset);
+  return (char*) ((uintptr_t) ei->u.mapped.image + str_shdr->sh_offset);
+}
+
+static bool elf_w (lookup_symbol_mapped) (
+    unw_addr_space_t as, unw_word_t ip, struct elf_image* ei, Elf_W(Addr) load_offset,
+    char* buf, size_t buf_len, unw_word_t* offp) {
+
+  Elf_W(Shdr)* shdr = elf_w (section_table) (ei);
+  if (!shdr) {
+    return false;
+  }
+
+  Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
+  int i;
+  for (i = 0; i < ehdr->e_shnum; ++i) {
+    switch (shdr->sh_type) {
+      case SHT_SYMTAB:
+      case SHT_DYNSYM:
+      {
+        Elf_W(Sym)* symtab = (Elf_W(Sym) *) ((char *) ei->u.mapped.image + shdr->sh_offset);
+        Elf_W(Sym)* symtab_end = (Elf_W(Sym) *) ((char *) symtab + shdr->sh_size);
+
+        char* strtab = elf_w (string_table) (ei, shdr->sh_link);
+        if (!strtab) {
+          continue;
+        }
+
+        Debug (16, "symtab=0x%lx[%d]\n", (long) shdr->sh_offset, shdr->sh_type);
+
+        Elf_W(Sym)* sym;
+        for (sym = symtab;
+             sym < symtab_end;
+             sym = (Elf_W(Sym) *) ((char *) sym + shdr->sh_entsize)) {
+          if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC && sym->st_shndx != SHN_UNDEF) {
+            Elf_W(Addr) val;
+            if (tdep_get_func_addr (as, sym->st_value, &val) < 0) {
+              continue;
+            }
+            if (sym->st_shndx != SHN_ABS) {
+              val += load_offset;
+            }
+            Debug (16, "0x%016lx info=0x%02x\n", (long) val, sym->st_info);
+            if (ip >= val && (Elf_W(Addr)) (ip - val) < sym->st_size) {
+              if (strtab + sym->st_name > (char*) ei->u.mapped.image + ei->u.mapped.size) {
+                // Malformed elf symbol table.
+                break;
+              }
+
+              // Make sure we don't try and read past the end of the image.
+              uintptr_t max_size = (uintptr_t) (strtab + sym->st_name)
+                  - (uintptr_t) ei->u.mapped.image;
+              if (buf_len > max_size) {
+                buf_len = max_size;
+              }
+
+              strncpy (buf, strtab + sym->st_name, buf_len);
+              if (buf[0] == '\0') {
+                // Empty name, so keep checking the other symbol tables
+                // for a possible match.
+                break;
+              }
+              if (offp != NULL) {
+                *offp = ip - val;
+              }
+              // Ensure the string is nul terminated.
+              buf[buf_len] = '\0';
+              return true;
+            }
+          }
+        }
+        break;
+      }
+
+      default:
+        break;
+    }
+    shdr = (Elf_W(Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
+  }
+  return false;
+}
+
+static Elf_W(Addr) elf_w (get_load_offset_mapped) (
+    struct elf_image *ei, unsigned long segbase, unsigned long mapoff) {
+  Elf_W(Ehdr) *ehdr = ei->u.mapped.image;
+  Elf_W(Phdr) *phdr = (Elf_W(Phdr) *) ((char *) ei->u.mapped.image + ehdr->e_phoff);
+
+  int i;
+  for (i = 0; i < ehdr->e_phnum; ++i) {
+    if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff) {
+      return segbase - phdr[i].p_vaddr;
+    }
+  }
+  return 0;
+}
+
+// --------------------------------------------------------------------------
+
+static inline bool elf_w (lookup_symbol) (
+    unw_addr_space_t as, unw_word_t ip, struct elf_image *ei, Elf_W(Addr) load_offset,
+    char *buf, size_t buf_len, unw_word_t* offp, Elf_W(Ehdr)* ehdr) {
+  if (!ei->valid)
+    return false;
+
+  if (buf_len <= 1) {
+    Debug (1, "lookup_symbol called with a buffer too small to hold a name %zu\n", buf_len);
+    return false;
+  }
+
+  // Leave enough space for the nul terminator.
+  buf_len--;
+
+  if (ei->mapped) {
+    return elf_w (lookup_symbol_mapped) (as, ip, ei, load_offset, buf, buf_len, offp);
+  } else {
+    return elf_w (lookup_symbol_memory) (as, ip, ei, load_offset, buf, buf_len, offp, ehdr);
+  }
+}
+
+static Elf_W(Addr) elf_w (get_load_offset) (
+    struct elf_image* ei, unsigned long segbase, unsigned long mapoff, Elf_W(Ehdr)* ehdr) {
+  if (ei->mapped) {
+    return elf_w (get_load_offset_mapped) (ei, segbase, mapoff);
+  } else {
+    Elf_W(Addr) load_offset;
+    if (elf_w (get_load_offset_memory) (ei, segbase, mapoff, ehdr, &load_offset)) {
+      return load_offset;
+    }
+  }
+  return 0;
 }
 
 #if HAVE_LZMA
-static size_t
-xz_uncompressed_size (uint8_t *compressed, size_t length)
-{
+static size_t xz_uncompressed_size (uint8_t* compressed, size_t length) {
   uint64_t memlimit = UINT64_MAX;
   size_t ret = 0, pos = 0;
   lzma_stream_flags options;
   lzma_index *index;
 
-  if (length < LZMA_STREAM_HEADER_SIZE)
+  if (length < LZMA_STREAM_HEADER_SIZE) {
     return 0;
+  }
 
   uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
-  if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
+  if (lzma_stream_footer_decode (&options, footer) != LZMA_OK) {
     return 0;
+  }
 
-  if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
+  if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size) {
     return 0;
+  }
 
-  uint8_t *indexdata = footer - options.backward_size;
+  uint8_t* indexdata = footer - options.backward_size;
   if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
-				&pos, options.backward_size) != LZMA_OK)
+                                &pos, options.backward_size) != LZMA_OK) {
     return 0;
+  }
 
-  if (lzma_index_size (index) == options.backward_size)
-    {
-      ret = lzma_index_uncompressed_size (index);
-    }
+  if (lzma_index_size (index) == options.backward_size) {
+    ret = lzma_index_uncompressed_size (index);
+  }
 
   lzma_index_end (index, NULL);
   return ret;
 }
 
-static int
-elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
-{
-  Elf_W (Ehdr) *ehdr = ei->image;
-  Elf_W (Shdr) *shdr;
+static bool elf_w (extract_minidebuginfo) (struct elf_image* ei, struct elf_image* mdi, Elf_W(Ehdr)* ehdr) {
+  Elf_W(Ehdr)* ehdr = ei->image;
+  Elf_W(Shdr)* shdr;
   char *strtab;
   int i;
   uint8_t *compressed = NULL;
   uint64_t memlimit = UINT64_MAX; /* no memory limit */
   size_t compressed_len, uncompressed_len;
 
-  if (!elf_w (valid_object) (ei))
-    return 0;
+  if (!ei->valid) {
+    return false;
+  }
 
   shdr = elf_w (section_table) (ei);
-  if (!shdr)
-    return 0;
+  if (!shdr) {
+    return false;
+  }
 
   strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
-  if (!strtab)
-    return 0;
+  if (!strtab) {
+    return false;
+  }
 
-  for (i = 0; i < ehdr->e_shnum; ++i)
-    {
-      if (strcmp (strtab + shdr->sh_name, ".gnu_debugdata") == 0)
-	{
-	  if (shdr->sh_offset + shdr->sh_size > ei->size)
-	    {
-	      Debug (1, ".gnu_debugdata outside image? (0x%lu > 0x%lu)\n",
-		     (unsigned long) shdr->sh_offset + shdr->sh_size,
-		     (unsigned long) ei->size);
-	      return 0;
-	    }
+  for (i = 0; i < ehdr->e_shnum; ++i) {
+    if (strcmp (strtab + shdr->sh_name, ".gnu_debugdata") == 0) {
+      if (shdr->sh_offset + shdr->sh_size > ei->size) {
+        Debug (1, ".gnu_debugdata outside image? (0x%lu > 0x%lu)\n",
+               (unsigned long) shdr->sh_offset + shdr->sh_size,
+               (unsigned long) ei->size);
+        return false;
+      }
 
-	  Debug (16, "found .gnu_debugdata at 0x%lx\n",
-		 (unsigned long) shdr->sh_offset);
-	  compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
-	  compressed_len = shdr->sh_size;
-	  break;
-	}
-
-      shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
+      Debug (16, "found .gnu_debugdata at 0x%lx\n",
+             (unsigned long) shdr->sh_offset);
+             compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
+             compressed_len = shdr->sh_size;
+      break;
     }
 
+    shdr = (Elf_W(Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
+  }
+
   /* not found */
-  if (!compressed)
-    return 0;
+  if (!compressed) {
+    return false;
+  }
 
   uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
-  if (uncompressed_len == 0)
-    {
-      Debug (1, "invalid .gnu_debugdata contents\n");
-      return 0;
-    }
+  if (uncompressed_len == 0) {
+    Debug (1, "invalid .gnu_debugdata contents\n");
+    return false;
+  }
 
   mdi->size = uncompressed_len;
   mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
-		     MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+                     MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
 
-  if (mdi->image == MAP_FAILED)
-    return 0;
+  if (mdi->image == MAP_FAILED) {
+    return false;
+  }
 
   size_t in_pos = 0, out_pos = 0;
   lzma_ret lret;
   lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
-				    compressed, &in_pos, compressed_len,
-				    mdi->image, &out_pos, mdi->size);
-  if (lret != LZMA_OK)
-    {
-      Debug (1, "LZMA decompression failed: %d\n", lret);
-      munmap (mdi->image, mdi->size);
-      return 0;
-    }
+                                    compressed, &in_pos, compressed_len,
+                                    mdi->image, &out_pos, mdi->size);
+  if (lret != LZMA_OK) {
+    Debug (1, "LZMA decompression failed: %d\n", lret);
+    munmap (mdi->image, mdi->size);
+    return false;
+  }
 
-  return 1;
+  return true;
 }
 #else
-static int
-elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
-{
-  return 0;
+static bool elf_w (extract_minidebuginfo) (struct elf_image* ei, struct elf_image* mdi, Elf_W(Ehdr)* ehdr) {
+  return false;
 }
 #endif /* !HAVE_LZMA */
 
-/* Find the ELF image that contains IP and return the "closest"
-   procedure name, if there is one.  With some caching, this could be
-   sped up greatly, but until an application materializes that's
-   sensitive to the performance of this routine, why bother...  */
+// Find the ELF image that contains IP and return the procedure name from
+// the symbol table that matches the IP.
+HIDDEN bool elf_w (get_proc_name_in_image) (
+    unw_addr_space_t as, struct elf_image* ei, unsigned long segbase, unsigned long mapoff,
+    unw_word_t ip, char* buf, size_t buf_len, unw_word_t* offp) {
+  Elf_W(Ehdr) ehdr;
+  memset(&ehdr, 0, sizeof(ehdr));
+  Elf_W(Addr) load_offset = elf_w (get_load_offset) (ei, segbase, mapoff, &ehdr);
+  if (load_offset == 0) {
+    return false;
+  }
 
-HIDDEN int
-elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei,
-		       unsigned long segbase,
-		       unsigned long mapoff,
-		       unw_word_t ip,
-		       char *buf, size_t buf_len, unw_word_t *offp)
-{
-  Elf_W (Addr) load_offset;
-  Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
-  int ret;
+  if (elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, offp, &ehdr) != 0) {
+    return true;
+  }
 
-  load_offset = elf_w (get_load_offset) (ei, segbase, mapoff);
-  ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, &min_dist);
-
-  /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in
-     there as well and replace the previously found if it is closer. */
+  // If the ELF image doesn't contain a match, look up the symbol in
+  // the MiniDebugInfo.
   struct elf_image mdi;
-  if (elf_w (extract_minidebuginfo) (ei, &mdi))
-    {
-      int ret_mdi;
-
-      load_offset = elf_w (get_load_offset) (&mdi, segbase, mapoff);
-      ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
-				       buf_len, &min_dist);
-
-      /* Closer symbol was found (possibly truncated). */
-      if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
-	{
-	  ret = ret_mdi;
-	}
-
-      munmap (mdi.image, mdi.size);
+  if (elf_w (extract_minidebuginfo) (ei, &mdi, &ehdr)) {
+    load_offset = elf_w (get_load_offset) (&mdi, segbase, mapoff, &ehdr);
+    if (load_offset == 0) {
+      return false;
     }
-
-  if (min_dist >= ei->size)
-    return -UNW_ENOINFO;		/* not found */
-  if (offp)
-    *offp = min_dist;
-  return ret;
+    if (elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf, buf_len, offp, &ehdr)) {
+      munmap (mdi.u.mapped.image, mdi.u.mapped.size);
+      return true;
+    }
+    return false;
+  }
+  return false;
 }
 
-/* ANDROID support update. */
-HIDDEN int
-elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
-		       char *buf, size_t buf_len, unw_word_t *offp)
-{
+HIDDEN bool elf_w (get_proc_name) (
+    unw_addr_space_t as, pid_t pid, unw_word_t ip, char* buf, size_t buf_len,
+    unw_word_t* offp, void* as_arg) {
   unsigned long segbase, mapoff;
   struct elf_image ei;
-  int ret;
 
-  ret = tdep_get_elf_image(as, &ei, pid, ip, &segbase, &mapoff, NULL);
-  if (ret < 0)
-    return ret;
+  if (tdep_get_elf_image(as, &ei, pid, ip, &segbase, &mapoff, NULL, as_arg) < 0) {
+    return false;
+  }
 
   return elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
 }
-/* ANDROID support update. */
+
+HIDDEN bool elf_w (get_load_base) (struct elf_image* ei, unw_word_t mapoff, unw_word_t* load_base) {
+  if (!ei->valid) {
+    return false;
+  }
+
+  if (ei->mapped) {
+    Elf_W(Ehdr)* ehdr = ei->u.mapped.image;
+    Elf_W(Phdr)* phdr = (Elf_W(Phdr)*) ((char*) ei->u.mapped.image + ehdr->e_phoff);
+    int i;
+    for (i = 0; i < ehdr->e_phnum; ++i) {
+      if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff) {
+        *load_base = phdr[i].p_vaddr;
+        return true;
+      }
+    }
+    return false;
+  } else {
+    Elf_W(Ehdr) ehdr;
+    GET_EHDR_FIELD(ei, &ehdr, e_phnum, false);
+    GET_EHDR_FIELD(ei, &ehdr, e_phoff, false);
+    int i;
+    unw_word_t offset = ehdr.e_phoff;
+    for (i = 0; i < ehdr.e_phnum; ++i) {
+      Elf_W(Phdr) phdr;
+      GET_PHDR_FIELD(ei, offset, &phdr, p_type);
+      GET_PHDR_FIELD(ei, offset, &phdr, p_offset);
+      if (phdr.p_type == PT_LOAD && phdr.p_offset == mapoff) {
+        GET_PHDR_FIELD(ei, offset, &phdr, p_vaddr);
+        *load_base = phdr.p_vaddr;
+        return true;
+      }
+      offset += sizeof(phdr);
+    }
+    return false;
+  }
+  return false;
+}
diff --git a/src/elfxx.h b/src/elfxx.h
index c331e50..0166d79 100644
--- a/src/elfxx.h
+++ b/src/elfxx.h
@@ -25,6 +25,7 @@
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 
 #include <fcntl.h>
+#include <stddef.h>
 #include <unistd.h>
 
 #include <sys/mman.h>
@@ -43,79 +44,131 @@
 # define elf_w(x)	_Uelf64_##x
 #endif
 
-extern int elf_w (get_proc_name) (unw_addr_space_t as,
-				  pid_t pid, unw_word_t ip,
-				  char *buf, size_t len,
-				  unw_word_t *offp);
+#define GET_FIELD(ei, offset, struct_name, elf_struct, field, check_cached) \
+  { \
+    if (!check_cached || (elf_struct)->field == 0) { \
+      if (sizeof((elf_struct)->field) != elf_w (memory_read) ( \
+          ei, ei->u.memory.map->start + offset + offsetof(struct_name, field), \
+          (uint8_t*) &((elf_struct)->field), sizeof((elf_struct)->field), false)) { \
+        return false; \
+      } \
+    } \
+  }
 
-extern int elf_w (get_proc_name_in_image) (unw_addr_space_t as,
-					   struct elf_image *ei,
-					   unsigned long segbase,
-					   unsigned long mapoff,
-					   unw_word_t ip,
-					   char *buf, size_t buf_len, unw_word_t *offp);
+#define GET_EHDR_FIELD(ei, ehdr, field, check_cached) \
+  GET_FIELD(ei, 0, Elf_W(Ehdr), ehdr, field, check_cached)
 
-static inline int
-elf_w (valid_object) (struct elf_image *ei)
-{
-  if (ei->size <= EI_VERSION)
-    return 0;
+#define GET_PHDR_FIELD(ei, offset, phdr, field) \
+  GET_FIELD(ei, offset, Elf_W(Phdr), phdr, field, false)
 
-  return (memcmp (ei->image, ELFMAG, SELFMAG) == 0
-	  && ((uint8_t *) ei->image)[EI_CLASS] == ELF_CLASS
-	  && ((uint8_t *) ei->image)[EI_VERSION] != EV_NONE
-	  && ((uint8_t *) ei->image)[EI_VERSION] <= EV_CURRENT);
+#define GET_SHDR_FIELD(ei, offset, shdr, field) \
+  GET_FIELD(ei, offset, Elf_W(Shdr), shdr, field, false)
+
+#define GET_SYM_FIELD(ei, offset, sym, field) \
+  GET_FIELD(ei, offset, Elf_W(Sym), sym, field, false)
+
+#define GET_DYN_FIELD(ei, offset, dyn, field) \
+  GET_FIELD(ei, offset, Elf_W(Dyn), dyn, field, false)
+
+extern bool elf_w (get_proc_name) (
+    unw_addr_space_t as, pid_t pid, unw_word_t ip, char* buf, size_t len,
+    unw_word_t* offp, void* as_arg);
+
+extern bool elf_w (get_proc_name_in_image) (
+    unw_addr_space_t as, struct elf_image* ei, unsigned long segbase,
+    unsigned long mapoff, unw_word_t ip, char* buf, size_t buf_len, unw_word_t* offp);
+
+extern bool elf_w (get_load_base) (struct elf_image* ei, unw_word_t mapoff, unw_word_t* load_base);
+
+extern size_t elf_w (memory_read) (
+    struct elf_image* ei, unw_word_t addr, uint8_t* buffer, size_t bytes, bool string_read);
+
+static inline bool elf_w (valid_object_mapped) (struct elf_image* ei) {
+  if (ei->u.mapped.size <= EI_VERSION) {
+    return false;
+  }
+
+  uint8_t* e_ident = (uint8_t*) ei->u.mapped.image;
+  return (memcmp (ei->u.mapped.image, ELFMAG, SELFMAG) == 0
+          && e_ident[EI_CLASS] == ELF_CLASS && e_ident[EI_VERSION] != EV_NONE
+          && e_ident[EI_VERSION] <= EV_CURRENT);
 }
 
-static inline int
-elf_map_image (struct elf_image *ei, const char *path)
-{
+static inline bool elf_w (valid_object_memory) (struct elf_image* ei) {
+  uint8_t e_ident[EI_NIDENT];
+  struct map_info* map = ei->u.memory.map;
+  if (SELFMAG != elf_w (memory_read) (ei, map->start, e_ident, SELFMAG, false)) {
+    return false;
+  }
+  if (memcmp (e_ident, ELFMAG, SELFMAG) != 0) {
+    return false;
+  }
+  // Read the rest of the ident data.
+  if (EI_NIDENT - SELFMAG != elf_w (memory_read) (
+      ei, map->start + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG, false)) {
+    return false;
+  }
+  return e_ident[EI_CLASS] == ELF_CLASS && e_ident[EI_VERSION] != EV_NONE
+         && e_ident[EI_VERSION] <= EV_CURRENT;
+}
+
+static inline bool elf_map_image (struct elf_image* ei, const char* path) {
   struct stat stat;
   int fd;
 
   fd = open (path, O_RDONLY);
-  if (fd < 0)
-    return -1;
-
-  if (fstat (fd, &stat) < 0)
-    {
-      close (fd);
-      return -1;
-    }
-
-  ei->size = stat.st_size;
-  ei->image = mmap (NULL, ei->size, PROT_READ, MAP_PRIVATE, fd, 0);
-  close (fd);
-  if (ei->image == MAP_FAILED)
-    return -1;
-
-  if (!elf_w (valid_object) (ei))
-  {
-    munmap(ei->image, ei->size);
-    return -1;
+  if (fd < 0) {
+    return false;
   }
 
-  return 0;
+  if (fstat (fd, &stat) == -1) {
+    close (fd);
+    return false;
+  }
+
+  ei->u.mapped.size = stat.st_size;
+  ei->u.mapped.image = mmap (NULL, ei->u.mapped.size, PROT_READ, MAP_PRIVATE, fd, 0);
+  close (fd);
+  if (ei->u.mapped.image == MAP_FAILED) {
+    return false;
+  }
+
+  ei->valid = elf_w (valid_object_mapped) (ei);
+  if (!ei->valid) {
+    munmap (ei->u.mapped.image, ei->u.mapped.size);
+  }
+
+  ei->mapped = true;
+  // Set to true for cases where this is called outside of elf_map_cached.
+  ei->load_attempted = true;
+
+  return true;
 }
 
-/* ANDROID support update */
-static inline int
-elf_map_cached_image (struct map_info *map, unw_word_t ip)
-{
+static inline bool elf_map_cached_image (
+    unw_addr_space_t as, void* as_arg, struct map_info* map, unw_word_t ip) {
   intrmask_t saved_mask;
-  int return_value = 0;
 
-  /* Lock while loading the cached elf image. */
+  // Lock while loading the cached elf image.
   lock_acquire (&map->ei_lock, saved_mask);
-  if (map->ei.image == NULL)
-    {
-      if (elf_map_image(&map->ei, map->path) < 0)
-        {
-          map->ei.image = NULL;
-          return_value = -1;
-        }
+  if (!map->ei.load_attempted) {
+    map->ei.load_attempted = true;
+
+    if (!elf_map_image (&map->ei, map->path)) {
+      // If the image cannot be loaded, we'll read data directly from
+      // the process using the access_mem function.
+      if (map->flags & PROT_READ) {
+        map->ei.u.memory.map = map;
+        map->ei.u.memory.as = as;
+        map->ei.u.memory.as_arg = as_arg;
+        map->ei.valid = elf_w (valid_object_memory) (&map->ei);
+      }
     }
+    unw_word_t load_base;
+    if (map->ei.valid && elf_w (get_load_base) (&map->ei, map->offset, &load_base)) {
+      map->load_base = load_base;
+    }
+  }
   lock_release (&map->ei_lock, saved_mask);
-  return return_value;
+  return map->ei.valid;
 }
-/* End of ANDROID update */
diff --git a/src/hppa/Ginit.c b/src/hppa/Ginit.c
index d98ad20..ac0a618 100644
--- a/src/hppa/Ginit.c
+++ b/src/hppa/Ginit.c
@@ -200,7 +200,7 @@
 		      char *buf, size_t buf_len, unw_word_t *offp,
 		      void *arg)
 {
-  return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
+  return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp, arg);
 }
 
 HIDDEN void
diff --git a/src/ia64/Ginit.c b/src/ia64/Ginit.c
index 5f38bb0..874157b 100644
--- a/src/ia64/Ginit.c
+++ b/src/ia64/Ginit.c
@@ -376,7 +376,7 @@
 		      char *buf, size_t buf_len, unw_word_t *offp,
 		      void *arg)
 {
-  return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
+  return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp, arg);
 }
 
 HIDDEN void
diff --git a/src/mi/map.c b/src/mi/map.c
index b5734b6..f604755 100644
--- a/src/mi/map.c
+++ b/src/mi/map.c
@@ -118,8 +118,8 @@
     {
       map = map_info;
       map_info = map->next;
-      if (map->ei.image != MAP_FAILED && map->ei.image != NULL)
-        munmap (map->ei.image, map->ei.size);
+      if (map->ei.mapped)
+        munmap (map->ei.u.mapped.image, map->ei.u.mapped.size);
       if (map->path)
         free (map->path);
       map_free_info (map);
diff --git a/src/mips/Ginit.c b/src/mips/Ginit.c
index b2c318a..308c047 100644
--- a/src/mips/Ginit.c
+++ b/src/mips/Ginit.c
@@ -206,7 +206,7 @@
 		      void *arg)
 {
 
-  return elf_w (get_proc_name) (as, getpid (), ip, buf, buf_len, offp);
+  return elf_w (get_proc_name) (as, getpid (), ip, buf, buf_len, offp, arg);
 }
 
 HIDDEN void
diff --git a/src/os-common.c b/src/os-common.c
index 86293b2..8f32af9 100644
--- a/src/os-common.c
+++ b/src/os-common.c
@@ -26,25 +26,26 @@
 #include "libunwind_i.h"
 #include "map_info.h"
 
-extern int local_get_elf_image (struct elf_image *, unw_word_t,
-                                unsigned long *, unsigned long *,
-                                char **);
+extern int local_get_elf_image (unw_addr_space_t as, struct elf_image *,
+                                unw_word_t, unsigned long *, unsigned long *,
+                                char **, void *);
 
 PROTECTED int
 tdep_get_elf_image (unw_addr_space_t as, struct elf_image *ei,
                     pid_t pid, unw_word_t ip,
-                    unsigned long *segbase, unsigned long *mapoff, char **path)
+                    unsigned long *segbase, unsigned long *mapoff, char **path,
+                    void *as_arg)
 {
   struct map_info *map;
 
   if (pid == getpid())
-    return local_get_elf_image (ei, ip, segbase, mapoff, path);
+    return local_get_elf_image (as, ei, ip, segbase, mapoff, path, as_arg);
 
   map = map_find_from_addr (as->map_list, ip);
   if (!map)
     return -UNW_ENOINFO;
 
-  if (elf_map_cached_image (map, ip) < 0)
+  if (!elf_map_cached_image (as, as_arg, map, ip))
     return -UNW_ENOINFO;
 
   *ei = map->ei;
diff --git a/src/os-linux.c b/src/os-linux.c
index 1c24333..3c675a4 100644
--- a/src/os-linux.c
+++ b/src/os-linux.c
@@ -55,8 +55,9 @@
       cur_map->flags = flags;
       cur_map->path = strdup (mi.path);
       mutex_init (&cur_map->ei_lock);
-      cur_map->ei.size = 0;
-      cur_map->ei.image = NULL;
+      cur_map->ei.valid = false;
+      cur_map->ei.load_attempted = false;
+      cur_map->ei.mapped = false;
 
       /* Indicate mapped memory of devices is special and should not
          be read or written. Use a special flag instead of zeroing the
@@ -72,15 +73,12 @@
       if ((flags & (PROT_EXEC | PROT_READ)) == (PROT_EXEC | PROT_READ)
           && !(cur_map->flags & MAP_FLAGS_DEVICE_MEM))
         {
-          /* No locking needed since we are building the list here. */
           if (elf_map_image (&cur_map->ei, cur_map->path) == 0)
             {
               unw_word_t load_base;
-              if (dwarf_get_load_base (&cur_map->ei, offset, &load_base) != 0)
+              if (elf_w (get_load_base) (&cur_map->ei, offset, &load_base))
                 cur_map->load_base = load_base;
             }
-          else
-            cur_map->ei.image = NULL;
         }
 
       map_list = cur_map;
diff --git a/src/ppc32/Ginit.c b/src/ppc32/Ginit.c
index c555c5d..9b9127a 100644
--- a/src/ppc32/Ginit.c
+++ b/src/ppc32/Ginit.c
@@ -222,7 +222,7 @@
 		      char *buf, size_t buf_len, unw_word_t *offp,
 		      void *arg)
 {
-  return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
+  return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp, arg);
 }
 
 HIDDEN void
diff --git a/src/ppc64/Ginit.c b/src/ppc64/Ginit.c
index 578c282..db9bf8c 100644
--- a/src/ppc64/Ginit.c
+++ b/src/ppc64/Ginit.c
@@ -231,7 +231,7 @@
 		      char *buf, size_t buf_len, unw_word_t *offp,
 		      void *arg)
 {
-  return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
+  return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp, arg);
 }
 
 HIDDEN void
diff --git a/src/ptrace/_UPT_find_proc_info.c b/src/ptrace/_UPT_find_proc_info.c
index a99a8e3..acaa5f9 100644
--- a/src/ptrace/_UPT_find_proc_info.c
+++ b/src/ptrace/_UPT_find_proc_info.c
@@ -33,7 +33,7 @@
 #include "_UPT_internal.h"
 
 static int
-get_unwind_info (struct elf_dyn_info *edi, pid_t pid, unw_addr_space_t as, unw_word_t ip)
+get_unwind_info (struct elf_dyn_info *edi, pid_t pid, unw_addr_space_t as, unw_word_t ip, void *as_arg)
 {
   /* ANDROID support update. */
   unsigned long segbase, mapoff;
@@ -63,7 +63,7 @@
   invalidate_edi(edi);
 
   /* ANDROID support update. */
-  if (tdep_get_elf_image (as, &ei, pid, ip, &segbase, &mapoff, &path) < 0)
+  if (tdep_get_elf_image (as, &ei, pid, ip, &segbase, &mapoff, &path, as_arg) < 0)
     return -UNW_ENOINFO;
 
   ret = tdep_find_unwind_table (edi, &ei, as, path, segbase, mapoff, ip);
@@ -101,7 +101,7 @@
   struct UPT_info *ui = arg;
   int ret = -UNW_ENOINFO;
 
-  if (get_unwind_info (&ui->edi, ui->pid, as, ip) < 0)
+  if (get_unwind_info (&ui->edi, ui->pid, as, ip, arg) < 0)
     return -UNW_ENOINFO;
 
 #if UNW_TARGET_IA64
diff --git a/src/ptrace/_UPT_get_dyn_info_list_addr.c b/src/ptrace/_UPT_get_dyn_info_list_addr.c
index 25fbf85..7bc8f81 100644
--- a/src/ptrace/_UPT_get_dyn_info_list_addr.c
+++ b/src/ptrace/_UPT_get_dyn_info_list_addr.c
@@ -48,10 +48,6 @@
 
       invalidate_edi(&ui->edi);
 
-      if (elf_map_image (&ui->edi.ei, path) < 0)
-	/* ignore unmappable stuff like "/SYSV00001b58 (deleted)" */
-	continue;
-
       Debug (16, "checking object %s\n", path);
 
       if (tdep_find_unwind_table (&ui->edi, as, path, lo, off, 0) > 0)
diff --git a/src/ptrace/_UPT_get_proc_name.c b/src/ptrace/_UPT_get_proc_name.c
index 6ac85a0..2d718fc 100644
--- a/src/ptrace/_UPT_get_proc_name.c
+++ b/src/ptrace/_UPT_get_proc_name.c
@@ -33,9 +33,9 @@
   struct UPT_info *ui = arg;
 
 #if ELF_CLASS == ELFCLASS64
-  return _Uelf64_get_proc_name (as, ui->pid, ip, buf, buf_len, offp);
+  return _Uelf64_get_proc_name (as, ui->pid, ip, buf, buf_len, offp, arg);
 #elif ELF_CLASS == ELFCLASS32
-  return _Uelf32_get_proc_name (as, ui->pid, ip, buf, buf_len, offp);
+  return _Uelf32_get_proc_name (as, ui->pid, ip, buf, buf_len, offp, arg);
 #else
   return -UNW_ENOINFO;
 #endif
diff --git a/src/sh/Ginit.c b/src/sh/Ginit.c
index 7a0cd01..72dea84 100644
--- a/src/sh/Ginit.c
+++ b/src/sh/Ginit.c
@@ -192,7 +192,7 @@
 		      char *buf, size_t buf_len, unw_word_t *offp,
 		      void *arg)
 {
-  return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
+  return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp, arg);
 }
 
 HIDDEN void
diff --git a/src/x86/Ginit.c b/src/x86/Ginit.c
index 5be6113..b9da78a 100644
--- a/src/x86/Ginit.c
+++ b/src/x86/Ginit.c
@@ -254,7 +254,7 @@
 		      char *buf, size_t buf_len, unw_word_t *offp,
 		      void *arg)
 {
-  return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
+  return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp, arg);
 }
 
 HIDDEN void
diff --git a/src/x86_64/Ginit.c b/src/x86_64/Ginit.c
index 3b84e4e..db18485 100644
--- a/src/x86_64/Ginit.c
+++ b/src/x86_64/Ginit.c
@@ -273,7 +273,7 @@
 		      char *buf, size_t buf_len, unw_word_t *offp,
 		      void *arg)
 {
-  return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
+  return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp, arg);
 }
 
 HIDDEN void