libunwind-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Libunwind-devel] [PATCH] Implement DWARF DW_CFA_val_expression for x86_


From: Tim Deegan
Subject: [Libunwind-devel] [PATCH] Implement DWARF DW_CFA_val_expression for x86_64
Date: Mon, 20 Jan 2014 12:41:29 +0100
User-agent: Mutt/1.5.22 (2013-10-16)

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 <address@hidden>
---
 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, &regnum, 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]