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;
}