@@ -64,16 +64,12 @@ enum arm_exbuf_cmd_flags {
#define ARM_EXBUF_COUNT(x) ((x) & 0x0f)
#define ARM_EXBUF_END(x) (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
-struct arm_exbuf_callback_data {
- uint8_t ops[11];
- uint8_t n_ops;
+struct arm_exbuf_data
+{
arm_exbuf_cmd_t cmd;
uint32_t data;
- void *cb_data;
};
-typedef int (*arm_exbuf_callback_t)(struct arm_exbuf_callback_data *aecb);
-
static inline void *
prel31_to_addr (void *addr)
{
@@ -99,30 +95,7 @@ int arm_exidx_entry_extract (struct elf_image *ei,
int arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf);
int arm_exidx_decode (const uint8_t *buf, uint8_t len,
- arm_exbuf_callback_t cb, void *cb_data);
-
-struct arm_stackframe {
- void *fp;
- void *sp;
- void *lr;
- void *pc;
-};
-
-struct arm_exidx_vrs {
- uint32_t vrs[16];
-};
-
-enum arm_exidx_vrs_regs {
- FP_thumb = 7,
- FP_arm = 11,
- SP = 13,
- LR = 14,
- PC = 15,
-};
-
-void arm_exidx_frame_to_vrs(struct arm_stackframe *f, struct arm_exidx_vrs *s);
-int arm_exidx_vrs_to_frame(struct arm_exidx_vrs *s, struct arm_stackframe *f);
-
-int arm_exidx_vrs_callback (struct arm_exbuf_callback_data *aecd);
+ struct dwarf_cursor *c);
+int arm_exidx_apply_cmd (struct arm_exbuf_data *edata, struct dwarf_cursor *c);
#endif // ARM_EX_TABLES_H
@@ -55,7 +55,6 @@ struct unw_addr_space
struct cursor
{
struct dwarf_cursor dwarf; /* must be first */
- struct arm_stackframe frame;
unw_word_t sigcontext_addr;
};
@@ -39,7 +39,6 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
PROTECTED int
unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
{
- register void *current_sp asm ("sp");
struct cursor *c = (struct cursor *) cursor;
if (tdep_needs_initialization)
@@ -51,18 +50,7 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
c->dwarf.as_arg = uc;
if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
- {
- int arm_exidx_init_done = 0;
- if (!arm_exidx_init_done)
- {
- arm_exidx_init_done = 1;
- arm_exidx_init_local ("libunwind");
- }
- c->frame.fp = __builtin_frame_address (0);
- c->frame.sp = current_sp;
- c->frame.lr = __builtin_return_address (0);
- c->frame.pc = &unw_init_local;
- }
+ arm_exidx_init_local ("libunwind");
return common_init (c, 1);
}
@@ -30,27 +30,42 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
static inline int
arm_exidx_step (struct cursor *c)
{
- struct arm_exidx_table *table = arm_exidx_table_find (c->frame.pc);
+ unw_word_t old_ip, old_cfa;
+ struct arm_exidx_table *table;
+ struct arm_exidx_entry *entry;
+ uint8_t buf[32];
+ int ret;
+
+ old_ip = c->dwarf.ip;
+ old_cfa = c->dwarf.cfa;
+
+ /* mark PC unsaved */
+ c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC;
+
+ table = arm_exidx_table_find ((void *)c->dwarf.ip);
if (NULL == table)
return -UNW_ENOINFO;
- struct arm_exidx_entry *entry = arm_exidx_table_lookup (table, c->frame.pc);
+ entry = arm_exidx_table_lookup (table, (void *)c->dwarf.ip);
if (NULL == entry)
return -UNW_ENOINFO;
- struct arm_exidx_vrs s;
- arm_exidx_frame_to_vrs (&c->frame, &s);
-
- uint8_t buf[32];
- int ret = arm_exidx_extract (entry, buf);
+ ret = arm_exidx_extract (entry, buf);
if (ret < 0)
return ret;
- ret = arm_exidx_decode (buf, ret, &arm_exidx_vrs_callback, &s);
+ ret = arm_exidx_decode (buf, ret, &c->dwarf);
if (ret < 0)
- return -ret;
+ return ret;
- return arm_exidx_vrs_to_frame (&s, &c->frame);
+ if (c->dwarf.ip == old_ip && c->dwarf.cfa == old_cfa)
+ {
+ Dprintf ("%s: ip and cfa unchanged; stopping here (ip=0x%lx)\n",
+ __FUNCTION__, (long) c->dwarf.ip);
+ return -UNW_EBADFRAME;
+ }
+
+ return (c->dwarf.ip == 0) ? 0 : 1;
}
PROTECTED int
@@ -65,9 +80,9 @@ unw_step (unw_cursor_t *cursor)
{
ret = arm_exidx_step (c);
if (ret >= 0)
- return ret;
- if (ret < 0 && ret != -UNW_ENOINFO)
- return ret;
+ return 1;
+ if (ret == -UNW_ESTOPUNWIND)
+ return 0;
}
/* Next, try DWARF-based unwinding. */
@@ -120,164 +120,146 @@ arm_exidx_table_lookup (struct arm_exidx_table *table, void *pc)
return first;
}
-static inline int
-arm_exidx_frame_reg(void *pc)
-{
- return ((unsigned)pc & 1) ? FP_thumb : FP_arm;
-}
-
-HIDDEN void
-arm_exidx_frame_to_vrs(struct arm_stackframe *f, struct arm_exidx_vrs *s)
-{
- int fp_reg = arm_exidx_frame_reg(f->pc);
- s->vrs[fp_reg] = (uint32_t)f->fp;
- s->vrs[SP] = (uint32_t)f->sp;
- s->vrs[LR] = (uint32_t)f->lr;
- s->vrs[PC] = 0;
-}
-
+/**
+ * Applies the given command onto the new state to the given dwarf_cursor.
+ */
HIDDEN int
-arm_exidx_vrs_to_frame(struct arm_exidx_vrs *s, struct arm_stackframe *f)
+arm_exidx_apply_cmd (struct arm_exbuf_data *edata, struct dwarf_cursor *c)
{
- if (s->vrs[PC] == 0)
- s->vrs[PC] = s->vrs[LR];
-
- if (f->pc == (void *)s->vrs[PC])
- return -1;
-
- int fp_reg = arm_exidx_frame_reg(f->pc);
- f->fp = (void *)s->vrs[fp_reg];
- f->sp = (void *)s->vrs[SP];
- f->lr = (void *)s->vrs[LR];
- f->pc = (void *)s->vrs[PC];
-
- return 0;
-}
+ int ret = 0;
+ unsigned i;
-HIDDEN int
-arm_exidx_vrs_callback (struct arm_exbuf_callback_data *aecd)
-{
- struct arm_exidx_vrs *s = aecd->cb_data;
- int ret = 0, i;
- switch (aecd->cmd)
+ switch (edata->cmd)
{
- case ARM_EXIDX_CMD_FINISH:
- break;
- case ARM_EXIDX_CMD_DATA_PUSH:
- Debug (2, "vsp = vsp - %d\n", aecd->data);
- s->vrs[SP] -= aecd->data;
- break;
- case ARM_EXIDX_CMD_DATA_POP:
- Debug (2, "vsp = vsp + %d\n", aecd->data);
- s->vrs[SP] += aecd->data;
- break;
- case ARM_EXIDX_CMD_REG_POP:
- for (i = 0; i < 16; i++)
- if (aecd->data & (1 << i))
- {
- s->vrs[i] = *(uint32_t*)s->vrs[SP];
- s->vrs[SP] += 4;
- Debug (2, "pop {r%d}\n", i);
- }
- break;
- case ARM_EXIDX_CMD_REG_TO_SP:
- assert (aecd->data < 16);
- Debug (2, "vsp = r%d\n", aecd->data);
- s->vrs[SP] = s->vrs[aecd->data];
- break;
- case ARM_EXIDX_CMD_VFP_POP:
- /* Skip VFP registers, but be sure to adjust stack */
- for (i = ARM_EXBUF_START (aecd->data); i < ARM_EXBUF_END (aecd->data); i++)
- s->vrs[SP] += 8;
- if (!(aecd->data & ARM_EXIDX_VFP_DOUBLE))
- s->vrs[SP] += 4;
- break;
- case ARM_EXIDX_CMD_WREG_POP:
- for (i = ARM_EXBUF_START (aecd->data); i < ARM_EXBUF_END (aecd->data); i++)
- s->vrs[SP] += 8;
- break;
- case ARM_EXIDX_CMD_WCGR_POP:
- for (i = 0; i < 4; i++)
- if (aecd->data & (1 << i))
- s->vrs[SP] += 4;
- break;
- case ARM_EXIDX_CMD_REFUSED:
- case ARM_EXIDX_CMD_RESERVED:
- ret = -1;
- break;
+ case ARM_EXIDX_CMD_FINISH:
+ /* Set LR to PC if not set already. */
+ if (DWARF_IS_NULL_LOC (c->loc[UNW_ARM_R15]))
+ c->loc[UNW_ARM_R15] = c->loc[UNW_ARM_R14];
+ /* Set IP. */
+ dwarf_get (c, c->loc[UNW_ARM_R15], &c->ip);
+ break;
+ case ARM_EXIDX_CMD_DATA_PUSH:
+ Debug (2, "vsp = vsp - %d\n", edata->data);
+ c->cfa -= edata->data;
+ break;
+ case ARM_EXIDX_CMD_DATA_POP:
+ Debug (2, "vsp = vsp + %d\n", edata->data);
+ c->cfa += edata->data;
+ break;
+ case ARM_EXIDX_CMD_REG_POP:
+ for (i = 0; i < 16; i++)
+ if (edata->data & (1 << i))
+ {
+ Debug (2, "pop {r%d}\n", i);
+ c->loc[UNW_ARM_R0 + i] = DWARF_LOC (c->cfa, 0);
+ c->cfa += 4;
+ }
+ /* Set cfa in case the SP got popped. */
+ if (edata->data & (1 << 13))
+ dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa);
+ break;
+ case ARM_EXIDX_CMD_REG_TO_SP:
+ assert (edata->data < 16);
+ Debug (2, "vsp = r%d\n", edata->data);
+ c->loc[UNW_ARM_R13] = c->loc[UNW_ARM_R0 + edata->data];
+ dwarf_get (c, c->loc[UNW_ARM_R13], &c->cfa);
+ break;
+ case ARM_EXIDX_CMD_VFP_POP:
+ /* Skip VFP registers, but be sure to adjust stack */
+ for (i = ARM_EXBUF_START (edata->data); i < ARM_EXBUF_END (edata->data);
+ i++)
+ c->cfa += 8;
+ if (!(edata->data & ARM_EXIDX_VFP_DOUBLE))
+ c->cfa += 4;
+ break;
+ case ARM_EXIDX_CMD_WREG_POP:
+ for (i = ARM_EXBUF_START (edata->data); i < ARM_EXBUF_END (edata->data);
+ i++)
+ c->cfa += 8;
+ break;
+ case ARM_EXIDX_CMD_WCGR_POP:
+ for (i = 0; i < 4; i++)
+ if (edata->data & (1 << i))
+ c->cfa += 4;
+ break;
+ case ARM_EXIDX_CMD_REFUSED:
+ case ARM_EXIDX_CMD_RESERVED:
+ ret = -1;
+ break;
}
return ret;
}
+
HIDDEN int
-arm_exidx_decode (const uint8_t *buf, uint8_t len,
- arm_exbuf_callback_t cb, void *cb_data)
+arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c)
{
-#define READ_OP() aecb.ops[aecb.n_ops++] = *buf++
+#define READ_OP() *buf++
const uint8_t *end = buf + len;
int ret;
+ struct arm_exbuf_data edata;
assert(buf != NULL);
- assert(cb != NULL);
+ assert(len > 0);
+
while (buf < end)
{
- struct arm_exbuf_callback_data aecb = { .cb_data = cb_data };
uint8_t op = READ_OP ();
if ((op & 0xc0) == 0x00)
{
- aecb.cmd = ARM_EXIDX_CMD_DATA_POP;
- aecb.data = (((int)op & 0x3f) << 2) + 4;
+ edata.cmd = ARM_EXIDX_CMD_DATA_POP;
+ edata.data = (((int)op & 0x3f) << 2) + 4;
}
else if ((op & 0xc0) == 0x40)
{
- aecb.cmd = ARM_EXIDX_CMD_DATA_PUSH;
- aecb.data = (((int)op & 0x3f) << 2) + 4;
+ edata.cmd = ARM_EXIDX_CMD_DATA_PUSH;
+ edata.data = (((int)op & 0x3f) << 2) + 4;
}
else if ((op & 0xf0) == 0x80)
{
uint8_t op2 = READ_OP ();
if (op == 0x80 && op2 == 0x00)
- aecb.cmd = ARM_EXIDX_CMD_REFUSED;
+ edata.cmd = ARM_EXIDX_CMD_REFUSED;
else
{
- aecb.cmd = ARM_EXIDX_CMD_REG_POP;
- aecb.data = ((op & 0xf) << 8) | op2;
- aecb.data = aecb.data << 4;
+ edata.cmd = ARM_EXIDX_CMD_REG_POP;
+ edata.data = ((op & 0xf) << 8) | op2;
+ edata.data = edata.data << 4;
}
}
else if ((op & 0xf0) == 0x90)
{
if (op == 0x9d || op == 0x9f)
- aecb.cmd = ARM_EXIDX_CMD_RESERVED;
+ edata.cmd = ARM_EXIDX_CMD_RESERVED;
else
{
- aecb.cmd = ARM_EXIDX_CMD_REG_TO_SP;
- aecb.data = op & 0x0f;
+ edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
+ edata.data = op & 0x0f;
}
}
else if ((op & 0xf0) == 0xa0)
{
unsigned end = (op & 0x07);
- aecb.data = (1 << (end + 1)) - 1;
- aecb.data = aecb.data << 4;
+ edata.data = (1 << (end + 1)) - 1;
+ edata.data = edata.data << 4;
if (op & 0x08)
- aecb.data |= 1 << 14;
- aecb.cmd = ARM_EXIDX_CMD_REG_POP;
+ edata.data |= 1 << 14;
+ edata.cmd = ARM_EXIDX_CMD_REG_POP;
}
else if (op == ARM_EXTBL_OP_FINISH)
{
- aecb.cmd = ARM_EXIDX_CMD_FINISH;
+ edata.cmd = ARM_EXIDX_CMD_FINISH;
buf = end;
}
else if (op == 0xb1)
{
uint8_t op2 = READ_OP ();
if (op2 == 0 || (op2 & 0xf0))
- aecb.cmd = ARM_EXIDX_CMD_RESERVED;
+ edata.cmd = ARM_EXIDX_CMD_RESERVED;
else
{
- aecb.cmd = ARM_EXIDX_CMD_REG_POP;
- aecb.data = op2 & 0x0f;
+ edata.cmd = ARM_EXIDX_CMD_REG_POP;
+ edata.data = op2 & 0x0f;
}
}
else if (op == 0xb2)
@@ -291,50 +273,50 @@ arm_exidx_decode (const uint8_t *buf, uint8_t len,
shift += 7;
}
while (byte & 0x80);
- aecb.data = offset * 4 + 0x204;
- aecb.cmd = ARM_EXIDX_CMD_DATA_POP;
+ edata.data = offset * 4 + 0x204;
+ edata.cmd = ARM_EXIDX_CMD_DATA_POP;
}
else if (op == 0xb3 || op == 0xc8 || op == 0xc9)
{
- aecb.cmd = ARM_EXIDX_CMD_VFP_POP;
- aecb.data = READ_OP ();
+ edata.cmd = ARM_EXIDX_CMD_VFP_POP;
+ edata.data = READ_OP ();
if (op == 0xc8)
- aecb.data |= ARM_EXIDX_VFP_SHIFT_16;
+ edata.data |= ARM_EXIDX_VFP_SHIFT_16;
if (op != 0xb3)
- aecb.data |= ARM_EXIDX_VFP_DOUBLE;
+ edata.data |= ARM_EXIDX_VFP_DOUBLE;
}
else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0)
{
- aecb.cmd = ARM_EXIDX_CMD_VFP_POP;
- aecb.data = 0x80 | (op & 0x07);
+ edata.cmd = ARM_EXIDX_CMD_VFP_POP;
+ edata.data = 0x80 | (op & 0x07);
if ((op & 0xf8) == 0xd0)
- aecb.data |= ARM_EXIDX_VFP_DOUBLE;
+ edata.data |= ARM_EXIDX_VFP_DOUBLE;
}
else if (op >= 0xc0 && op <= 0xc5)
{
- aecb.cmd = ARM_EXIDX_CMD_WREG_POP;
- aecb.data = 0xa0 | (op & 0x07);
+ edata.cmd = ARM_EXIDX_CMD_WREG_POP;
+ edata.data = 0xa0 | (op & 0x07);
}
else if (op == 0xc6)
{
- aecb.cmd = ARM_EXIDX_CMD_WREG_POP;
- aecb.data = READ_OP ();
+ edata.cmd = ARM_EXIDX_CMD_WREG_POP;
+ edata.data = READ_OP ();
}
else if (op == 0xc7)
{
uint8_t op2 = READ_OP ();
if (op2 == 0 || (op2 & 0xf0))
- aecb.cmd = ARM_EXIDX_CMD_RESERVED;
+ edata.cmd = ARM_EXIDX_CMD_RESERVED;
else
{
- aecb.cmd = ARM_EXIDX_CMD_WCGR_POP;
- aecb.data = op2 & 0x0f;
+ edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
+ edata.data = op2 & 0x0f;
}
}
else
- aecb.cmd = ARM_EXIDX_CMD_RESERVED;
+ edata.cmd = ARM_EXIDX_CMD_RESERVED;
- ret = (*cb) (&aecb);
+ ret = arm_exidx_apply_cmd (&edata, c);
if (ret < 0)
return ret;
}
This eliminates the arm_stackframe and therefore the need to synchronize the two models. It also clears the way for unwinding call stacks with mixed DWARF- and extbl-frames. Signed-off-by: Ken Werner <ken.werner@linaro.org> --- include/tdep-arm/ex_tables.h | 35 +------ include/tdep-arm/libunwind_i.h | 1 - src/arm/Ginit_local.c | 14 +--- src/arm/Gstep.c | 41 +++++--- src/arm/ex_tables.c | 228 ++++++++++++++++++--------------------- 5 files changed, 138 insertions(+), 181 deletions(-)