summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStephen Roberts <43952910+stephen-roberts-work@users.noreply.github.com>2018-11-13 16:57:42 +0000
committerDave Watson <davejwatson@fb.com>2018-11-13 08:57:42 -0800
commit748a2df11f0d10bd39fd5291d2b27b61392732da (patch)
tree6ad18852707f713cae689cd1734a7759863cdc92 /src
parentf551e16213c52169af8bda554e4051b756a169cc (diff)
downloadlibunwind-748a2df11f0d10bd39fd5291d2b27b61392732da.tar.gz
dwarf: Push correct CFA onto stack for dwarf expression evaluation. (#93)
dwarf: Push correct CFA onto stack for dwarf expression evaluation. This change fixes a bug where stale CFAs were pushed onto the dwarf expression stack before expression evaluation. Some optimising compilers emit CFI which relies on this being correct.
Diffstat (limited to 'src')
-rw-r--r--src/dwarf/Gexpr.c14
-rw-r--r--src/dwarf/Gparser.c17
2 files changed, 21 insertions, 10 deletions
diff --git a/src/dwarf/Gexpr.c b/src/dwarf/Gexpr.c
index 709c0c8f..2af45433 100644
--- a/src/dwarf/Gexpr.c
+++ b/src/dwarf/Gexpr.c
@@ -237,8 +237,8 @@ dwarf_stack_aligned(struct dwarf_cursor *c, unw_word_t cfa_addr,
}
HIDDEN int
-dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t *addr, unw_word_t len,
- unw_word_t *valp, int *is_register)
+dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t stack_val, unw_word_t *addr,
+ unw_word_t len, unw_word_t *valp, int *is_register)
{
unw_word_t operand1 = 0, operand2 = 0, tmp1, tmp2 = 0, tmp3, end_addr;
uint8_t opcode, operands_signature, u8;
@@ -287,10 +287,14 @@ do { \
end_addr = *addr + len;
*is_register = 0;
- Debug (14, "len=%lu, pushing cfa=0x%lx\n",
- (unsigned long) len, (unsigned long) c->cfa);
+ Debug (14, "len=%lu, pushing initial value=0x%lx\n",
+ (unsigned long) len, (unsigned long) stack_val);
- push (c->cfa); /* push current CFA as required by DWARF spec */
+ /* The DWARF standard requires the current CFA to be pushed onto the stack */
+ /* before evaluating DW_CFA_expression and DW_CFA_val_expression programs. */
+ /* DW_CFA_def_cfa_expressions do not take an initial value, but we push on */
+ /* a dummy value to keep this logic consistent. */
+ push (stack_val);
while (*addr < end_addr)
{
diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c
index 1ce862c2..fe7c5817 100644
--- a/src/dwarf/Gparser.c
+++ b/src/dwarf/Gparser.c
@@ -734,7 +734,7 @@ create_state_record_for (struct dwarf_cursor *c, dwarf_state_record_t *sr,
}
static inline int
-eval_location_expr (struct dwarf_cursor *c, unw_addr_space_t as,
+eval_location_expr (struct dwarf_cursor *c, unw_word_t stack_val, unw_addr_space_t as,
unw_accessors_t *a, unw_word_t addr,
dwarf_loc_t *locp, void *arg)
{
@@ -746,7 +746,7 @@ eval_location_expr (struct dwarf_cursor *c, unw_addr_space_t as,
return ret;
/* evaluate the expression: */
- if ((ret = dwarf_eval_expr (c, &addr, len, &val, &is_register)) < 0)
+ if ((ret = dwarf_eval_expr (c, stack_val, &addr, len, &val, &is_register)) < 0)
return ret;
if (is_register)
@@ -804,7 +804,10 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs)
assert (rs->reg.where[DWARF_CFA_REG_COLUMN] == DWARF_WHERE_EXPR);
addr = rs->reg.val[DWARF_CFA_REG_COLUMN];
- if ((ret = eval_location_expr (c, as, a, addr, &cfa_loc, arg)) < 0)
+ /* The dwarf standard doesn't specify an initial value to be pushed on */
+ /* the stack before DW_CFA_def_cfa_expression evaluation. We push on a */
+ /* dummy value (0) to keep the eval_location_expr function consistent. */
+ if ((ret = eval_location_expr (c, 0, as, a, addr, &cfa_loc, arg)) < 0)
return ret;
/* the returned location better be a memory location... */
if (DWARF_IS_REG_LOC (cfa_loc))
@@ -844,13 +847,17 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs)
case DWARF_WHERE_EXPR:
addr = rs->reg.val[i];
- if ((ret = eval_location_expr (c, as, a, addr, new_loc + i, arg)) < 0)
+ /* The dwarf standard requires the current CFA to be pushed on the */
+ /* stack before DW_CFA_expression evaluation. */
+ if ((ret = eval_location_expr (c, cfa, as, a, addr, new_loc + i, arg)) < 0)
return ret;
break;
case DWARF_WHERE_VAL_EXPR:
addr = rs->reg.val[i];
- if ((ret = eval_location_expr (c, as, a, addr, new_loc + i, arg)) < 0)
+ /* The dwarf standard requires the current CFA to be pushed on the */
+ /* stack before DW_CFA_val_expression evaluation. */
+ if ((ret = eval_location_expr (c, cfa, as, a, addr, new_loc + i, arg)) < 0)
return ret;
new_loc[i] = DWARF_VAL_LOC (c, DWARF_GET_LOC (new_loc[i]));
break;