@@ -280,11 +280,6 @@ typedef ucontext_t unw_tdep_context_t;
#include "libunwind-dynamic.h"
-struct arm_exidx_entry {
- uint32_t addr;
- uint32_t data;
-};
-
typedef struct
{
/* no arm-specific auxiliary proc-info */
@@ -48,7 +48,7 @@ struct arm_exbuf_data
#define arm_exidx_decode UNW_OBJ(arm_exidx_decode)
#define arm_exidx_apply_cmd UNW_OBJ(arm_exidx_apply_cmd)
-int arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf);
+int arm_exidx_extract (struct dwarf_cursor *c, 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);
@@ -44,11 +44,19 @@ prel31_read (uint32_t prel31)
return ((int32_t)prel31 << 1) >> 1;
}
-static inline uint32_t
-prel31_to_addr (uint32_t *addr)
+static inline int
+prel31_to_addr (unw_addr_space_t as, void *arg, unw_word_t prel31,
+ unw_word_t *val)
{
- uint32_t offset = ((long)*addr << 1) >> 1;
- return (uint32_t)addr + offset;
+ unw_word_t offset;
+
+ if ((*as->acc.access_mem)(as, prel31, &offset, 0, arg) < 0)
+ return -UNW_EINVAL;
+
+ offset = ((long)offset << 1) >> 1;
+ *val = prel31 + offset;
+
+ return 0;
}
/**
@@ -258,17 +266,31 @@ arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c)
}
/**
- * Reads the given entry and extracts the unwind instructions into buf.
- * Returns the number of the extracted unwind insns or -UNW_ESTOPUNWIND
- * if the special bit pattern ARM_EXIDX_CANT_UNWIND (0x1) was found.
+ * Reads the entry from the given cursor and extracts the unwind instructions
+ * into buf. Returns the number of the extracted unwind insns or
+ * -UNW_ESTOPUNWIND if the special bit pattern ARM_EXIDX_CANT_UNWIND (0x1) was
+ * found.
*/
HIDDEN int
-arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf)
+arm_exidx_extract (struct dwarf_cursor *c, uint8_t *buf)
{
int nbuf = 0;
- unw_word_t addr = prel31_to_addr (&entry->addr);
+ unw_word_t entry = (unw_word_t) c->pi.unwind_info;
+ unw_word_t addr;
+ uint32_t data;
+
+ /* An ARM unwind entry consists of a prel31 offset to the start of a
+ function followed by 31bits of data:
+ * if set to 0x1: the function cannot be unwound (EXIDX_CANTUNWIND)
+ * if bit 31 is one: this is a table entry itself (ARM_EXIDX_COMPACT)
+ * if bit 31 is zero: this is a prel31 offset of the start of the
+ table entry for this function */
+ if (prel31_to_addr(c->as, c->as_arg, entry, &addr) < 0)
+ return -UNW_EINVAL;
+
+ if ((*c->as->acc.access_mem)(c->as, entry + 4, &data, 0, c->as_arg) < 0)
+ return -UNW_EINVAL;
- uint32_t data = entry->data;
if (data == ARM_EXIDX_CANT_UNWIND)
{
Debug (2, "0x1 [can't unwind]\n");
@@ -276,16 +298,23 @@ arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf)
}
else if (data & ARM_EXIDX_COMPACT)
{
- Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr, (data >> 24) & 0x7f, data);
+ Debug (2, "%p compact model %d [%8.8x]\n", (void *)addr,
+ (data >> 24) & 0x7f, data);
buf[nbuf++] = data >> 16;
buf[nbuf++] = data >> 8;
buf[nbuf++] = data;
}
else
{
- uint32_t *extbl_data = (uint32_t *)prel31_to_addr (&entry->data);
- data = (unw_word_t)extbl_data[0];
+ unw_word_t extbl_data;
unsigned int n_table_words = 0;
+
+ if (prel31_to_addr(c->as, c->as_arg, entry + 4, &extbl_data) < 0)
+ return -UNW_EINVAL;
+
+ if ((*c->as->acc.access_mem)(c->as, extbl_data, &data, 0, c->as_arg) < 0)
+ return -UNW_EINVAL;
+
if (data & ARM_EXIDX_COMPACT)
{
int pers = (data >> 24) & 0x0f;
@@ -293,7 +322,7 @@ arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf)
if (pers == 1 || pers == 2)
{
n_table_words = (data >> 16) & 0xff;
- extbl_data += 1;
+ extbl_data += 4;
}
else
buf[nbuf++] = data >> 16;
@@ -302,19 +331,28 @@ arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf)
}
else
{
- unw_word_t pers = prel31_to_addr (extbl_data);
- Debug (2, "%p Personality routine: %8p\n", (void *)addr, (void *)pers);
- n_table_words = extbl_data[1] >> 24;
- buf[nbuf++] = extbl_data[1] >> 16;
- buf[nbuf++] = extbl_data[1] >> 8;
- buf[nbuf++] = extbl_data[1];
- extbl_data += 2;
+ unw_word_t pers;
+ if (prel31_to_addr (c->as, c->as_arg, extbl_data, &pers) < 0)
+ return -UNW_EINVAL;
+ Debug (2, "%p Personality routine: %8p\n", (void *)addr,
+ (void *)pers);
+ if ((*c->as->acc.access_mem)(c->as, extbl_data + 4, &data, 0,
+ c->as_arg) < 0)
+ return -UNW_EINVAL;
+ n_table_words = data >> 24;
+ buf[nbuf++] = data >> 16;
+ buf[nbuf++] = data >> 8;
+ buf[nbuf++] = data;
+ extbl_data += 8;
}
assert (n_table_words <= 5);
unsigned j;
for (j = 0; j < n_table_words; j++)
{
- data = *extbl_data++;
+ if ((*c->as->acc.access_mem)(c->as, extbl_data, &data, 0,
+ c->as_arg) < 0)
+ return -UNW_EINVAL;
+ extbl_data += 4;
buf[nbuf++] = data >> 24;
buf[nbuf++] = data >> 16;
buf[nbuf++] = data >> 8;
@@ -336,41 +374,59 @@ unw_search_arm_unwind_table (unw_addr_space_t as, unw_word_t ip,
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))
+ /* The .ARM.exidx section contains a sorted list of key-value pairs -
+ the unwind entries. The 'key' is a prel31 offset to the start of a
+ function. We binary search this section in order to find the
+ appropriate unwind entry. */
+ unw_word_t first = di->u.rti.table_data;
+ unw_word_t last = di->u.rti.table_data + di->u.rti.table_len - 8;
+ unw_word_t entry, val;
+
+ if (prel31_to_addr (as, arg, first, &val) < 0 && ip < val)
return -UNW_ENOINFO;
- else if (ip >= prel31_to_addr (&last->addr))
+
+ if (prel31_to_addr (as, arg, last, &val) < 0)
+ return -UNW_EINVAL;
+
+ if (ip >= val)
{
- entry = last;
- pi->start_ip = prel31_to_addr (&last->addr);
- pi->end_ip = di->end_ip - 1;
+ entry = last;
+
+ if (prel31_to_addr (as, arg, last, &pi->start_ip) < 0)
+ return -UNW_EINVAL;
+
+ pi->end_ip = di->end_ip -1;
}
else
{
- while (first < last - 1)
+ while (first < last - 8)
{
- entry = first + ((last - first + 1) >> 1);
- if (ip < prel31_to_addr (&entry->addr))
+ entry = first + (((last - first) / 8 + 1) >> 1) * 8;
+
+ if (prel31_to_addr (as, arg, entry, &val) < 0)
+ return -UNW_EINVAL;
+
+ if (ip < val)
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 (prel31_to_addr (as, arg, entry, &pi->start_ip) < 0)
+ return -UNW_EINVAL;
+
+ if (prel31_to_addr (as, arg, entry + 8, &pi->end_ip) < 0)
+ return -UNW_EINVAL;
+
+ pi->end_ip--;
}
if (need_unwind_info)
{
pi->unwind_info_size = 8;
- pi->unwind_info = entry;
+ pi->unwind_info = (void *) entry;
pi->format = UNW_INFO_FORMAT_ARM_EXIDX;
}
return 0;
@@ -35,7 +35,6 @@ static inline int
arm_exidx_step (struct cursor *c)
{
unw_word_t old_ip, old_cfa;
- struct arm_exidx_entry *entry;
uint8_t buf[32];
int ret;
@@ -51,8 +50,7 @@ arm_exidx_step (struct cursor *c)
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);
+ ret = arm_exidx_extract (&c->dwarf, buf);
if (ret < 0)
return ret;
@@ -176,6 +176,9 @@ _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as,
unw_proc_info_t pi;
unw_accessors_t *a;
Elf_W(Ehdr) *ehdr;
+#if UNW_TARGET_ARM
+ const Elf_W(Phdr) *parm_exidx = NULL;
+#endif
int i, ret, found = 0;
/* XXX: Much of this code is Linux/LSB-specific. */
@@ -205,6 +208,12 @@ _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as,
pdyn = phdr + i;
break;
+#if UNW_TARGET_ARM
+ case PT_ARM_EXIDX:
+ parm_exidx = phdr + i;
+ break;
+#endif
+
default:
break;
}
@@ -316,6 +325,20 @@ _UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as,
found = 1;
}
+#if UNW_TARGET_ARM
+ if (parm_exidx)
+ {
+ ui->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
+ ui->di_arm.start_ip = segbase;
+ ui->di_arm.end_ip = ui->di_arm.start_ip + ptxt->p_memsz;
+ ui->di_arm.u.rti.name_ptr = (unw_word_t) path;
+ ui->di_arm.u.rti.table_data = parm_exidx->p_vaddr + segbase -
+ ptxt->p_vaddr;
+ ui->di_arm.u.rti.table_len = parm_exidx->p_memsz;
+ found = 1;
+ }
+#endif
+
#ifdef CONFIG_DEBUG_FRAME
{
/* Try .debug_frame. */
@@ -362,6 +385,10 @@ get_unwind_info (struct UPT_info *ui, unw_addr_space_t as, unw_word_t ip)
if ((ui->di_cache.format != -1
&& ip >= ui->di_cache.start_ip && ip < ui->di_cache.end_ip)
+#if UNW_TARGET_ARM
+ || (ui->di_debug.format != -1
+ && ip >= ui->di_arm.start_ip && ip < ui->di_arm.end_ip)
+#endif
|| (ui->di_debug.format != -1
&& ip >= ui->di_debug.start_ip && ip < ui->di_debug.end_ip))
return 0;
@@ -377,6 +404,10 @@ get_unwind_info (struct UPT_info *ui, unw_addr_space_t as, unw_word_t ip)
ui->di_debug.start_ip = ui->di_debug.end_ip = 0;
ui->di_cache.format = -1;
ui->di_debug.format = -1;
+#if UNW_TARGET_ARM
+ ui->di_arm.start_ip = ui->di_arm.end_ip = 0;
+ ui->di_arm.format = -1;
+#endif
}
if (tdep_get_elf_image (&ui->ei, ui->pid, ip, &segbase, &mapoff, path,
@@ -400,7 +431,11 @@ get_unwind_info (struct UPT_info *ui, unw_addr_space_t as, unw_word_t ip)
&& (ip < ui->di_debug.start_ip || ip >= ui->di_debug.end_ip))
ui->di_debug.format = -1;
- if (ui->di_cache.format == -1 && ui->di_debug.format == -1)
+ if (ui->di_cache.format == -1
+#if UNW_TARGET_ARM
+ && ui->di_arm.format == -1
+#endif
+ && ui->di_debug.format == -1)
return -UNW_ENOINFO;
return 0;
@@ -444,10 +479,16 @@ _UPT_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
}
#endif
- if (ret == -UNW_ENOINFO && ui->di_cache.format == -1)
+ if (ret == -UNW_ENOINFO && ui->di_cache.format != -1)
ret = tdep_search_unwind_table (as, ip, &ui->di_cache,
pi, need_unwind_info, arg);
+#if UNW_TARGET_ARM
+ if (ret == -UNW_ENOINFO && ui->di_arm.format != -1)
+ ret = tdep_search_unwind_table (as, ip, &ui->di_arm, pi,
+ need_unwind_info, arg);
+#endif
+
if (ret == -UNW_ENOINFO && ui->di_debug.format != -1)
ret = tdep_search_unwind_table (as, ip, &ui->di_debug, pi,
need_unwind_info, arg);
@@ -57,6 +57,9 @@ struct UPT_info
#if UNW_TARGET_IA64
unw_dyn_info_t ktab;
#endif
+#if UNW_TARGET_ARM
+ unw_dyn_info_t di_arm; /* additional table info for .ARM.exidx */
+#endif
};
extern int _UPT_reg_offset[UNW_REG_LAST + 1];
Change _UPTi_find_unwind_table to also look for the ARM specific unwind information. Adjust the ARM unwind code to read memory using the accessor routines. Signed-off-by: Ken Werner <ken.werner@linaro.org> --- include/libunwind-arm.h | 5 -- include/tdep-arm/ex_tables.h | 2 +- src/arm/Gex_tables.c | 136 +++++++++++++++++++++++++++----------- src/arm/Gstep.c | 4 +- src/ptrace/_UPT_find_proc_info.c | 45 ++++++++++++- src/ptrace/_UPT_internal.h | 3 + 6 files changed, 144 insertions(+), 51 deletions(-)