Fix unwind in exec maps with non-zero offsets.

The packed relocations have a non-zero load base, so libunwind needs
to account for that.

Bug: 20687795
Change-Id: If3ea886799d0ed6750fb8c67a96ddb90acd757c9
diff --git a/include/dwarf.h b/include/dwarf.h
index c66a206..1e34376 100644
--- a/include/dwarf.h
+++ b/include/dwarf.h
@@ -380,6 +380,9 @@
 #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)
@@ -409,6 +412,8 @@
 				      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-common.h b/include/libunwind-common.h
index 7274308..d90d97c 100644
--- a/include/libunwind-common.h
+++ b/include/libunwind-common.h
@@ -228,6 +228,7 @@
   {
     unw_word_t start;
     unw_word_t end;
+    unw_word_t load_base;
     char *path;
     int flags;
   }
diff --git a/include/map_info.h b/include/map_info.h
index 9e18c01..4b53c32 100644
--- a/include/map_info.h
+++ b/include/map_info.h
@@ -33,6 +33,7 @@
     uintptr_t start;
     uintptr_t end;
     uintptr_t offset;
+    uintptr_t load_base;
     int flags;
     char *path;
 
diff --git a/src/dwarf/Gfind_unwind_table.c b/src/dwarf/Gfind_unwind_table.c
index 63d2388..e3b33b0 100644
--- a/src/dwarf/Gfind_unwind_table.c
+++ b/src/dwarf/Gfind_unwind_table.c
@@ -35,6 +35,38 @@
 
 /* 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;
+  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;
+        }
+    }
+
+  if (!ptxt)
+    return 0;
+
+  *load_base = ptxt->p_vaddr;
+  return 1;
+}
+/* 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)
diff --git a/src/mi/Lmap.c b/src/mi/Lmap.c
index ba93ab9..d204aaa 100644
--- a/src/mi/Lmap.c
+++ b/src/mi/Lmap.c
@@ -121,6 +121,7 @@
     {
       unw_map->start = map_info->start;
       unw_map->end = map_info->end;
+      unw_map->load_base = map_info->load_base;
       unw_map->flags = map_info->flags;
       if (map_info->path)
         unw_map->path = strdup (map_info->path);
diff --git a/src/mi/map.c b/src/mi/map.c
index 7204b97..e2de31e 100644
--- a/src/mi/map.c
+++ b/src/mi/map.c
@@ -75,6 +75,7 @@
 
   unw_map->start = map_info->start;
   unw_map->end = map_info->end;
+  unw_map->load_base = map_info->load_base;
   unw_map->flags = map_info->flags;
   unw_map->path = map_info->path;
 
diff --git a/src/os-linux.c b/src/os-linux.c
index b052e06..1c24333 100644
--- a/src/os-linux.c
+++ b/src/os-linux.c
@@ -51,6 +51,7 @@
       cur_map->start = start;
       cur_map->end = end;
       cur_map->offset = offset;
+      cur_map->load_base = 0;
       cur_map->flags = flags;
       cur_map->path = strdup (mi.path);
       mutex_init (&cur_map->ei_lock);
@@ -67,6 +68,21 @@
           && strncmp ("ashmem/", cur_map->path + 5, 7) != 0)
         cur_map->flags |= MAP_FLAGS_DEVICE_MEM;
 
+      /* If this is a readable executable map, find the load_base. */
+      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)
+                cur_map->load_base = load_base;
+            }
+          else
+            cur_map->ei.image = NULL;
+        }
+
       map_list = cur_map;
     }