diff mbox

[3/5] Rework the lookup of the ARM specific unwind entry for local unwinding

Message ID 1312373525-20741-4-git-send-email-ken.werner@linaro.org
State Superseded
Headers show

Commit Message

Ken Werner Aug. 3, 2011, 12:12 p.m. UTC
Use the dh_iterate_phdr callback in Gfind_proc_info-lsb.c to find the ARM
specific unwind tables rather than doing it on our own at the unw_init_local.

Signed-off-by: Ken Werner <ken.werner@linaro.org>
---
 include/libunwind-arm.h         |   13 ++--
 include/libunwind-dynamic.h     |    3 +-
 include/tdep-arm/ex_tables.h    |   10 ---
 include/tdep-arm/libunwind_i.h  |    6 +-
 src/arm/Gex_tables.c            |  148 ++++++++++++---------------------------
 src/arm/Ginit_local.c           |    3 -
 src/arm/Gstep.c                 |   10 +--
 src/dwarf/Gfind_proc_info-lsb.c |   41 ++++++++++-
 src/dwarf/Gparser.c             |    5 ++
 9 files changed, 102 insertions(+), 137 deletions(-)
diff mbox

Patch

diff --git a/include/libunwind-arm.h b/include/libunwind-arm.h
index 3e7ebf3..3338b42 100644
--- a/include/libunwind-arm.h
+++ b/include/libunwind-arm.h
@@ -285,14 +285,6 @@  struct arm_exidx_entry {
   uint32_t data;
 };
 
-struct arm_exidx_table {
-  const char *name;
-  struct arm_exidx_entry *start;
-  struct arm_exidx_entry *end;
-  unw_word_t start_addr;
-  unw_word_t end_addr;
-};
-
 typedef struct
   {
     /* no arm-specific auxiliary proc-info */
@@ -304,6 +296,11 @@  unw_tdep_proc_info_t;
 #define unw_tdep_is_fpreg		UNW_ARCH_OBJ(is_fpreg)
 extern int unw_tdep_is_fpreg (int);
 
+#define unw_search_arm_unwind_table    UNW_OBJ(search_unwind_table)
+extern int unw_search_arm_unwind_table (unw_addr_space_t, unw_word_t,
+					unw_dyn_info_t *, unw_proc_info_t *,
+					int, void *);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
diff --git a/include/libunwind-dynamic.h b/include/libunwind-dynamic.h
index 6c94f05..584f392 100644
--- a/include/libunwind-dynamic.h
+++ b/include/libunwind-dynamic.h
@@ -75,7 +75,8 @@  typedef enum
   {
     UNW_INFO_FORMAT_DYNAMIC,		/* unw_dyn_proc_info_t */
     UNW_INFO_FORMAT_TABLE,		/* unw_dyn_table_t */
-    UNW_INFO_FORMAT_REMOTE_TABLE	/* unw_dyn_remote_table_t */
+    UNW_INFO_FORMAT_REMOTE_TABLE,	/* unw_dyn_remote_table_t */
+    UNW_INFO_FORMAT_ARM_EXIDX		/* ARM specific unwind info */
   }
 unw_dyn_info_format_t;
 
diff --git a/include/tdep-arm/ex_tables.h b/include/tdep-arm/ex_tables.h
index b5e6dcf..6ea3414 100644
--- a/include/tdep-arm/ex_tables.h
+++ b/include/tdep-arm/ex_tables.h
@@ -44,20 +44,10 @@  struct arm_exbuf_data
   uint32_t data;
 };
 
-#define arm_exidx_init_local	UNW_OBJ(arm_exidx_init_local)
-#define arm_exidx_table_add	UNW_OBJ(arm_exidx_table_add)
-#define arm_exidx_table_find	UNW_OBJ(arm_exidx_table_find)
-#define arm_exidx_table_lookup	UNW_OBJ(arm_exidx_table_lookup)
 #define arm_exidx_extract	UNW_OBJ(arm_exidx_extract)
 #define arm_exidx_decode	UNW_OBJ(arm_exidx_decode)
 #define arm_exidx_apply_cmd	UNW_OBJ(arm_exidx_apply_cmd)
 
-int arm_exidx_init_local (void);
-int arm_exidx_table_add (const char *name, struct arm_exidx_entry *start,
-			 struct arm_exidx_entry *end);
-struct arm_exidx_table *arm_exidx_table_find (unw_word_t pc);
-struct arm_exidx_entry *arm_exidx_table_lookup (struct arm_exidx_table *table,
-						unw_word_t pc);
 int arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf);
 int arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c);
 int arm_exidx_apply_cmd (struct arm_exbuf_data *edata, struct dwarf_cursor *c);
diff --git a/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h
index 508f6c8..205f605 100644
--- a/include/tdep-arm/libunwind_i.h
+++ b/include/tdep-arm/libunwind_i.h
@@ -231,7 +231,7 @@  dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
 #define tdep_init			UNW_OBJ(init)
 /* Platforms that support UNW_INFO_FORMAT_TABLE need to define
    tdep_search_unwind_table.  */
-#define tdep_search_unwind_table	dwarf_search_unwind_table
+#define tdep_search_unwind_table	unw_search_arm_unwind_table
 #define tdep_uc_addr			UNW_ARCH_OBJ(uc_addr)
 #define tdep_get_elf_image		UNW_ARCH_OBJ(get_elf_image)
 #define tdep_access_reg			UNW_OBJ(access_reg)
@@ -264,9 +264,9 @@  dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
 extern int tdep_needs_initialization;
 
 extern void tdep_init (void);
-extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
+/*extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
 				     unw_dyn_info_t *di, unw_proc_info_t *pi,
-				     int need_unwind_info, void *arg);
+				     int need_unwind_info, void *arg);*/
 extern void *tdep_uc_addr (ucontext_t *uc, int reg);
 extern int tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip,
 			       unsigned long *segbase, unsigned long *mapoff,
diff --git a/src/arm/Gex_tables.c b/src/arm/Gex_tables.c
index b72dabb..c90f675 100644
--- a/src/arm/Gex_tables.c
+++ b/src/arm/Gex_tables.c
@@ -33,22 +33,11 @@  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 
 #define ARM_EXTBL_OP_FINISH	0xb0
 
-#define ARM_EXIDX_TABLE_LIMIT	32
-
-#define arm_exidx_tables		UNW_OBJ(arm_exidx_tables)
-#define arm_exidx_table_count		UNW_OBJ(arm_exidx_table_count)
-#define arm_exidx_table_find		UNW_OBJ(arm_exidx_table_find)
-#define arm_exidx_table_reset_all	UNW_OBJ(arm_exidx_table_reset_all)
-#define arm_exidx_init_local_cb		UNW_OBJ(arm_exidx_init_local_cb)
-
 enum arm_exbuf_cmd_flags {
   ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
   ARM_EXIDX_VFP_DOUBLE = 1 << 17,
 };
 
-static struct arm_exidx_table arm_exidx_tables[ARM_EXIDX_TABLE_LIMIT];
-static unsigned arm_exidx_table_count = 0;
-
 static inline uint32_t
 prel31_read (uint32_t prel31)
 {
@@ -62,68 +51,6 @@  prel31_to_addr (uint32_t *addr)
   return (uint32_t)addr + offset;
 }
 
-static void
-arm_exidx_table_reset_all (void)
-{
-  arm_exidx_table_count = 0;
-}
-
-HIDDEN int
-arm_exidx_table_add (const char *name,
-    struct arm_exidx_entry *start, struct arm_exidx_entry *end)
-{
-  if (arm_exidx_table_count >= ARM_EXIDX_TABLE_LIMIT)
-    return -1;
-  struct arm_exidx_table *table = &arm_exidx_tables[arm_exidx_table_count++];
-  table->name = name;
-  table->start = start;
-  table->end = end;
-  table->start_addr = prel31_to_addr (&start->addr);
-  table->end_addr = prel31_to_addr (&(end - 1)->addr);
-  Debug (2, "name=%s, range=%p-%p, addr=%p-%p\n",
-      name, start, end, (void *)table->start_addr, (void *)table->end_addr);
-  return 0;
-}
-
-/**
- * Locate the appropriate unwind table from the given PC.
- */
-HIDDEN struct arm_exidx_table *
-arm_exidx_table_find (unw_word_t pc)
-{
-  struct arm_exidx_table *table;
-  unsigned i;
-  for (i = 0; i < arm_exidx_table_count; i++)
-  {
-    table = &arm_exidx_tables[i];
-    if (pc >= table->start_addr && pc < table->end_addr)
-      return table;
-  }
-  return NULL;
-}
-
-/**
- * Finds the corresponding arm_exidx_entry from a given index table and PC.
- */
-HIDDEN struct arm_exidx_entry *
-arm_exidx_table_lookup (struct arm_exidx_table *table, unw_word_t pc)
-{
-  struct arm_exidx_entry *first = table->start, *last = table->end - 1;
-  if (pc < prel31_to_addr (&first->addr))
-    return NULL;
-  else if (pc >= prel31_to_addr (&last->addr))
-    return last;
-  while (first < last - 1)
-    {
-      struct arm_exidx_entry *mid = first + ((last - first + 1) >> 1);
-      if (pc < prel31_to_addr (&mid->addr))
-	last = mid;
-      else
-	first = mid;
-    }
-  return first;
-}
-
 /**
  * Applies the given command onto the new state to the given dwarf_cursor.
  */
@@ -401,39 +328,56 @@  arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf)
   return nbuf;
 }
 
-/**
- * Callback to dl_iterate_phdr to find the unwind tables.
- * If found, calls arm_exidx_table_add to remember it for later lookups.
- */
-static int
-arm_exidx_init_local_cb (struct dl_phdr_info *info, size_t size, void *data)
+PROTECTED int
+unw_search_arm_unwind_table (unw_addr_space_t as, unw_word_t ip,
+			     unw_dyn_info_t *di, unw_proc_info_t *pi,
+			     int need_unwind_info, void *arg)
 {
-  unsigned i;
-
-  for (i = 0; i < info->dlpi_phnum; i++)
+  if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)
+      && di->format == UNW_INFO_FORMAT_ARM_EXIDX)
     {
-      const ElfW (Phdr) *phdr = info->dlpi_phdr + i;
-      if (phdr->p_type != PT_ARM_EXIDX)
-	continue;
+      struct arm_exidx_entry *first =
+	(struct arm_exidx_entry *) di->u.rti.table_data;
+      struct arm_exidx_entry *last =
+	((struct arm_exidx_entry *) (di->u.rti.table_data +
+				     di->u.rti.table_len)) - 1;
+      struct arm_exidx_entry *entry;
+
+      if (ip < prel31_to_addr (&first->addr))
+	return -UNW_ENOINFO;
+      else if (ip >= prel31_to_addr (&last->addr))
+	{
+	   entry = last;
+	   pi->start_ip = prel31_to_addr (&last->addr);
+	   pi->end_ip = di->end_ip - 1;
+	}
+      else
+	{
+	  while (first < last - 1)
+	    {
+	      entry = first + ((last - first + 1) >> 1);
+	      if (ip < prel31_to_addr (&entry->addr))
+		last = entry;
+	      else
+		first = entry;
+	    }
 
-      ElfW (Addr) addr = info->dlpi_addr + phdr->p_vaddr;
-      ElfW (Word) size = phdr->p_filesz;
+	  entry = first;
+	  pi->start_ip = prel31_to_addr (&entry->addr);
+	  pi->end_ip = prel31_to_addr (&(entry + 1)->addr) - 1;
+	}
 
-      arm_exidx_table_add (info->dlpi_name,
-	  (struct arm_exidx_entry *)addr,
-	  (struct arm_exidx_entry *)(addr + size));
-      break;
+      if (need_unwind_info)
+	{
+	  pi->unwind_info_size = 8;
+	  pi->unwind_info = entry;
+	  pi->format = UNW_INFO_FORMAT_ARM_EXIDX;
+	}
+      return 0;
     }
-  return 0;
-}
+  else if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF
+	   && di->format != UNW_INFO_FORMAT_ARM_EXIDX))
+    return dwarf_search_unwind_table (as, ip, di, pi, need_unwind_info, arg);
 
-/**
- * Traverse the program headers of the executable and its loaded
- * shared objects to collect the unwind tables.
- */
-HIDDEN int
-arm_exidx_init_local (void)
-{
-  arm_exidx_table_reset_all();
-  return dl_iterate_phdr (&arm_exidx_init_local_cb, NULL);
+  return -UNW_ENOINFO; 
 }
diff --git a/src/arm/Ginit_local.c b/src/arm/Ginit_local.c
index f8ee19c..b0f73cd 100644
--- a/src/arm/Ginit_local.c
+++ b/src/arm/Ginit_local.c
@@ -49,9 +49,6 @@  unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
   c->dwarf.as = unw_local_addr_space;
   c->dwarf.as_arg = uc;
 
-  if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
-    arm_exidx_init_local ();
-
   return common_init (c, 1);
 }
 
diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c
index cfe01c0..60cbf2c 100644
--- a/src/arm/Gstep.c
+++ b/src/arm/Gstep.c
@@ -35,7 +35,6 @@  static inline int
 arm_exidx_step (struct cursor *c)
 {
   unw_word_t old_ip, old_cfa;
-  struct arm_exidx_table *table;
   struct arm_exidx_entry *entry;
   uint8_t buf[32];
   int ret;
@@ -46,14 +45,13 @@  arm_exidx_step (struct cursor *c)
   /* mark PC unsaved */
   c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC;
 
-  table = arm_exidx_table_find (c->dwarf.ip);
-  if (NULL == table)
-    return -UNW_ENOINFO;
+  if ((ret = tdep_find_proc_info (&c->dwarf, c->dwarf.ip, 1)) < 0)
+     return -UNW_ENOINFO;
 
-  entry = arm_exidx_table_lookup (table, c->dwarf.ip);
-  if (NULL == entry)
+  if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX)
     return -UNW_ENOINFO;
 
+  entry = (struct arm_exidx_entry *)c->dwarf.pi.unwind_info;
   ret = arm_exidx_extract (entry, buf);
   if (ret < 0)
     return ret;
diff --git a/src/dwarf/Gfind_proc_info-lsb.c b/src/dwarf/Gfind_proc_info-lsb.c
index d65342c..5c1931d 100644
--- a/src/dwarf/Gfind_proc_info-lsb.c
+++ b/src/dwarf/Gfind_proc_info-lsb.c
@@ -57,6 +57,9 @@  struct callback_data
     int single_fde;		/* did we find a single FDE? (vs. a table) */
     unw_dyn_info_t di;		/* table info (if single_fde is false) */
     unw_dyn_info_t di_debug;	/* additional table info for .debug_frame */
+#if UNW_TARGET_ARM
+    unw_dyn_info_t di_arm;	/* additional table info for .ARM.exidx */
+#endif
   };
 
 static int
@@ -576,6 +579,9 @@  callback (struct dl_phdr_info *info, size_t size, void *ptr)
   struct callback_data *cb_data = ptr;
   unw_dyn_info_t *di = &cb_data->di;
   const Elf_W(Phdr) *phdr, *p_eh_hdr, *p_dynamic, *p_text;
+#if UNW_TARGET_ARM
+  const Elf_W(Phdr) *p_arm_exidx = NULL;
+#endif
   unw_word_t addr, eh_frame_start, eh_frame_end, fde_count, ip;
   Elf_W(Addr) load_base, segbase = 0, max_load_addr = 0;
   int ret, need_unwind_info = cb_data->need_unwind_info;
@@ -619,6 +625,10 @@  callback (struct dl_phdr_info *info, size_t size, void *ptr)
 	p_eh_hdr = phdr;
       else if (phdr->p_type == PT_DYNAMIC)
 	p_dynamic = phdr;
+#if UNW_TARGET_ARM
+      else if (phdr->p_type == PT_ARM_EXIDX)
+	p_arm_exidx = phdr;
+#endif
     }
   
   if (!p_text)
@@ -747,6 +757,19 @@  callback (struct dl_phdr_info *info, size_t size, void *ptr)
   }
 #endif  /* CONFIG_DEBUG_FRAME */
 
+#if UNW_TARGET_ARM
+  if (p_arm_exidx)
+    {
+      cb_data->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
+      cb_data->di_arm.start_ip = p_text->p_vaddr + load_base;
+      cb_data->di_arm.end_ip = cb_data->di_arm.start_ip + p_text->p_memsz;
+      cb_data->di_arm.u.rti.name_ptr = (unw_word_t) info->dlpi_name;
+      cb_data->di_arm.u.rti.table_data = p_arm_exidx->p_vaddr + load_base;
+      cb_data->di_arm.u.rti.table_len = p_arm_exidx->p_memsz;
+      found = 1;
+    }
+#endif
+
   return found;
 }
 
@@ -766,6 +789,9 @@  dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip,
   cb_data.need_unwind_info = need_unwind_info;
   cb_data.di.format = -1;
   cb_data.di_debug.format = -1;
+#if UNW_TARGET_ARM
+  cb_data.di_arm.format = -1;
+#endif
 
   SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
   ret = dl_iterate_phdr (callback, &cb_data);
@@ -783,14 +809,21 @@  dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip,
 
   /* search the table: */
   if (cb_data.di.format != -1)
-    ret = dwarf_search_unwind_table (as, ip, &cb_data.di,
-				      pi, need_unwind_info, arg);
+    ret = tdep_search_unwind_table (as, ip, &cb_data.di,
+				    pi, need_unwind_info, arg);
   else
     ret = -UNW_ENOINFO;
 
+#if UNW_TARGET_ARM
+  if (ret == -UNW_ENOINFO && cb_data.di_arm.format != -1)
+    ret = tdep_search_unwind_table (as, ip, &cb_data.di_arm, pi,
+				    need_unwind_info, arg);
+#endif
+
   if (ret == -UNW_ENOINFO && cb_data.di_debug.format != -1)
-    ret = dwarf_search_unwind_table (as, ip, &cb_data.di_debug, pi,
-				     need_unwind_info, arg);
+    ret = tdep_search_unwind_table (as, ip, &cb_data.di_debug, pi,
+				    need_unwind_info, arg);
+
   return ret;
 }
 
diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c
index 1db1546..e82df6b 100644
--- a/src/dwarf/Gparser.c
+++ b/src/dwarf/Gparser.c
@@ -423,6 +423,11 @@  fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, int need_unwind_info)
 	return ret;
     }
 
+  if (c->pi.format != UNW_INFO_FORMAT_DYNAMIC
+      && c->pi.format != UNW_INFO_FORMAT_TABLE
+      && c->pi.format != UNW_INFO_FORMAT_REMOTE_TABLE)
+    return -UNW_ENOINFO;
+
   c->pi_valid = 1;
   c->pi_is_dynamic = dynamic;