1
0
Fork 0
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:
Ken Werner 2011-03-23 15:54:59 +00:00 committed by Arun Sharma
parent 04fc88fa31
commit cf8d5e41af
5 changed files with 138 additions and 181 deletions

View file

@ -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

View file

@ -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;
};

View file

@ -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);
}

View file

@ -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. */

View file

@ -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;
}