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 <[email protected]> --- include/dwarf.h | 2 ++ include/libunwind_i.h | 5 +++++ include/tdep-x86_64/libunwind_i.h | 18 ++++++++++++++++-- src/dwarf/Gparser.c | 29 ++++++++++++++++++++++++++--- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/include/dwarf.h b/include/dwarf.h index 7800567..fba91f9 100644 --- a/include/dwarf.h +++ b/include/dwarf.h @@ -165,6 +165,7 @@ typedef enum DW_CFA_offset_extended_sf = 0x11, DW_CFA_def_cfa_sf = 0x12, DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_expression = 0x16, DW_CFA_lo_user = 0x1c, DW_CFA_MIPS_advance_loc8 = 0x1d, DW_CFA_GNU_window_save = 0x2d, @@ -224,6 +225,7 @@ typedef enum DWARF_WHERE_CFAREL, /* register saved at CFA-relative address */ DWARF_WHERE_REG, /* register saved in another register */ DWARF_WHERE_EXPR, /* register saved */ + DWARF_WHERE_EXPR_VAL, /* register has computed value */ } dwarf_where_t; diff --git a/include/libunwind_i.h b/include/libunwind_i.h index 0be551f..892e39c 100644 --- a/include/libunwind_i.h +++ b/include/libunwind_i.h @@ -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) #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)) #endif /* libunwind_i_h */ diff --git a/include/tdep-x86_64/libunwind_i.h b/include/tdep-x86_64/libunwind_i.h index 8c9cd05..23da05d 100644 --- a/include/tdep-x86_64/libunwind_i.h +++ b/include/tdep-x86_64/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_LOC(r, t) ((dwarf_loc_t) { .val = (r) }) # 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) \ x86_64_r_uc_addr(dwarf_get_uc(c), (r)), 0)) # define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0) # define DWARF_FPREG_LOC(c,r) (DWARF_LOC((unw_word_t) \ x86_64_r_uc_addr(dwarf_get_uc(c), (r)), 0)) +# define DWARF_VAL_LOC(c,v) DWARF_NULL_LOC + #else /* !UNW_LOCAL_ONLY */ # define DWARF_LOC_TYPE_FP (1 << 0) # define DWARF_LOC_TYPE_REG (1 << 1) +# define DWARF_LOC_TYPE_VAL (1 << 2) # define DWARF_NULL_LOC DWARF_LOC (0, 0) # define DWARF_IS_NULL_LOC(l) \ ({ 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_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_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_MEM_LOC(c,m) DWARF_LOC ((m), 0) # define DWARF_FPREG_LOC(c,r) DWARF_LOC((r), (DWARF_LOC_TYPE_REG \ | DWARF_LOC_TYPE_FP)) +# define DWARF_VAL_LOC(c,v) DWARF_LOC ((v), DWARF_LOC_TYPE_VAL) #endif /* !UNW_LOCAL_ONLY */ @@ -158,9 +166,12 @@ dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val) if (DWARF_IS_REG_LOC (loc)) return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), val, 0, c->as_arg); - else + if (DWARF_IS_MEM_LOC (loc)) return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val, 0, c->as_arg); + assert(DWARF_IS_VAL_LOC (loc)); + *val = DWARF_GET_LOC (loc); + return 0; } static inline int @@ -172,9 +183,12 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) if (DWARF_IS_REG_LOC (loc)) return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), &val, 1, c->as_arg); - else + if (DWARF_IS_MEM_LOC (loc)) return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), &val, 1, c->as_arg); + assert(DWARF_IS_VAL_LOC (loc)); + loc = DWARF_VAL_LOC(c, val); + return 0; } #define tdep_getcontext_trace UNW_ARCH_OBJ(getcontext_trace) diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c index b251e31..82a6129 100644 --- a/src/dwarf/Gparser.c +++ b/src/dwarf/Gparser.c @@ -334,6 +334,21 @@ run_cfi_program (struct dwarf_cursor *c, dwarf_state_record_t *sr, *addr += len; break; + 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_EXPR_VAL, *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) goto fail; @@ -685,7 +700,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, unw_accessors_t *a, unw_word_t addr, - dwarf_loc_t *locp, void *arg) + dwarf_loc_t *locp, void *arg, int is_val) { int ret, is_register; unw_word_t len, val; @@ -700,6 +715,8 @@ eval_location_expr (struct dwarf_cursor *c, unw_addr_space_t as, if (is_register) *locp = DWARF_REG_LOC (c, dwarf_to_unw_regnum (val)); + else if (is_val) + *locp = DWARF_VAL_LOC (c, val); else *locp = DWARF_MEM_LOC (c, val); @@ -753,7 +770,7 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs) assert (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_EXPR); addr = rs->reg[DWARF_CFA_REG_COLUMN].val; - if ((ret = eval_location_expr (c, as, a, addr, &cfa_loc, arg)) < 0) + if ((ret = eval_location_expr (c, as, a, addr, &cfa_loc, arg, 0)) < 0) return ret; /* the returned location better be a memory location... */ if (DWARF_IS_REG_LOC (cfa_loc)) @@ -782,7 +799,13 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs) case DWARF_WHERE_EXPR: addr = rs->reg[i].val; - 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)) < 0) + return ret; + break; + + case DWARF_WHERE_EXPR_VAL: + addr = rs->reg[i].val; + if ((ret = eval_location_expr (c, as, a, addr, c->loc + i, arg, 1)) < 0) return ret; break; } -- 1.8.3.2 _______________________________________________ Libunwind-devel mailing list [email protected] https://lists.nongnu.org/mailman/listinfo/libunwind-devel
