diff --git a/include/tdep-arm/ex_tables.h b/include/tdep-arm/ex_tables.h index 3805c608..7369b35c 100644 --- a/include/tdep-arm/ex_tables.h +++ b/include/tdep-arm/ex_tables.h @@ -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 diff --git a/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h index 839415cc..acaf6d71 100644 --- a/include/tdep-arm/libunwind_i.h +++ b/include/tdep-arm/libunwind_i.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; }; diff --git a/src/arm/Ginit_local.c b/src/arm/Ginit_local.c index 2a0d73a7..cdf05d2b 100644 --- a/src/arm/Ginit_local.c +++ b/src/arm/Ginit_local.c @@ -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); } diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c index ab556d78..1b4b135f 100644 --- a/src/arm/Gstep.c +++ b/src/arm/Gstep.c @@ -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. */ diff --git a/src/arm/ex_tables.c b/src/arm/ex_tables.c index 3b5e8ef1..6fe29474 100644 --- a/src/arm/ex_tables.c +++ b/src/arm/ex_tables.c @@ -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]; + int ret = 0; + unsigned i; - 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; -} - -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; }