mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2025-02-16 10:11:41 +01:00
Implement DWARF DW_CFA_val_expression for x86_64
Ubuntu's libc-bin (2.15-0ubuntu20.2) on x86_64 uses DW_CFA_val_expression in describing the pthread spinlock operations __lll_unlock_wake() and __lll_lock_wait(). libunwind 1.1 doesn't understand that opcode and so backtraces from those operations are truncated. This changeset adds basic support for it, by adding a new type to dwarf_loc_t that describes the register's actual contents rather than its location. I've only implemented the new type for x86_64, and stubbed it out for all other architectures -- it looks like a lot of that code is duplicated so oughtn't to be that hard, but I don't have test cases for them. Tested that DW_CFA_val_expression works on x86_64 (by using https://code.google.com/p/gperftools/ on a lock-heavy program). Build-tested on x86, x86_64 and arm. The unit tests don't pass for me on any of those archs, but this cset doesn't break anything that was passing before. Signed-off-by: Tim Deegan <tjd@phlegethon.org>
This commit is contained in:
parent
11a7d98e39
commit
4eb880e1b5
4 changed files with 44 additions and 2 deletions
|
@ -165,6 +165,7 @@ typedef enum
|
||||||
DW_CFA_offset_extended_sf = 0x11,
|
DW_CFA_offset_extended_sf = 0x11,
|
||||||
DW_CFA_def_cfa_sf = 0x12,
|
DW_CFA_def_cfa_sf = 0x12,
|
||||||
DW_CFA_def_cfa_offset_sf = 0x13,
|
DW_CFA_def_cfa_offset_sf = 0x13,
|
||||||
|
DW_CFA_val_expression = 0x16,
|
||||||
DW_CFA_lo_user = 0x1c,
|
DW_CFA_lo_user = 0x1c,
|
||||||
DW_CFA_MIPS_advance_loc8 = 0x1d,
|
DW_CFA_MIPS_advance_loc8 = 0x1d,
|
||||||
DW_CFA_GNU_window_save = 0x2d,
|
DW_CFA_GNU_window_save = 0x2d,
|
||||||
|
@ -224,6 +225,7 @@ typedef enum
|
||||||
DWARF_WHERE_CFAREL, /* register saved at CFA-relative address */
|
DWARF_WHERE_CFAREL, /* register saved at CFA-relative address */
|
||||||
DWARF_WHERE_REG, /* register saved in another register */
|
DWARF_WHERE_REG, /* register saved in another register */
|
||||||
DWARF_WHERE_EXPR, /* register saved */
|
DWARF_WHERE_EXPR, /* register saved */
|
||||||
|
DWARF_WHERE_VAL_EXPR, /* register has computed value */
|
||||||
}
|
}
|
||||||
dwarf_where_t;
|
dwarf_where_t;
|
||||||
|
|
||||||
|
|
|
@ -351,6 +351,11 @@ static inline void invalidate_edi (struct elf_dyn_info *edi)
|
||||||
# define tdep_get_func_addr(as,addr,v) (*(v) = addr, 0)
|
# define tdep_get_func_addr(as,addr,v) (*(v) = addr, 0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef DWARF_VAL_LOC
|
||||||
|
# define DWARF_IS_VAL_LOC(l) 0
|
||||||
|
# define DWARF_VAL_LOC(c,v) DWARF_NULL_LOC
|
||||||
|
#endif
|
||||||
|
|
||||||
#define UNW_ALIGN(x,a) (((x)+(a)-1UL)&~((a)-1UL))
|
#define UNW_ALIGN(x,a) (((x)+(a)-1UL)&~((a)-1UL))
|
||||||
|
|
||||||
#endif /* libunwind_i_h */
|
#endif /* libunwind_i_h */
|
||||||
|
|
|
@ -109,25 +109,33 @@ dwarf_get_uc(const struct dwarf_cursor *cursor)
|
||||||
# define DWARF_IS_NULL_LOC(l) (DWARF_GET_LOC (l) == 0)
|
# define DWARF_IS_NULL_LOC(l) (DWARF_GET_LOC (l) == 0)
|
||||||
# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r) })
|
# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r) })
|
||||||
# define DWARF_IS_REG_LOC(l) 0
|
# define DWARF_IS_REG_LOC(l) 0
|
||||||
|
# define DWARF_IS_MEM_LOC(l) 1
|
||||||
|
# define DWARF_IS_VAL_LOC(l) 0
|
||||||
# define DWARF_REG_LOC(c,r) (DWARF_LOC((unw_word_t) \
|
# define DWARF_REG_LOC(c,r) (DWARF_LOC((unw_word_t) \
|
||||||
x86_64_r_uc_addr(dwarf_get_uc(c), (r)), 0))
|
x86_64_r_uc_addr(dwarf_get_uc(c), (r)), 0))
|
||||||
# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0)
|
# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0)
|
||||||
# define DWARF_FPREG_LOC(c,r) (DWARF_LOC((unw_word_t) \
|
# define DWARF_FPREG_LOC(c,r) (DWARF_LOC((unw_word_t) \
|
||||||
x86_64_r_uc_addr(dwarf_get_uc(c), (r)), 0))
|
x86_64_r_uc_addr(dwarf_get_uc(c), (r)), 0))
|
||||||
|
# define DWARF_VAL_LOC(c,v) DWARF_NULL_LOC
|
||||||
|
|
||||||
#else /* !UNW_LOCAL_ONLY */
|
#else /* !UNW_LOCAL_ONLY */
|
||||||
|
|
||||||
# define DWARF_LOC_TYPE_FP (1 << 0)
|
# define DWARF_LOC_TYPE_FP (1 << 0)
|
||||||
# define DWARF_LOC_TYPE_REG (1 << 1)
|
# define DWARF_LOC_TYPE_REG (1 << 1)
|
||||||
|
# define DWARF_LOC_TYPE_VAL (1 << 2)
|
||||||
# define DWARF_NULL_LOC DWARF_LOC (0, 0)
|
# define DWARF_NULL_LOC DWARF_LOC (0, 0)
|
||||||
# define DWARF_IS_NULL_LOC(l) \
|
# define DWARF_IS_NULL_LOC(l) \
|
||||||
({ dwarf_loc_t _l = (l); _l.val == 0 && _l.type == 0; })
|
({ dwarf_loc_t _l = (l); _l.val == 0 && _l.type == 0; })
|
||||||
# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r), .type = (t) })
|
# define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r), .type = (t) })
|
||||||
# define DWARF_IS_REG_LOC(l) (((l).type & DWARF_LOC_TYPE_REG) != 0)
|
# define DWARF_IS_REG_LOC(l) (((l).type & DWARF_LOC_TYPE_REG) != 0)
|
||||||
# define DWARF_IS_FP_LOC(l) (((l).type & DWARF_LOC_TYPE_FP) != 0)
|
# define DWARF_IS_FP_LOC(l) (((l).type & DWARF_LOC_TYPE_FP) != 0)
|
||||||
|
# define DWARF_IS_MEM_LOC(l) ((l).type == 0)
|
||||||
|
# define DWARF_IS_VAL_LOC(l) (((l).type & DWARF_LOC_TYPE_VAL) != 0)
|
||||||
# define DWARF_REG_LOC(c,r) DWARF_LOC((r), DWARF_LOC_TYPE_REG)
|
# define DWARF_REG_LOC(c,r) DWARF_LOC((r), DWARF_LOC_TYPE_REG)
|
||||||
# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0)
|
# define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0)
|
||||||
# define DWARF_FPREG_LOC(c,r) DWARF_LOC((r), (DWARF_LOC_TYPE_REG \
|
# define DWARF_FPREG_LOC(c,r) DWARF_LOC((r), (DWARF_LOC_TYPE_REG \
|
||||||
| DWARF_LOC_TYPE_FP))
|
| DWARF_LOC_TYPE_FP))
|
||||||
|
# define DWARF_VAL_LOC(c,v) DWARF_LOC ((v), DWARF_LOC_TYPE_VAL)
|
||||||
|
|
||||||
#endif /* !UNW_LOCAL_ONLY */
|
#endif /* !UNW_LOCAL_ONLY */
|
||||||
|
|
||||||
|
@ -158,14 +166,19 @@ dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val)
|
||||||
if (DWARF_IS_REG_LOC (loc))
|
if (DWARF_IS_REG_LOC (loc))
|
||||||
return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), val,
|
return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), val,
|
||||||
0, c->as_arg);
|
0, c->as_arg);
|
||||||
else
|
if (DWARF_IS_MEM_LOC (loc))
|
||||||
return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val,
|
return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val,
|
||||||
0, c->as_arg);
|
0, c->as_arg);
|
||||||
|
assert(DWARF_IS_VAL_LOC (loc));
|
||||||
|
*val = DWARF_GET_LOC (loc);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
|
dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
|
||||||
{
|
{
|
||||||
|
assert(!DWARF_IS_VAL_LOC (loc));
|
||||||
|
|
||||||
if (DWARF_IS_NULL_LOC (loc))
|
if (DWARF_IS_NULL_LOC (loc))
|
||||||
return -UNW_EBADREG;
|
return -UNW_EBADREG;
|
||||||
|
|
||||||
|
|
|
@ -334,7 +334,22 @@ run_cfi_program (struct dwarf_cursor *c, dwarf_state_record_t *sr,
|
||||||
*addr += len;
|
*addr += len;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DW_CFA_GNU_args_size:
|
case DW_CFA_val_expression:
|
||||||
|
if ((ret = read_regnum (as, a, addr, ®num, arg)) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* Save the address of the DW_FORM_block for later evaluation. */
|
||||||
|
set_reg (sr, regnum, DWARF_WHERE_VAL_EXPR, *addr);
|
||||||
|
|
||||||
|
if ((ret = dwarf_read_uleb128 (as, a, addr, &len, arg)) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
Debug (15, "CFA_val_expression r%lu @ 0x%lx [%lu bytes]\n",
|
||||||
|
(long) regnum, (long) addr, (long) len);
|
||||||
|
*addr += len;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DW_CFA_GNU_args_size:
|
||||||
if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
|
if ((ret = dwarf_read_uleb128 (as, a, addr, &val, arg)) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
sr->args_size = val;
|
sr->args_size = val;
|
||||||
|
@ -785,6 +800,13 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs)
|
||||||
if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg)) < 0)
|
if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DWARF_WHERE_VAL_EXPR:
|
||||||
|
addr = rs->reg[i].val;
|
||||||
|
if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg)) < 0)
|
||||||
|
return ret;
|
||||||
|
c->loc[i] = DWARF_VAL_LOC (c, DWARF_GET_LOC (c->loc[i]));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue