diff options
author | Tim Deegan <tjd@phlegethon.org> | 2014-02-03 12:01:10 +0100 |
---|---|---|
committer | Arun Sharma <arun@sharma-home.net> | 2014-02-08 20:19:20 -0800 |
commit | 4eb880e1b57be69b3e136870e25994ccfa8004b5 (patch) | |
tree | b0501ea2740d0ed343dd993889d7fcd5f53f3a01 /src/dwarf | |
parent | 11a7d98e3963d66436e6519202cc2e136e324030 (diff) | |
download | libunwind-4eb880e1b57be69b3e136870e25994ccfa8004b5.tar.gz |
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>
Diffstat (limited to 'src/dwarf')
-rw-r--r-- | src/dwarf/Gparser.c | 24 |
1 files changed, 23 insertions, 1 deletions
diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c index b251e311..fefd8093 100644 --- a/src/dwarf/Gparser.c +++ b/src/dwarf/Gparser.c @@ -334,7 +334,22 @@ run_cfi_program (struct dwarf_cursor *c, dwarf_state_record_t *sr, *addr += len; 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) goto fail; 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) return ret; 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; } } |