mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2024-11-25 16:47:38 +01:00
Have the ARM extbtl-parser operate on the DWARF model directly.
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>
This commit is contained in:
parent
04fc88fa31
commit
cf8d5e41af
5 changed files with 138 additions and 181 deletions
|
@ -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];
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue