diff mbox

[2/4] Rework the lookup of the ARM specific unwind info

Message ID 1313432594-23980-3-git-send-email-ken.werner@linaro.org
State Accepted
Commit 545023c2072975c6b85a09d5faf2cf05db10e064
Headers show

Commit Message

Ken Werner Aug. 15, 2011, 6:23 p.m. UTC
Implement routines for finding the proc_info and searching the unwind table
for the ARM backend.

Signed-off-by: Ken Werner <ken.werner@linaro.org>
---
 include/libunwind-arm.h        |    8 --
 include/libunwind-dynamic.h    |    3 +-
 include/tdep-arm/ex_tables.h   |   10 --
 include/tdep-arm/libunwind_i.h |   13 ++-
 src/arm/Gex_tables.c           |  254 +++++++++++++++++++++++++---------------
 src/arm/Ginit.c                |   10 +--
 src/arm/Ginit_local.c          |    3 -
 src/arm/Gstep.c                |   10 +-
 src/dwarf/Gparser.c            |    5 +
 9 files changed, 183 insertions(+), 133 deletions(-)
diff mbox

Patch

diff --git a/include/libunwind-arm.h b/include/libunwind-arm.h
index 3e7ebf3..211ae50 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 */
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..7a5a394 100644
--- a/include/tdep-arm/libunwind_i.h
+++ b/include/tdep-arm/libunwind_i.h
@@ -229,9 +229,11 @@  dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
 #define tdep_getcontext_trace           unw_getcontext
 #define tdep_needs_initialization	UNW_OBJ(needs_initialization)
 #define tdep_init			UNW_OBJ(init)
+#define arm_find_proc_info		UNW_OBJ(find_proc_info)
+#define arm_put_unwind_info		UNW_OBJ(put_unwind_info)
 /* 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_OBJ(search_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)
@@ -244,10 +246,10 @@  dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
 
 #ifdef UNW_LOCAL_ONLY
 # define tdep_find_proc_info(c,ip,n)				\
-	dwarf_find_proc_info((c)->as, (ip), &(c)->pi, (n),	\
+	arm_find_proc_info((c)->as, (ip), &(c)->pi, (n),	\
 				       (c)->as_arg)
 # define tdep_put_unwind_info(as,pi,arg)		\
-	dwarf_put_unwind_info((as), (pi), (arg))
+	arm_put_unwind_info((as), (pi), (arg))
 #else
 # define tdep_find_proc_info(c,ip,n)					\
 	(*(c)->as->acc.find_proc_info)((c)->as, (ip), &(c)->pi, (n),	\
@@ -264,6 +266,11 @@  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 arm_find_proc_info (unw_addr_space_t as, unw_word_t ip,
+			       unw_proc_info_t *pi, int need_unwind_info,
+			       void *arg);
+extern void arm_put_unwind_info (unw_addr_space_t as,
+				  unw_proc_info_t *pi, void *arg);
 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);
diff --git a/src/arm/Gex_tables.c b/src/arm/Gex_tables.c
index b72dabb..fc18088 100644
--- a/src/arm/Gex_tables.c
+++ b/src/arm/Gex_tables.c
@@ -33,21 +33,19 @@  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;
+struct arm_cb_data
+  {
+    /* in: */
+    unw_word_t ip;             /* instruction-pointer we're looking for */
+    unw_proc_info_t *pi;       /* proc-info pointer */
+    /* out: */
+    unw_dyn_info_t di;         /* info about the ARM exidx segment */
+  };
 
 static inline uint32_t
 prel31_read (uint32_t prel31)
@@ -62,68 +60,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 +337,169 @@  arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf)
   return nbuf;
 }
 
+PROTECTED 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)
+{
+  if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)
+      && di->format == UNW_INFO_FORMAT_ARM_EXIDX)
+    {
+      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;
+	    }
+
+	  entry = first;
+	  pi->start_ip = prel31_to_addr (&entry->addr);
+	  pi->end_ip = prel31_to_addr (&(entry + 1)->addr) - 1;
+	}
+
+      if (need_unwind_info)
+	{
+	  pi->unwind_info_size = 8;
+	  pi->unwind_info = entry;
+	  pi->format = UNW_INFO_FORMAT_ARM_EXIDX;
+	}
+      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);
+
+  return -UNW_ENOINFO; 
+}
+
 /**
- * Callback to dl_iterate_phdr to find the unwind tables.
- * If found, calls arm_exidx_table_add to remember it for later lookups.
+ * Callback to dl_iterate_phdr to find infos about the ARM exidx segment.
  */
 static int
-arm_exidx_init_local_cb (struct dl_phdr_info *info, size_t size, void *data)
+arm_phdr_cb (struct dl_phdr_info *info, size_t size, void *data)
 {
-  unsigned i;
+  struct arm_cb_data *cb_data = data;
+  const Elf_W(Phdr) *p_text = NULL;
+  const Elf_W(Phdr) *p_arm_exidx = NULL;
+  const Elf_W(Phdr) *phdr = info->dlpi_phdr;
+  long n;
 
-  for (i = 0; i < info->dlpi_phnum; i++)
+  for (n = info->dlpi_phnum; --n >= 0; phdr++)
     {
-      const ElfW (Phdr) *phdr = info->dlpi_phdr + i;
-      if (phdr->p_type != PT_ARM_EXIDX)
-	continue;
-
-      ElfW (Addr) addr = info->dlpi_addr + phdr->p_vaddr;
-      ElfW (Word) size = phdr->p_filesz;
+      switch (phdr->p_type)
+	{
+	case PT_LOAD:
+	  if (cb_data->ip >= phdr->p_vaddr + info->dlpi_addr &&
+	      cb_data->ip < phdr->p_vaddr + info->dlpi_addr + phdr->p_memsz)
+	    p_text = phdr;
+	  break;
+
+	case PT_ARM_EXIDX:
+	  p_arm_exidx = phdr;
+	  break;
+
+	default:
+	  break;
+	}
+    }
 
-      arm_exidx_table_add (info->dlpi_name,
-	  (struct arm_exidx_entry *)addr,
-	  (struct arm_exidx_entry *)(addr + size));
-      break;
+  if (p_text && p_arm_exidx)
+    {
+      cb_data->di.format = UNW_INFO_FORMAT_ARM_EXIDX;
+      cb_data->di.start_ip = p_text->p_vaddr + info->dlpi_addr;
+      cb_data->di.end_ip = cb_data->di.start_ip + p_text->p_memsz;
+      cb_data->di.u.rti.name_ptr = (unw_word_t) info->dlpi_name;
+      cb_data->di.u.rti.table_data = p_arm_exidx->p_vaddr + info->dlpi_addr;
+      cb_data->di.u.rti.table_len = p_arm_exidx->p_memsz;
+      return 1;
     }
+
   return 0;
 }
 
-/**
- * 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_find_proc_info (unw_addr_space_t as, unw_word_t ip,
+		    unw_proc_info_t *pi, int need_unwind_info, void *arg)
+{
+  int ret = 0;
+  intrmask_t saved_mask;
+
+  Debug (14, "looking for IP=0x%lx\n", (long) ip);
+
+  if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
+    {
+      struct arm_cb_data cb_data;
+
+      memset (&cb_data, 0, sizeof (cb_data));
+      cb_data.ip = ip;
+      cb_data.pi = pi;
+      cb_data.di.format = -1;
+
+      SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
+      ret = dl_iterate_phdr (arm_phdr_cb, &cb_data);
+      SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
+
+      if (cb_data.di.format != -1)
+	ret = tdep_search_unwind_table (as, ip, &cb_data.di, pi,
+					need_unwind_info, arg);
+      else
+	ret = -UNW_ENOINFO;
+    }
+
+  if (ret < 0 && UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
+    {
+      struct dwarf_callback_data cb_data;
+
+      memset (&cb_data, 0, sizeof (cb_data));
+      cb_data.ip = ip;
+      cb_data.pi = pi;
+      cb_data.need_unwind_info = need_unwind_info;
+      cb_data.di.format = -1;
+      cb_data.di_debug.format = -1;
+
+      SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
+      ret = dl_iterate_phdr (dwarf_callback, &cb_data);
+      SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
+
+      if (cb_data.single_fde)
+	/* already got the result in *pi */
+	return 0;
+
+      if (cb_data.di_debug.format != -1)
+	ret = tdep_search_unwind_table (as, ip, &cb_data.di_debug, pi,
+					need_unwind_info, arg);
+      else
+	ret = -UNW_ENOINFO;
+    }
+
+  if (ret < 0)
+    Debug (14, "IP=0x%lx not found\n", (long) ip);
+
+  return ret;
+}
+
+HIDDEN void
+arm_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
 {
-  arm_exidx_table_reset_all();
-  return dl_iterate_phdr (&arm_exidx_init_local_cb, NULL);
+  /* it's a no-op */
 }
+
diff --git a/src/arm/Ginit.c b/src/arm/Ginit.c
index 5d7dcd6..b1e5228 100644
--- a/src/arm/Ginit.c
+++ b/src/arm/Ginit.c
@@ -85,12 +85,6 @@  HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
        unwind-table entry.  Perhaps something similar can be done with
        DWARF2 unwind info.  */
 
-static void
-put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
-{
-  /* it's a no-op */
-}
-
 static int
 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
 			void *arg)
@@ -193,8 +187,8 @@  arm_local_addr_space_init (void)
 {
   memset (&local_addr_space, 0, sizeof (local_addr_space));
   local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
-  local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
-  local_addr_space.acc.put_unwind_info = put_unwind_info;
+  local_addr_space.acc.find_proc_info = arm_find_proc_info;
+  local_addr_space.acc.put_unwind_info = arm_put_unwind_info;
   local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
   local_addr_space.acc.access_mem = access_mem;
   local_addr_space.acc.access_reg = access_reg;
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/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;