]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add limited support for DWARF3 'expressions' in stack-unwind (CFI)
authorJulian Seward <jseward@acm.org>
Tue, 27 Feb 2007 16:52:23 +0000 (16:52 +0000)
committerJulian Seward <jseward@acm.org>
Tue, 27 Feb 2007 16:52:23 +0000 (16:52 +0000)
info (DW_CFA_def_cfa_expression, DW_CFA_expression,
DW_CFA_val_expression).  Mechanism to support all of these is in place
although only DW_CFA_val_expression is currently connected up.

This is really nasty.  The basic idea is to partially evaluate each
expression at the debuginfo-reading time by running it on a stack
machine in which each stack element is an expression tree.  If the
expression can be 'run' successfully, the tree (dag, really) remaining
at the top of the stack is massaged and put into the DiCfSI record for
that address range.  At unwind time the tree is evaluated if needed.
Such cases are in fact extremely rare and so the vast majority of
unwindings use the same mechanism as before.

As a result of all this:

* some obscure cases in glibc-2.5's libpthread.so unwind when they
  didn't before

* --debug-dump=frames produces identical output to that of readelf
  for libc-2.5.so and associated libpthread.so

* All the action centers around the new type CfiExpr, which is a
  union expression-tree type in the same style as IRExpr et al

* Many dark corners of the CFI reader have been looked at and
  (re-)validated

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@6620

coregrind/m_debuginfo/debuginfo.c
coregrind/m_debuginfo/priv_storage.h
coregrind/m_debuginfo/readdwarf.c
coregrind/m_debuginfo/readelf.c
coregrind/m_debuginfo/readstabs.c
coregrind/m_debuginfo/storage.c

index 1ae8c11f665d6044dd43c86acdf9e2b3a3f04da1..2c05f154e55f86798ae9a5f4126bfdc251dabd3e 100644 (file)
@@ -47,6 +47,7 @@
 #include "pub_core_redir.h"       // VG_(redir_notify_{new,delete}_SegInfo)
 #include "pub_core_aspacemgr.h"
 #include "pub_core_machine.h"     // VG_PLAT_USES_PPCTOC
+#include "pub_core_xarray.h"
 #include "priv_storage.h"
 #include "priv_readdwarf.h"
 #include "priv_readstabs.h"
@@ -141,7 +142,6 @@ SegInfo* alloc_SegInfo(Addr start, SizeT size, OffT foffset,
       si->ddump_frames = VG_(clo_debug_dump_frames);
    }
 
-
    return si;
 }
 
@@ -151,10 +151,11 @@ static void free_SegInfo ( SegInfo* si )
 {
    struct strchunk *chunk, *next;
    vg_assert(si != NULL);
-   if (si->filename) VG_(arena_free)(VG_AR_SYMTAB, si->filename);
-   if (si->symtab)   VG_(arena_free)(VG_AR_SYMTAB, si->symtab);
-   if (si->loctab)   VG_(arena_free)(VG_AR_SYMTAB, si->loctab);
-   if (si->cfsi)     VG_(arena_free)(VG_AR_SYMTAB, si->cfsi);
+   if (si->filename)   VG_(arena_free)(VG_AR_SYMTAB, si->filename);
+   if (si->symtab)     VG_(arena_free)(VG_AR_SYMTAB, si->symtab);
+   if (si->loctab)     VG_(arena_free)(VG_AR_SYMTAB, si->loctab);
+   if (si->cfsi)       VG_(arena_free)(VG_AR_SYMTAB, si->cfsi);
+   if (si->cfsi_exprs) VG_(deleteXA)(si->cfsi_exprs);
 
    for (chunk = si->strchunks; chunk != NULL; chunk = next) {
       next = chunk->next;
@@ -971,6 +972,72 @@ Char* VG_(describe_IP)(Addr eip, Char* buf, Int n_buf)
 #  undef BUF_LEN
 }
 
+
+/*------------------------------------------------------------*/
+/*--- For unwinding the stack using                       --- */
+/*--- pre-summarised DWARF3 .eh_frame info                 ---*/
+/*------------------------------------------------------------*/
+
+/* Gather up all the constant pieces of info needed to evaluate
+   a CfiExpr into one convenient struct. */
+typedef
+   struct {
+      Addr ipHere;
+      Addr spHere;
+      Addr fpHere;
+      Addr min_accessible;
+      Addr max_accessible;
+   }
+   CfiExprEvalContext;
+
+/* Evaluate the CfiExpr rooted at ix in exprs given the context eec.
+   *ok is set to False on failure, but not to True on success.  The
+   caller must set it to True before calling. */
+static 
+UWord evalCfiExpr ( XArray* exprs, Int ix, 
+                    CfiExprEvalContext* eec, Bool* ok )
+{
+   UWord wL, wR;
+   CfiExpr* e = VG_(indexXA)( exprs, ix );
+   switch (e->tag) {
+      case Cex_Binop:
+         wL = evalCfiExpr( exprs, e->Cex.Binop.ixL, eec, ok );
+         if (!(*ok)) return 0;
+         wR = evalCfiExpr( exprs, e->Cex.Binop.ixR, eec, ok );
+         if (!(*ok)) return 0;
+         switch (e->Cex.Binop.op) {
+            case Cop_Add: return wL + wR;
+            case Cop_Sub: return wL - wR;
+            default: goto unhandled;
+         }
+         /*NOTREACHED*/
+      case Cex_CfiReg:
+         switch (e->Cex.CfiReg.reg) {
+            case Creg_IP: return (Addr)eec->ipHere;
+            case Creg_SP: return (Addr)eec->spHere;
+            case Creg_FP: return (Addr)eec->fpHere;
+            default: goto unhandled;
+         }
+         /*NOTREACHED*/
+      case Cex_Const:
+         return e->Cex.Const.con;
+      default: 
+         goto unhandled;
+   }
+   /*NOTREACHED*/
+  unhandled:
+   VG_(printf)("\n\nevalCfiExpr: unhandled\n");
+   ML_(ppCfiExpr)( exprs, ix );
+   VG_(printf)("\n");
+   vg_assert(0);
+   /*NOTREACHED*/
+   return 0;
+}
+
+
+/* The main function for DWARF2/3 CFI-based stack unwinding.
+   Given an IP/SP/FP triple, produce the IP/SP/FP values for the
+   previous frame, if possible. */
 /* Returns True if OK.  If not OK, *{ip,sp,fp}P are not changed. */
 /* NOTE: this function may rearrange the order of entries in the
    SegInfo list. */
@@ -980,11 +1047,14 @@ Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP,
                         Addr min_accessible,
                         Addr max_accessible )
 {
+   Bool     ok;
    Int      i;
    SegInfo* si;
    DiCfSI*  cfsi = NULL;
    Addr     cfa, ipHere, spHere, fpHere, ipPrev, spPrev, fpPrev;
 
+   CfiExprEvalContext eec;
+
    static UInt n_search = 0;
    static UInt n_steps = 0;
    n_search++;
@@ -1016,7 +1086,7 @@ Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP,
       VG_(printf)("%u %u\n", n_search, n_steps);
 
    /* Start of performance-enhancing hack: once every 16 (chosen
-      hackily after profiling) successful searchs, move the found
+      hackily after profiling) successful searches, move the found
       SegInfo one step closer to the start of the list.  This makes
       future searches cheaper.  For starting konqueror on amd64, this
       in fact reduces the total amount of searching done by the above
@@ -1048,7 +1118,7 @@ Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP,
 
    if (0) {
       VG_(printf)("found cfisi: "); 
-      ML_(ppDiCfSI)(cfsi);
+      ML_(ppDiCfSI)(si->cfsi_exprs, cfsi);
    }
 
    ipPrev = spPrev = fpPrev = 0;
@@ -1057,7 +1127,24 @@ Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP,
    spHere = *spP;
    fpHere = *fpP;
 
-   cfa = cfsi->cfa_off + (cfsi->cfa_sprel ? spHere : fpHere);
+   /* First compute the CFA. */
+   cfa = 0;
+   switch (cfsi->cfa_how) {
+      case CFIC_SPREL: 
+         cfa = cfsi->cfa_off + spHere;
+         break;
+      case CFIC_FPREL: 
+         cfa = cfsi->cfa_off + fpHere;
+         break;
+      case CFIC_EXPR: 
+         vg_assert(0);
+         break;
+      default: 
+         vg_assert(0);
+   }
+
+   /* Now we know the CFA, use it to roll back the registers we're
+      interested in. */
 
 #  define COMPUTE(_prev, _here, _how, _off)             \
       do {                                              \
@@ -1077,6 +1164,20 @@ Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP,
             case CFIR_CFAREL:                           \
                _prev = cfa + (Word)_off;                \
                break;                                   \
+            case CFIR_EXPR:                             \
+               if (0)                                   \
+                  ML_(ppCfiExpr)(si->cfsi_exprs,_off);  \
+               eec.ipHere = ipHere;                     \
+               eec.spHere = spHere;                     \
+               eec.fpHere = fpHere;                     \
+               eec.min_accessible = min_accessible;     \
+               eec.max_accessible = max_accessible;     \
+               ok = True;                               \
+               _prev = evalCfiExpr(si->cfsi_exprs, _off, &eec, &ok ); \
+               if (!ok) return False;                   \
+               break;                                   \
+            default:                                    \
+               vg_assert(0);                            \
          }                                              \
       } while (0)
 
index dcd488784facba6b32a9a4ec4abcdb0666e3106b..69a2e83f1ce74dba1d3934cadbb7e0e626751ff5 100644 (file)
@@ -37,6 +37,8 @@
 /* See comment at top of debuginfo.c for explanation of
    the _svma / _avma / _image / _bias naming scheme.
 */
+/* Note this is not freestanding; needs pub_core_xarray.h to be
+   included before it. */
 
 #ifndef __PRIV_STORAGE_H
 #define __PRIV_STORAGE_H
@@ -97,7 +99,10 @@ typedef
 
    First off, calculate CFA, the Canonical Frame Address, thusly:
 
-     cfa = if cfa_sprel then sp+cfa_off else fp+cfa_off
+     cfa = case cfa_how of
+              CFIC_SPREL -> sp + cfa_off
+              CFIC_FPREL -> fp + cfa_off
+              CFIR_EXPR  -> expr whose index is in cfa_off
 
    Once that is done, the previous frame's sp/fp values and this
    frame's ra value can be calculated like this:
@@ -108,21 +113,27 @@ typedef
               CFIR_SAME      -> same as it was before (sp/fp only)
               CFIR_CFAREL    -> cfa + sp/fp/ra_off
               CFIR_MEMCFAREL -> *( cfa + sp/fp/ra_off )
+              CFIR_EXPR      -> expr whose index is in sp/fp/ra_off
 */
 
-#define CFIR_UNKNOWN   ((UChar)0)
-#define CFIR_SAME      ((UChar)1)
-#define CFIR_CFAREL    ((UChar)2)
-#define CFIR_MEMCFAREL ((UChar)3)
+#define CFIC_SPREL     ((UChar)1)
+#define CFIC_FPREL     ((UChar)2)
+#define CFIC_EXPR      ((UChar)3)
+
+#define CFIR_UNKNOWN   ((UChar)4)
+#define CFIR_SAME      ((UChar)5)
+#define CFIR_CFAREL    ((UChar)6)
+#define CFIR_MEMCFAREL ((UChar)7)
+#define CFIR_EXPR      ((UChar)8)
 
 typedef
    struct {
       Addr  base;
       UInt  len;
-      Bool  cfa_sprel;
-      UChar ra_how; /* a CFIR_ value */
-      UChar sp_how; /* a CFIR_ value */
-      UChar fp_how; /* a CFIR_ value */
+      UChar cfa_how; /* a CFIC_ value */
+      UChar ra_how;  /* a CFIR_ value */
+      UChar sp_how;  /* a CFIR_ value */
+      UChar fp_how;  /* a CFIR_ value */
       Int   cfa_off;
       Int   ra_off;
       Int   sp_off;
@@ -130,6 +141,71 @@ typedef
    }
    DiCfSI;
 
+
+typedef
+   enum {
+      Cop_Add=0x321,
+      Cop_Sub,
+      Cop_And
+   }
+   CfiOp;
+
+typedef
+   enum {
+      Creg_SP=0x213,
+      Creg_FP,
+      Creg_IP
+   }
+   CfiReg;
+
+typedef
+   enum {
+      Cex_Undef=0x123,
+      Cex_Deref,
+      Cex_Const,
+      Cex_Binop,
+      Cex_CfiReg,
+      Cex_DwReg
+   }
+   CfiExprTag;
+
+typedef 
+   struct {
+      CfiExprTag tag;
+      union {
+         struct {
+         } Undef;
+         struct {
+            Int ixAddr;
+         } Deref;
+         struct {
+            UWord con;
+         } Const;
+         struct {
+            CfiOp op;
+            Int ixL;
+            Int ixR;
+         } Binop;
+         struct {
+            CfiReg reg;
+         } CfiReg;
+         struct {
+            Int reg;
+         } DwReg;
+      }
+      Cex;
+   }
+   CfiExpr;
+
+extern Int ML_(CfiExpr_Undef) ( XArray* dst );
+extern Int ML_(CfiExpr_Deref) ( XArray* dst, Int ixAddr );
+extern Int ML_(CfiExpr_Const) ( XArray* dst, UWord con );
+extern Int ML_(CfiExpr_Binop) ( XArray* dst, CfiOp op, Int ixL, Int ixR );
+extern Int ML_(CfiExpr_CfiReg)( XArray* dst, CfiReg reg );
+extern Int ML_(CfiExpr_DwReg) ( XArray* dst, Int reg );
+
+extern void ML_(ppCfiExpr)( XArray* src, Int ix );
+
 /* --------------------- SEGINFO --------------------- */
 
 /* This is the top-level data type.  It's a structure which contains
@@ -159,12 +235,15 @@ struct _SegInfo {
    UInt    loctab_size;
    /* An expandable array of CFI summary info records.  Also includes
       summary address bounds, showing the min and max address covered
-      by any of the records, as an aid to fast searching. */
+      by any of the records, as an aid to fast searching.  And, if the
+      records require any expression nodes, they are stored in
+      cfsi_exprs. */
    DiCfSI* cfsi;
    UInt    cfsi_used;
    UInt    cfsi_size;
    Addr    cfsi_minaddr;
    Addr    cfsi_maxaddr;
+   XArray* cfsi_exprs; /* XArray of CfSiExpr */
 
    /* Expandable arrays of characters -- the string table.  Pointers
       into this are stable (the arrays are not reallocated). */
@@ -256,7 +335,7 @@ extern void ML_(symerr) ( HChar* msg );
 extern void ML_(ppSym) ( Int idx, DiSym* sym );
 
 /* Print a call-frame-info summary. */
-extern void ML_(ppDiCfSI) ( DiCfSI* si );
+extern void ML_(ppDiCfSI) ( XArray* /* of CfiExpr */ exprs, DiCfSI* si );
 
 
 #define TRACE_SYMTAB(format, args...) \
index b0605d94c33e375b8179a94f83432dc225212701..1e07e99ca9a4a00998fb77f77796dc9225fff8f5 100644 (file)
@@ -39,6 +39,7 @@
 #include "pub_core_libcprint.h"
 #include "pub_core_mallocfree.h"
 #include "pub_core_options.h"
+#include "pub_core_xarray.h"
 #include "priv_storage.h"
 #include "priv_readdwarf.h"        /* self */
 
@@ -204,6 +205,8 @@ UInt read_leb128 ( UChar* data, Int* length_return, Int sign )
   Int    shift = 0;
   UChar  byte;
 
+  vg_assert(sign == 0 || sign == 1);
+
   do
     {
       byte = * data ++;
@@ -225,7 +228,6 @@ UInt read_leb128 ( UChar* data, Int* length_return, Int sign )
   return result;
 }
 
-
 /* Small helper functions easier to use
  * value is returned and the given pointer is
  * moved past end of leb128 data */
@@ -1807,35 +1809,65 @@ enum dwarf_cfa_secondary_ops
    The result is then summarised into a sequence of CfiSIs, if
    possible.  UnwindContext effectively holds the state of the
    abstract machine whilst it is running.
+
+   The CFA can either be a signed offset from a register,
+   or an expression:
+
+   CFA = cfa_reg + cfa_off   when UnwindContext.cfa_is_regoff==True
+       | [[ cfa_expr_id ]]
+
+   When .cfa_is_regoff == True,  cfa_expr_id must be zero
+   When .cfa_is_regoff == False, cfa_reg must be zero
+                                 and cfa_off must be zero
+
+   RegRule describes, for each register, how to get its
+   value in the previous frame, where 'cfa' denotes the cfa
+   for the frame as a whole:
+
+   RegRule = RR_Undef          -- undefined
+           | RR_Same           -- same as in previous frame
+           | RR_CFAOff    arg  -- is at * ( cfa + arg )
+           | RR_CFAValOff arg  -- is ( cfa + arg )
+           | RR_Reg       arg  -- is in register 'arg' 
+           | RR_Expr      arg  -- is at * [[ arg ]]
+           | RR_ValExpr   arg  -- is [[ arg ]]
+           | RR_Arch           -- dunno
+
+   All expressions are stored in exprs in the containing
+   UnwindContext.  Since the UnwindContext gets reinitialised for each
+   new FDE, summarise_context needs to copy out any expressions it
+   wants to keep into the cfsi_exprs field of the containing SegInfo.
 */
 typedef
    struct {
-      enum { RR_Undef, RR_Same, RR_CFAoff, RR_Reg, RR_Arch, RR_Expr,
-            RR_CFAValoff, RR_ValExpr } tag;
-
-      /* Note, .coff and .reg are never both in use.  Therefore could
-         merge them into one. */
-
-      /* CFA offset if tag==RR_CFAoff */
-      Int coff;
-
-      /* reg, if tag==RR_Reg */
-      Int reg;
+      enum { RR_Undef, RR_Same, RR_CFAOff, RR_CFAValOff, 
+             RR_Reg, RR_Expr, RR_ValExpr, RR_Arch } tag;
+      /* meaning:  int offset for CFAoff/CFAValOff
+                   reg # for Reg
+                   expr index for Expr/ValExpr */
+      Int arg;
    }
    RegRule;
 
-static void ppRegRule ( RegRule* reg )
+static void ppRegRule ( XArray* exprs, RegRule* rrule )
 {
-   switch (reg->tag) {
-      case RR_Undef:  VG_(printf)("u  "); break;
-      case RR_Same:   VG_(printf)("s  "); break;
-      case RR_CFAoff: VG_(printf)("c%d ", reg->coff); break;
-      case RR_CFAValoff: VG_(printf)("v%d ", reg->coff); break;
-      case RR_Reg:    VG_(printf)("r%d ", reg->reg); break;
-      case RR_Arch:   VG_(printf)("a  "); break;
-      case RR_Expr:   VG_(printf)("e  "); break;
-      case RR_ValExpr:   VG_(printf)("ve "); break;
-      default:        VG_(core_panic)("ppRegRule");
+   vg_assert(exprs);
+   switch (rrule->tag) {
+      case RR_Undef:     VG_(printf)("u  "); break;
+      case RR_Same:      VG_(printf)("s  "); break;
+      case RR_CFAOff:    VG_(printf)("c%d ", rrule->arg); break;
+      case RR_CFAValOff: VG_(printf)("v%d ", rrule->arg); break;
+      case RR_Reg:       VG_(printf)("r%d ", rrule->arg); break;
+      case RR_Expr:      VG_(printf)("e{"); 
+                         ML_(ppCfiExpr)( exprs, rrule->arg ); 
+                         VG_(printf)("} "); 
+                         break;
+      case RR_ValExpr:   VG_(printf)("ve{"); 
+                         ML_(ppCfiExpr)( exprs, rrule->arg ); 
+                         VG_(printf)("} "); 
+                         break;
+      case RR_Arch:      VG_(printf)("a  "); break;
+      default:           VG_(core_panic)("ppRegRule");
    }
 }
 
@@ -1843,19 +1875,23 @@ static void ppRegRule ( RegRule* reg )
 typedef
    struct {
       /* Read-only fields (set by the CIE) */
-      Int  code_a_f;
-      Int  data_a_f;
-      Addr initloc;
-      Int  ra_reg;
+      Int     code_a_f;
+      Int     data_a_f;
+      Addr    initloc;
+      Int     ra_reg;
       /* The rest of these fields can be modifed by
          run_CF_instruction. */
       /* The LOC entry */
-      Addr loc;
-      /* The CFA entry.  If -1, means we don't know (Dwarf3 Expression). */
-      Int cfa_reg;
-      Int cfa_offset; /* in bytes */
+      Addr    loc;
+      /* The CFA entry.  This can be either reg+/-offset or an expr. */
+      Bool    cfa_is_regoff; /* True=>is reg+offset; False=>is expr */
+      Int     cfa_reg;
+      Int     cfa_off;  /* in bytes */
+      Int     cfa_expr_ix; /* index into cfa_exprs */
       /* register unwind rules */
       RegRule reg[N_CFI_REGS];
+      /* array of CfiExpr, shared by reg[] and cfa_expr_ix */
+      XArray* exprs;
    }
    UnwindContext;
 
@@ -1863,26 +1899,42 @@ static void ppUnwindContext ( UnwindContext* ctx )
 {
    Int i;
    VG_(printf)("0x%llx: ", (ULong)ctx->loc);
-   VG_(printf)("%d(r%d) ",  ctx->cfa_offset, ctx->cfa_reg);
+   if (ctx->cfa_is_regoff) {
+      VG_(printf)("%d(r%d) ",  ctx->cfa_off, ctx->cfa_reg);
+   } else {
+      vg_assert(ctx->exprs);
+      VG_(printf)("{");
+      ML_(ppCfiExpr)( ctx->exprs, ctx->cfa_expr_ix );
+      VG_(printf)("} ");
+   }
    for (i = 0; i < N_CFI_REGS; i++)
-      ppRegRule(&ctx->reg[i]);
+      ppRegRule(ctx->exprs, &ctx->reg[i]);
    VG_(printf)("\n");
 }
 
+static void* symtab_alloc ( SizeT szB ) {
+   return VG_(arena_malloc)( VG_AR_SYMTAB, szB );
+}
+static void symtab_free ( void* v ) {
+   VG_(arena_free)( VG_AR_SYMTAB, v );
+}
+
 static void initUnwindContext ( /*OUT*/UnwindContext* ctx )
 {
    Int i;
-   ctx->code_a_f   = 0;
-   ctx->data_a_f   = 0;
-   ctx->initloc    = 0;
-   ctx->ra_reg     = RA_REG_DEFAULT;
-   ctx->loc        = 0;
-   ctx->cfa_reg    = 0;
-   ctx->cfa_offset = 0;
+   ctx->code_a_f      = 0;
+   ctx->data_a_f      = 0;
+   ctx->initloc       = 0;
+   ctx->ra_reg        = RA_REG_DEFAULT;
+   ctx->loc           = 0;
+   ctx->cfa_is_regoff = True;
+   ctx->cfa_reg       = 0;
+   ctx->cfa_off       = 0;
+   ctx->cfa_expr_ix   = 0;
+   ctx->exprs         = NULL;
    for (i = 0; i < N_CFI_REGS; i++) {
       ctx->reg[i].tag = RR_Undef;
-      ctx->reg[i].coff = 0;
-      ctx->reg[i].reg = 0;
+      ctx->reg[i].arg = 0;
    }
 }
 
@@ -1903,21 +1955,27 @@ typedef
 
 static void initCfiSI ( DiCfSI* si )
 {
-   si->base      = 0;
-   si->len       = 0;
-   si->cfa_sprel = False;
-   si->ra_how    = 0;
-   si->sp_how    = 0;
-   si->fp_how    = 0;
-   si->cfa_off   = 0;
-   si->ra_off    = 0;
-   si->sp_off    = 0;
-   si->fp_off    = 0;
+   si->base    = 0;
+   si->len     = 0;
+   si->cfa_how = 0;
+   si->ra_how  = 0;
+   si->sp_how  = 0;
+   si->fp_how  = 0;
+   si->cfa_off = 0;
+   si->ra_off  = 0;
+   si->sp_off  = 0;
+   si->fp_off  = 0;
 }
 
 
 /* --------------- Summarisation --------------- */
 
+/* Forward */
+static 
+Int copy_convert_CfiExpr_tree ( XArray*        dst,
+                                UnwindContext* srcuc, 
+                                Int            nd );
+
 /* Summarise ctx into si, if possible.  Returns True if successful.
    This is taken to be just after ctx's loc advances; hence the
    summary is up to but not including the current loc.  This works
@@ -1932,31 +1990,57 @@ static Bool summarise_context( /*OUT*/DiCfSI* si,
    initCfiSI(si);
 
    /* How to generate the CFA */
-   if (ctx->cfa_reg == -1) {
+   if (!ctx->cfa_is_regoff) {
       /* it was set by DW_CFA_def_cfa_expression; we don't know what
          it really is */
       why = 6;
       goto failed;
    } else
-   if (ctx->cfa_reg == SP_REG) {
-      si->cfa_sprel = True;
-      si->cfa_off   = ctx->cfa_offset;
+   if (ctx->cfa_is_regoff && ctx->cfa_reg == SP_REG) {
+      si->cfa_how = CFIC_SPREL;
+      si->cfa_off = ctx->cfa_off;
    } else
-   if (ctx->cfa_reg == FP_REG) {
-      si->cfa_sprel = False;
-      si->cfa_off   = ctx->cfa_offset;
+   if (ctx->cfa_is_regoff && ctx->cfa_reg == FP_REG) {
+      si->cfa_how = CFIC_FPREL;
+      si->cfa_off = ctx->cfa_off;
    } else {
       why = 1;
       goto failed;
    }
 
-#  define SUMMARISE_HOW(_how, _off, _ctxreg)                             \
-   switch (_ctxreg.tag) {                                                \
-      case RR_Undef:  _how = CFIR_UNKNOWN;   _off = 0; break;            \
-      case RR_Same:   _how = CFIR_SAME;      _off = 0; break;            \
-      case RR_CFAoff: _how = CFIR_MEMCFAREL; _off = _ctxreg.coff; break; \
-      case RR_CFAValoff: _how = CFIR_CFAREL; _off = _ctxreg.coff; break; \
-      default:        { why = 2; goto failed; } /* otherwise give up */  \
+#  define SUMMARISE_HOW(_how, _off, _ctxreg)                  \
+   switch (_ctxreg.tag) {                                     \
+      case RR_Undef:                                          \
+         _how = CFIR_UNKNOWN;   _off = 0; break;              \
+      case RR_Same:                                           \
+         _how = CFIR_SAME;      _off = 0; break;              \
+      case RR_CFAOff:                                         \
+         _how = CFIR_MEMCFAREL; _off = _ctxreg.arg; break;    \
+      case RR_CFAValOff:                                      \
+         _how = CFIR_CFAREL;    _off = _ctxreg.arg; break;    \
+      case RR_ValExpr: {                                      \
+         XArray *src, *dst;                                   \
+         Int    conv;                                         \
+         src = ctx->exprs;                                    \
+         dst = seginfo->cfsi_exprs;                           \
+         if (src && (VG_(sizeXA)(src) > 0) && (!dst)) {       \
+            dst = VG_(newXA)( symtab_alloc, symtab_free,      \
+                              sizeof(CfiExpr) );              \
+            vg_assert(dst);                                   \
+            seginfo->cfsi_exprs = dst;                        \
+         }                                                    \
+         conv = copy_convert_CfiExpr_tree                     \
+                       ( dst, ctx, _ctxreg.arg );             \
+         vg_assert(conv >= -1);                               \
+         if (conv == -1) { why = 7; goto failed; }            \
+         _how = CFIR_EXPR;                                    \
+         _off = conv;                                         \
+         if (0 && seginfo->ddump_frames)                      \
+            ML_(ppCfiExpr)(dst, conv);                        \
+         break;                                               \
+      }                                                       \
+      default:                                                \
+         why = 2; goto failed; /* otherwise give up */        \
    }
 
    SUMMARISE_HOW(si->ra_how, si->ra_off, ctx->reg[ctx->ra_reg] );
@@ -2000,24 +2084,77 @@ static Bool summarise_context( /*OUT*/DiCfSI* si,
    return False;
 }
 
+/* Copy the tree rooted at srcuc->exprs node srcix to dstxa, on the
+   way converting any DwReg regs (regs numbered using the Dwarf scheme
+   defined by each architecture's ABI) into CfiRegs, which are
+   platform independent.  If the conversion isn't possible because
+   there is no equivalent register, return -1.  This has the
+   undesirable side effect of de-dagifying the input; oh well. */
+static Int copy_convert_CfiExpr_tree ( XArray*        dstxa,
+                                       UnwindContext* srcuc, 
+                                       Int            srcix )
+{
+   CfiExpr* src;
+   Int      cpL, cpR, dwreg;
+   XArray*  srcxa = srcuc->exprs;
+   vg_assert(srcxa);
+   vg_assert(dstxa);
+   vg_assert(srcix >= 0 && srcix < VG_(sizeXA)(srcxa));
+
+   src = VG_(indexXA)( srcxa, srcix );
+   switch (src->tag) {
+      case Cex_Undef:
+         return ML_(CfiExpr_Undef)( dstxa );
+      case Cex_Deref:
+         return ML_(CfiExpr_Deref)( dstxa, src->Cex.Deref.ixAddr );
+      case Cex_Const:
+         return ML_(CfiExpr_Const)( dstxa, src->Cex.Const.con );
+      case Cex_Binop:
+         cpL = copy_convert_CfiExpr_tree( dstxa, srcuc, src->Cex.Binop.ixL );
+         cpR = copy_convert_CfiExpr_tree( dstxa, srcuc, src->Cex.Binop.ixR );
+         vg_assert(cpL >= -1 && cpR >= -1);
+         if (cpL == -1 || cpR == -1)
+            return -1; /* propagate failure */
+         return ML_(CfiExpr_Binop)( dstxa, src->Cex.Binop.op, cpL, cpR );
+      case Cex_CfiReg:
+         /* should not see these in input (are created only by this
+            conversion step!) */
+         VG_(core_panic)("copy_convert_CfiExpr_tree: CfiReg in input");
+      case Cex_DwReg:
+         /* This is the only place where the conversion can fail. */
+         dwreg = src->Cex.DwReg.reg;
+         if (dwreg == SP_REG)
+            return ML_(CfiExpr_CfiReg)( dstxa, Creg_SP );
+         if (dwreg == FP_REG)
+            return ML_(CfiExpr_CfiReg)( dstxa, Creg_FP );
+         if (dwreg == srcuc->ra_reg)
+            return ML_(CfiExpr_CfiReg)( dstxa, Creg_IP ); /* correct? */
+         /* else we must fail - can't represent the reg */
+         return -1;
+      default:
+         VG_(core_panic)("copy_convert_CfiExpr_tree: default");
+   }
+}
+
+
 static void ppUnwindContext_summary ( UnwindContext* ctx )
 {
    VG_(printf)("0x%llx-1: ", (ULong)ctx->loc);
 
    if (ctx->cfa_reg == SP_REG) {
-      VG_(printf)("SP/CFA=%d+SP   ", ctx->cfa_offset);
+      VG_(printf)("SP/CFA=%d+SP   ", ctx->cfa_off);
    } else
    if (ctx->cfa_reg == FP_REG) {
-      VG_(printf)("SP/CFA=%d+FP   ", ctx->cfa_offset);
+      VG_(printf)("SP/CFA=%d+FP   ", ctx->cfa_off);
    } else {
-      VG_(printf)("SP/CFA=unknown  ", ctx->cfa_offset);
+      VG_(printf)("SP/CFA=unknown  ", ctx->cfa_off);
    }
 
    VG_(printf)("RA=");
-   ppRegRule( &ctx->reg[ctx->ra_reg] );
+   ppRegRule( ctx->exprs, &ctx->reg[ctx->ra_reg] );
 
    VG_(printf)("FP=");
-   ppRegRule( &ctx->reg[FP_REG] );
+   ppRegRule( ctx->exprs, &ctx->reg[FP_REG] );
    VG_(printf)("\n");
 }
 
@@ -2106,7 +2243,7 @@ static UChar read_UChar ( UChar* data )
    return data[0];
 }
 
-static ULong read_le_encoded_literal ( UChar* data, UInt size )
+static ULong read_le_u_encoded_literal ( UChar* data, UInt size )
 {
    switch (size) {
       case 8:  return (ULong)read_ULong( data );
@@ -2117,6 +2254,18 @@ static ULong read_le_encoded_literal ( UChar* data, UInt size )
    }
 }
 
+static Long read_le_s_encoded_literal ( UChar* data, UInt size )
+{
+   Long s64 = read_le_u_encoded_literal( data, size );
+   switch (size) {
+      case 8:  break;
+      case 4:  s64 <<= 32; s64 >>= 32; break;
+      case 2:  s64 <<= 48; s64 >>= 48; break;
+      case 1:  s64 <<= 56; s64 >>= 56; break;
+      default: vg_assert(0); /*NOTREACHED*/ return 0;
+   }
+   return s64;
+}
 
 static UChar default_Addr_encoding ( void )
 {
@@ -2232,6 +2381,321 @@ static Addr read_encoded_Addr ( /*OUT*/Int* nbytes,
 }
 
 
+/* ------------ Run/show DWARF3 expressions ---------- */
+
+/* Taken from binutils-2.17/include/elf/dwarf2.h */
+enum dwarf_location_atom
+  {
+    DW_OP_addr = 0x03,
+    DW_OP_deref = 0x06,
+    DW_OP_const1u = 0x08,
+    DW_OP_const1s = 0x09,
+    DW_OP_const2u = 0x0a,
+    DW_OP_const2s = 0x0b,
+    DW_OP_const4u = 0x0c,
+    DW_OP_const4s = 0x0d,
+    DW_OP_const8u = 0x0e,
+    DW_OP_const8s = 0x0f,
+    DW_OP_constu = 0x10,
+    DW_OP_consts = 0x11,
+    DW_OP_dup = 0x12,
+    DW_OP_drop = 0x13,
+    DW_OP_over = 0x14,
+    DW_OP_pick = 0x15,
+    DW_OP_swap = 0x16,
+    DW_OP_rot = 0x17,
+    DW_OP_xderef = 0x18,
+    DW_OP_abs = 0x19,
+    DW_OP_and = 0x1a,
+    DW_OP_div = 0x1b,
+    DW_OP_minus = 0x1c,
+    DW_OP_mod = 0x1d,
+    DW_OP_mul = 0x1e,
+    DW_OP_neg = 0x1f,
+    DW_OP_not = 0x20,
+    DW_OP_or = 0x21,
+    DW_OP_plus = 0x22,
+    DW_OP_plus_uconst = 0x23,
+    DW_OP_shl = 0x24,
+    DW_OP_shr = 0x25,
+    DW_OP_shra = 0x26,
+    DW_OP_xor = 0x27,
+    DW_OP_bra = 0x28,
+    DW_OP_eq = 0x29,
+    DW_OP_ge = 0x2a,
+    DW_OP_gt = 0x2b,
+    DW_OP_le = 0x2c,
+    DW_OP_lt = 0x2d,
+    DW_OP_ne = 0x2e,
+    DW_OP_skip = 0x2f,
+    DW_OP_lit0 = 0x30,
+    DW_OP_lit1 = 0x31,
+    DW_OP_lit2 = 0x32,
+    DW_OP_lit3 = 0x33,
+    DW_OP_lit4 = 0x34,
+    DW_OP_lit5 = 0x35,
+    DW_OP_lit6 = 0x36,
+    DW_OP_lit7 = 0x37,
+    DW_OP_lit8 = 0x38,
+    DW_OP_lit9 = 0x39,
+    DW_OP_lit10 = 0x3a,
+    DW_OP_lit11 = 0x3b,
+    DW_OP_lit12 = 0x3c,
+    DW_OP_lit13 = 0x3d,
+    DW_OP_lit14 = 0x3e,
+    DW_OP_lit15 = 0x3f,
+    DW_OP_lit16 = 0x40,
+    DW_OP_lit17 = 0x41,
+    DW_OP_lit18 = 0x42,
+    DW_OP_lit19 = 0x43,
+    DW_OP_lit20 = 0x44,
+    DW_OP_lit21 = 0x45,
+    DW_OP_lit22 = 0x46,
+    DW_OP_lit23 = 0x47,
+    DW_OP_lit24 = 0x48,
+    DW_OP_lit25 = 0x49,
+    DW_OP_lit26 = 0x4a,
+    DW_OP_lit27 = 0x4b,
+    DW_OP_lit28 = 0x4c,
+    DW_OP_lit29 = 0x4d,
+    DW_OP_lit30 = 0x4e,
+    DW_OP_lit31 = 0x4f,
+    DW_OP_reg0 = 0x50,
+    DW_OP_reg1 = 0x51,
+    DW_OP_reg2 = 0x52,
+    DW_OP_reg3 = 0x53,
+    DW_OP_reg4 = 0x54,
+    DW_OP_reg5 = 0x55,
+    DW_OP_reg6 = 0x56,
+    DW_OP_reg7 = 0x57,
+    DW_OP_reg8 = 0x58,
+    DW_OP_reg9 = 0x59,
+    DW_OP_reg10 = 0x5a,
+    DW_OP_reg11 = 0x5b,
+    DW_OP_reg12 = 0x5c,
+    DW_OP_reg13 = 0x5d,
+    DW_OP_reg14 = 0x5e,
+    DW_OP_reg15 = 0x5f,
+    DW_OP_reg16 = 0x60,
+    DW_OP_reg17 = 0x61,
+    DW_OP_reg18 = 0x62,
+    DW_OP_reg19 = 0x63,
+    DW_OP_reg20 = 0x64,
+    DW_OP_reg21 = 0x65,
+    DW_OP_reg22 = 0x66,
+    DW_OP_reg23 = 0x67,
+    DW_OP_reg24 = 0x68,
+    DW_OP_reg25 = 0x69,
+    DW_OP_reg26 = 0x6a,
+    DW_OP_reg27 = 0x6b,
+    DW_OP_reg28 = 0x6c,
+    DW_OP_reg29 = 0x6d,
+    DW_OP_reg30 = 0x6e,
+    DW_OP_reg31 = 0x6f,
+    DW_OP_breg0 = 0x70,
+    DW_OP_breg1 = 0x71,
+    DW_OP_breg2 = 0x72,
+    DW_OP_breg3 = 0x73,
+    DW_OP_breg4 = 0x74,
+    DW_OP_breg5 = 0x75,
+    DW_OP_breg6 = 0x76,
+    DW_OP_breg7 = 0x77,
+    DW_OP_breg8 = 0x78,
+    DW_OP_breg9 = 0x79,
+    DW_OP_breg10 = 0x7a,
+    DW_OP_breg11 = 0x7b,
+    DW_OP_breg12 = 0x7c,
+    DW_OP_breg13 = 0x7d,
+    DW_OP_breg14 = 0x7e,
+    DW_OP_breg15 = 0x7f,
+    DW_OP_breg16 = 0x80,
+    DW_OP_breg17 = 0x81,
+    DW_OP_breg18 = 0x82,
+    DW_OP_breg19 = 0x83,
+    DW_OP_breg20 = 0x84,
+    DW_OP_breg21 = 0x85,
+    DW_OP_breg22 = 0x86,
+    DW_OP_breg23 = 0x87,
+    DW_OP_breg24 = 0x88,
+    DW_OP_breg25 = 0x89,
+    DW_OP_breg26 = 0x8a,
+    DW_OP_breg27 = 0x8b,
+    DW_OP_breg28 = 0x8c,
+    DW_OP_breg29 = 0x8d,
+    DW_OP_breg30 = 0x8e,
+    DW_OP_breg31 = 0x8f,
+    DW_OP_regx = 0x90,
+    DW_OP_fbreg = 0x91,
+    DW_OP_bregx = 0x92,
+    DW_OP_piece = 0x93,
+    DW_OP_deref_size = 0x94,
+    DW_OP_xderef_size = 0x95,
+    DW_OP_nop = 0x96,
+    /* DWARF 3 extensions.  */
+    DW_OP_push_object_address = 0x97,
+    DW_OP_call2 = 0x98,
+    DW_OP_call4 = 0x99,
+    DW_OP_call_ref = 0x9a,
+    DW_OP_form_tls_address = 0x9b,
+    DW_OP_call_frame_cfa = 0x9c,
+    DW_OP_bit_piece = 0x9d,
+    /* GNU extensions.  */
+    DW_OP_GNU_push_tls_address = 0xe0,
+    /* HP extensions.  */
+    DW_OP_HP_unknown     = 0xe0, /* Ouch, the same as GNU_push_tls_address.  */
+    DW_OP_HP_is_value    = 0xe1,
+    DW_OP_HP_fltconst4   = 0xe2,
+    DW_OP_HP_fltconst8   = 0xe3,
+    DW_OP_HP_mod_range   = 0xe4,
+    DW_OP_HP_unmod_range = 0xe5,
+    DW_OP_HP_tls         = 0xe6
+  };
+
+
+/* Convert the DWARF3 expression in expr[0 .. exprlen-1] into a dag
+   (of CfiExprs) stored in ctx->exprs, and return the index in
+   ctx->exprs of the root node.  Or fail in which case return -1. */
+
+static Int dwarfexpr_to_dag ( UnwindContext* ctx, 
+                              UChar* expr, Int exprlen, 
+                              Bool push_cfa_at_start,
+                              Bool ddump_frames )
+{
+#  define N_EXPR_STACK 20
+
+#  define PUSH(_arg)                               \
+      do {                                         \
+         vg_assert(sp >= -1 && sp < N_EXPR_STACK); \
+         if (sp == N_EXPR_STACK-1)                 \
+            return -1;                             \
+         sp++;                                     \
+         stack[sp] = (_arg);                       \
+      } while (0)
+
+#  define POP(_lval)                               \
+      do {                                         \
+         vg_assert(sp >= -1 && sp < N_EXPR_STACK); \
+         if (sp == -1)                             \
+            return -1;                             \
+         _lval = stack[sp];                        \
+         sp--;                                     \
+      } while (0)
+
+   Int   ix, ix2, reg;
+   UChar opcode;
+   Word  sw;
+
+   Int sp; /* # of top element: valid is -1 .. N_EXPR_STACK-1 */
+   Int stack[N_EXPR_STACK];  /* indices into ctx->exprs */
+
+   XArray* dst   = ctx->exprs;
+   UChar*  limit = expr + exprlen;
+
+   vg_assert(dst);
+   vg_assert(exprlen >= 0);
+
+   sp = -1; /* empty */
+
+   /* Synthesise the CFA as a CfiExpr */
+   if (push_cfa_at_start) {
+      if (ctx->cfa_is_regoff) {
+         /* cfa is reg +/- offset */
+         ix = ML_(CfiExpr_Binop)( dst,
+                 Cop_Add,
+                 ML_(CfiExpr_DwReg)( dst, ctx->cfa_reg ),
+                 ML_(CfiExpr_Const)( dst, (UWord)(Word)ctx->cfa_off )
+              );
+         PUSH(ix);
+      } else {
+         /* CFA is already an expr; use its root node */
+         PUSH(ctx->cfa_expr_ix);
+      }
+   }
+
+   while (True) {
+
+      vg_assert(sp >= -1 && sp < N_EXPR_STACK);
+
+      if (expr > limit) 
+         return -1;  /* overrun - something's wrong */
+
+      if (expr == limit) {
+        /* end of expr - return expr on the top of stack. */
+        if (sp == -1)
+           return -1; /* stack empty.  Bad. */
+        else
+           break;
+      }
+
+      opcode = *expr++;
+      switch (opcode) {
+
+         case DW_OP_breg0 ... DW_OP_breg31:
+            /* push: reg + sleb128 */
+            reg = (Int)opcode - (Int)DW_OP_breg0;
+            vg_assert(reg >= 0 && reg <= 31);
+            sw = read_leb128S( &expr );
+            ix = ML_(CfiExpr_Binop)( dst,
+                    Cop_Add,
+                    ML_(CfiExpr_DwReg)( dst, reg ),
+                    ML_(CfiExpr_Const)( dst, (UWord)sw )
+                 );
+            PUSH(ix);
+            if (ddump_frames)
+               VG_(printf)("DW_OP_breg%d: %ld", reg, sw);
+            break;
+
+         case DW_OP_const4s:
+            /* push: 32-bit signed immediate */
+            sw = read_le_s_encoded_literal( expr, 4 );
+            expr += 4;
+            PUSH( ML_(CfiExpr_Const)( dst, (UWord)sw ) );
+            if (ddump_frames)
+               VG_(printf)("DW_OP_const4s: %ld", sw);
+            break;
+
+         case DW_OP_minus:
+            POP( ix );
+            POP( ix2 );
+           PUSH( ML_(CfiExpr_Binop)( dst, Cop_Sub, ix2, ix ) );
+            if (ddump_frames)
+               VG_(printf)("DW_OP_minus");
+            break;
+
+         case DW_OP_plus:
+            POP( ix );
+            POP( ix2 );
+           PUSH( ML_(CfiExpr_Binop)( dst, Cop_Add, ix2, ix ) );
+            if (ddump_frames)
+               VG_(printf)("DW_OP_plus");
+            break;
+
+         default:
+            if (ddump_frames /* || trace_cfi*/)
+               VG_(printf)("XXX unhandled dwarf expr opcode 0x%x\n", 
+                           (Int)opcode );
+            return -1;
+      }
+
+      if (expr < limit && ddump_frames)
+         VG_(printf)("; ");
+
+   }
+
+   vg_assert(sp >= -1 && sp < N_EXPR_STACK);
+   if (sp == -1)
+      return -1;
+
+   if (0 && ddump_frames)
+      ML_(ppCfiExpr)( dst, stack[sp] );
+   return stack[sp];
+
+#  undef POP
+#  undef PUSH
+#  undef N_EXPR_STACK
+}
+
+
 /* ------------ Run/show CFI instructions ------------ */
 
 /* Run a CFI instruction, and also return its length.
@@ -2243,12 +2707,14 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
                                 AddressDecodingInfo* adi,
                                 struct _SegInfo* si )
 {
-   Int   off, reg, reg2, nleb, len;
-   UInt  delta;
-   Int   i   = 0;
-   UChar hi2 = (instr[i] >> 6) & 3;
-   UChar lo6 = instr[i] & 0x3F;
-   Addr  printing_bias = ((Addr)ctx->initloc) - ((Addr)si->text_bias);
+   Int    off, reg, reg2, nleb, len;
+   UInt   delta;
+   UChar* expr;
+   Int    j;
+   Int    i   = 0;
+   UChar  hi2 = (instr[i] >> 6) & 3;
+   UChar  lo6 = instr[i] & 0x3F;
+   Addr   printing_bias = ((Addr)ctx->initloc) - ((Addr)si->text_bias);
    i++;
 
    if (hi2 == DW_CFA_advance_loc) {
@@ -2261,18 +2727,18 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
    }
 
    if (hi2 == DW_CFA_offset) {
-      /* Set rule for reg 'lo6' to CFAoffset(off * data_af) */
+      /* Set rule for reg 'lo6' to CFAOff(off * data_af) */
       off = read_leb128( &instr[i], &nleb, 0 );
       i += nleb;
       reg = (Int)lo6;
       if (reg < 0 || reg >= N_CFI_REGS) 
          return 0; /* fail */
-      ctx->reg[reg].tag = RR_CFAoff;
-      ctx->reg[reg].coff = off * ctx->data_a_f;
+      ctx->reg[reg].tag = RR_CFAOff;
+      ctx->reg[reg].arg = off * ctx->data_a_f;
       if (si->ddump_frames)
          VG_(printf)("  DW_CFA_offset: r%d at cfa%s%d\n",
-                     (Int)reg, ctx->reg[reg].coff < 0 ? "" : "+", 
-                     (Int)ctx->reg[reg].coff  );
+                     (Int)reg, ctx->reg[reg].arg < 0 ? "" : "+", 
+                     (Int)ctx->reg[reg].arg );
       return i;
    }
 
@@ -2336,8 +2802,10 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          i += nleb;
          if (reg < 0 || reg >= N_CFI_REGS) 
             return 0; /* fail */
-         ctx->cfa_reg    = reg;
-         ctx->cfa_offset = off;
+         ctx->cfa_is_regoff = True;
+         ctx->cfa_expr_ix   = 0;
+         ctx->cfa_reg       = reg;
+         ctx->cfa_off       = off;
          if (si->ddump_frames)
             VG_(printf)("  DW_CFA_def_cfa: r%d ofs %d\n", (Int)reg, (Int)off);
          break;
@@ -2349,8 +2817,10 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          i += nleb;
          if (reg < 0 || reg >= N_CFI_REGS)
             return 0; /* fail */
-         ctx->cfa_reg    = reg;
-         ctx->cfa_offset = off * ctx->data_a_f;
+         ctx->cfa_is_regoff = True;
+         ctx->cfa_expr_ix   = 0;
+         ctx->cfa_reg       = reg;
+         ctx->cfa_off       = off * ctx->data_a_f;
          if (si->ddump_frames)
             VG_(printf)("  rci:DW_CFA_def_cfa_sf\n");
          break;
@@ -2365,9 +2835,10 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          if (reg2 < 0 || reg2 >= N_CFI_REGS) 
             return 0; /* fail */
          ctx->reg[reg].tag = RR_Reg;
-         ctx->reg[reg].reg = reg2;
+         ctx->reg[reg].arg = reg2;
          if (si->ddump_frames)
-            VG_(printf)("  rci:DW_CFA_register\n");
+            VG_(printf)("  DW_CFA_register: r%d in r%d\n", 
+                        (Int)reg, (Int)reg2);
          break;
 
       case DW_CFA_offset_extended:
@@ -2377,8 +2848,8 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          i += nleb;
          if (reg < 0 || reg >= N_CFI_REGS)
             return 0; /* fail */
-         ctx->reg[reg].tag = RR_CFAoff;
-         ctx->reg[reg].coff = off * ctx->data_a_f;
+         ctx->reg[reg].tag = RR_CFAOff;
+         ctx->reg[reg].arg = off * ctx->data_a_f;
          if (si->ddump_frames)
             VG_(printf)("  rci:DW_CFA_offset_extended\n");
          break;
@@ -2390,12 +2861,12 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          i += nleb;
          if (reg < 0 || reg >= N_CFI_REGS) 
             return 0; /* fail */
-         ctx->reg[reg].tag = RR_CFAoff;
-         ctx->reg[reg].coff = off * ctx->data_a_f;
+         ctx->reg[reg].tag = RR_CFAOff;
+         ctx->reg[reg].arg = off * ctx->data_a_f;
          if (si->ddump_frames)
             VG_(printf)("  DW_CFA_offset_extended_sf: r%d at cfa%s%d\n", 
-                        reg, ctx->reg[reg].coff < 0 ? "" : "+", 
-                        (Int)ctx->reg[reg].coff);
+                        reg, ctx->reg[reg].arg < 0 ? "" : "+", 
+                        (Int)ctx->reg[reg].arg);
          break;
 
       case DW_CFA_GNU_negative_offset_extended:
@@ -2405,8 +2876,8 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          i += nleb;
          if (reg < 0 || reg >= N_CFI_REGS)
             return 0; /* fail */
-         ctx->reg[reg].tag = RR_CFAoff;
-         ctx->reg[reg].coff = -off * ctx->data_a_f;
+         ctx->reg[reg].tag = RR_CFAOff;
+         ctx->reg[reg].arg = (-off) * ctx->data_a_f;
          if (si->ddump_frames)
             VG_(printf)("  rci:DW_CFA_GNU_negative_offset_extended\n");
          break;
@@ -2430,8 +2901,8 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          i += nleb;
          if (reg < 0 || reg >= N_CFI_REGS)
             return 0; /* fail */
-         ctx->reg[reg].tag = RR_CFAValoff;
-         ctx->reg[reg].coff = off * ctx->data_a_f;
+         ctx->reg[reg].tag = RR_CFAValOff;
+         ctx->reg[reg].arg = off * ctx->data_a_f;
          if (si->ddump_frames)
             VG_(printf)("  rci:DW_CFA_val_offset\n");
          break;
@@ -2443,8 +2914,8 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          i += nleb;
          if (reg < 0 || reg >= N_CFI_REGS)
             return 0; /* fail */
-         ctx->reg[reg].tag = RR_CFAValoff;
-         ctx->reg[reg].coff = off * ctx->data_a_f;
+         ctx->reg[reg].tag = RR_CFAValOff;
+         ctx->reg[reg].arg = off * ctx->data_a_f;
          if (si->ddump_frames)
             VG_(printf)("  rci:DW_CFA_val_offset_sf\n");
          break;
@@ -2454,7 +2925,10 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          i += nleb;
          if (reg < 0 || reg >= N_CFI_REGS) 
             return 0; /* fail */
-         ctx->cfa_reg = reg;
+         ctx->cfa_is_regoff = True;
+         ctx->cfa_expr_ix   = 0;
+         ctx->cfa_reg       = reg;
+         /* ->cfa_off unchanged */
          if (si->ddump_frames)
             VG_(printf)("  DW_CFA_def_cfa_reg: r%d\n", (Int)reg );
          break;
@@ -2462,7 +2936,10 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
       case DW_CFA_def_cfa_offset:
          off = read_leb128( &instr[i], &nleb, 0);
          i += nleb;
-         ctx->cfa_offset = off;
+         ctx->cfa_is_regoff = True;
+         ctx->cfa_expr_ix   = 0;
+         /* ->reg is unchanged */
+         ctx->cfa_off       = off;
          if (si->ddump_frames)
             VG_(printf)("  DW_CFA_def_cfa_offset: %d\n", (Int)off);
          break;
@@ -2470,9 +2947,12 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
       case DW_CFA_def_cfa_offset_sf:
          off = read_leb128( &instr[i], &nleb, 1);
          i += nleb;
-         ctx->cfa_offset = off * ctx->data_a_f;
+         ctx->cfa_is_regoff = True;
+         ctx->cfa_expr_ix   = 0;
+         /* ->reg is unchanged */
+         ctx->cfa_off       = off * ctx->data_a_f;
          if (si->ddump_frames)
-            VG_(printf)("  DW_CFA_def_cfa_offset_sf: %d\n", ctx->cfa_offset);
+            VG_(printf)("  DW_CFA_def_cfa_offset_sf: %d\n", ctx->cfa_off);
          break;
 
       case DW_CFA_undefined:
@@ -2481,8 +2961,7 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          if (reg < 0 || reg >= N_CFI_REGS) 
             return 0; /* fail */
          ctx->reg[reg].tag = RR_Undef;
-         ctx->reg[reg].coff = 0;
-         ctx->reg[reg].reg = 0;
+         ctx->reg[reg].arg = 0;
          if (si->ddump_frames)
             VG_(printf)("  rci:DW_CFA_undefined\n");
          break;
@@ -2515,22 +2994,32 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          break;
 
       case DW_CFA_val_expression:
-         /* Too difficult to really handle; just skip over it and say
-            that we don't know what do to with the register. */
-         if (si->trace_cfi)
-            VG_(printf)("DWARF2 CFI reader: "
-                        "ignoring DW_CFA_val_expression\n");
          reg = read_leb128( &instr[i], &nleb, 0 );
          i += nleb;
          len = read_leb128( &instr[i], &nleb, 0 );
          i += nleb;
+         expr = &instr[i];
          i += len;
          if (reg < 0 || reg >= N_CFI_REGS)
             return 0; /* fail */
-         ctx->reg[reg].tag = RR_ValExpr;
          if (si->ddump_frames)
-            VG_(printf)("  DW_CFA_val_expression: r%d (ignored)\n", 
+            VG_(printf)("  DW_CFA_val_expression: r%d (", 
                         (Int)reg);
+         /* Convert the expression into a dag rooted at ctx->exprs index j,
+            or fail. */
+         j = dwarfexpr_to_dag ( ctx, expr, len, True/*push CFA at start*/, 
+                                si->ddump_frames);
+         if (si->ddump_frames)
+            VG_(printf)(")\n");
+         vg_assert(j >= -1);
+         if (j >= 0) {
+            vg_assert(ctx->exprs);
+            vg_assert( j < VG_(sizeXA)(ctx->exprs) );
+         }
+         if (j == -1)
+            return 0; /* fail */
+         ctx->reg[reg].tag = RR_ValExpr;
+         ctx->reg[reg].arg = j;
          break;
 
       case DW_CFA_def_cfa_expression:
@@ -2540,7 +3029,10 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          len = read_leb128( &instr[i], &nleb, 0 );
          i += nleb;
          i += len;
-         ctx->cfa_reg = -1; /* indicating we don't know */
+         ctx->cfa_is_regoff = False;
+         ctx->cfa_reg       = 0;
+         ctx->cfa_off       = 0;
+         ctx->cfa_expr_ix   = -1; /* invalid - should handle properly */
          if (si->ddump_frames)
             VG_(printf)("  rci:DW_CFA_def_cfa_expression (ignored)\n");
          break;
@@ -2840,7 +3332,7 @@ Bool run_CF_instructions ( struct _SegInfo* si,
          if (summ_ok) {
             ML_(addDiCfSI)(si, &cfsi);
             if (si->trace_cfi)
-               ML_(ppDiCfSI)(&cfsi);
+               ML_(ppDiCfSI)(si->cfsi_exprs, &cfsi);
          }
       }
    }
@@ -2852,7 +3344,7 @@ Bool run_CF_instructions ( struct _SegInfo* si,
          if (summ_ok) {
             ML_(addDiCfSI)(si, &cfsi);
             if (si->trace_cfi)
-               ML_(ppDiCfSI)(&cfsi);
+               ML_(ppDiCfSI)(si->cfsi_exprs, &cfsi);
          }
       }
    }
@@ -3237,7 +3729,7 @@ void ML_(read_callframe_info_dwarf3)
            switch (ptr_size) {
               case 8: case 4: case 2: case 1: 
                  fde_arange 
-                    = (UWord)read_le_encoded_literal(data, ptr_size);
+                    = (UWord)read_le_u_encoded_literal(data, ptr_size);
                  data += ptr_size;
                  break;
               default: 
@@ -3300,6 +3792,9 @@ void ML_(read_callframe_info_dwarf3)
          ctx.data_a_f = the_CIEs[cie].data_a_f;
          ctx.initloc  = fde_initloc;
          ctx.ra_reg   = the_CIEs[cie].ra_reg;
+         ctx.exprs    = VG_(newXA)( symtab_alloc, symtab_free, 
+                                    sizeof(CfiExpr) );
+         vg_assert(ctx.exprs);
 
         /* Run the CIE's instructions.  Ugly hack: if
             --debug-dump=frames is in effect, suppress output for
@@ -3328,6 +3823,8 @@ void ML_(read_callframe_info_dwarf3)
             if (si->ddump_frames)
                VG_(printf)("\n");
         }
+
+         VG_(deleteXA)( ctx.exprs );
       }
    }
 
index 7a2946d3913fc22fd26df3d379f2f1e95edccd9c..5f24264c1cbdc936d7ac3f09ce55d130eeace6b0 100644 (file)
@@ -46,6 +46,7 @@
 #include "pub_core_options.h"
 #include "pub_core_oset.h"
 #include "pub_core_tooliface.h"    /* VG_(needs) */
+#include "pub_core_xarray.h"
 #include "priv_storage.h"
 #include "priv_readelf.h"          /* self */
 #include "priv_readdwarf.h"        /* 'cos ELF contains DWARF */
index ba146b7d9872c8d075966777cda4202536f5d58c..194d2ec39dc32d299efb5735484771b7eed00736 100644 (file)
@@ -39,6 +39,7 @@
 #include "pub_core_libcassert.h"
 #include "pub_core_libcprint.h"
 #include "pub_core_mallocfree.h"
+#include "pub_core_xarray.h"
 #include "priv_storage.h"
 #include "priv_readstabs.h"        /* self */
 
index 4f3095f92ff0efa1048bb9a7856b586d581b6f3b..e4ce739965ffe1883b0438b46e718eae88185342 100644 (file)
@@ -1,7 +1,7 @@
 
 /*--------------------------------------------------------------------*/
 /*--- Format-neutral storage of and querying of info acquired from ---*/
-/*--- ELF/XCOFF stabs/dwarf1/dwarf2 debug info.                    ---*/
+/*--- ELF/XCOFF stabs/dwarf1/dwarf2/dwarf3 debug info.             ---*/
 /*---                                                    storage.c ---*/
 /*--------------------------------------------------------------------*/
 
@@ -43,6 +43,7 @@
 #include "pub_core_libcbase.h"
 #include "pub_core_libcprint.h"
 #include "pub_core_mallocfree.h"
+#include "pub_core_xarray.h"
 #include "priv_storage.h"          /* self */
 
 
@@ -69,7 +70,7 @@ void ML_(ppSym) ( Int idx, DiSym* sym )
 }
 
 /* Print a call-frame-info summary. */
-void ML_(ppDiCfSI) ( DiCfSI* si )
+void ML_(ppDiCfSI) ( XArray* /* of CfiExpr */ exprs, DiCfSI* si )
 {
 #  define SHOW_HOW(_how, _off)                   \
       do {                                       \
@@ -84,15 +85,34 @@ void ML_(ppDiCfSI) ( DiCfSI* si )
          } else                                  \
          if (_how == CFIR_MEMCFAREL) {           \
             VG_(printf)("*(cfa+%d)", _off);      \
+         } else                                  \
+         if (_how == CFIR_EXPR) {                \
+            VG_(printf)("{");                    \
+            ML_(ppCfiExpr)(exprs, _off);         \
+            VG_(printf)("}");                    \
          } else {                                \
-            VG_(printf)("???");                  \
+            vg_assert(0+0);                      \
          }                                       \
       } while (0)
 
    VG_(printf)("[%p .. %p]: ", si->base, 
                                si->base + (UWord)si->len - 1);
-   VG_(printf)("let cfa=%s+%d", 
-               si->cfa_sprel ? "oldSP" : "oldFP", si->cfa_off);
+   switch (si->cfa_how) {
+      case CFIC_SPREL: 
+         VG_(printf)("let cfa=oldSP+%d", si->cfa_off); 
+         break;
+      case CFIC_FPREL: 
+         VG_(printf)("let cfa=oldFP+%d", si->cfa_off); 
+         break;
+      case CFIC_EXPR: 
+         VG_(printf)("let cfa={"); 
+         ML_(ppCfiExpr)(exprs, si->cfa_off);
+         VG_(printf)("}"); 
+         break;
+      default: 
+         vg_assert(0);
+   }
+
    VG_(printf)(" in RA=");
    SHOW_HOW(si->ra_how, si->ra_off);
    VG_(printf)(" SP=");
@@ -308,7 +328,7 @@ void ML_(addDiCfSI) ( struct _SegInfo* si, DiCfSI* cfsi )
 
    if (debug) {
       VG_(printf)("adding DiCfSI: ");
-      ML_(ppDiCfSI)(cfsi);
+      ML_(ppDiCfSI)(si->cfsi_exprs, cfsi);
    }
 
    /* sanity */
@@ -338,7 +358,7 @@ void ML_(addDiCfSI) ( struct _SegInfo* si, DiCfSI* cfsi )
             );
          }
          if (VG_(clo_trace_cfi)) 
-            ML_(ppDiCfSI)(cfsi);
+            ML_(ppDiCfSI)(si->cfsi_exprs, cfsi);
       }
       return;
    }
@@ -362,6 +382,116 @@ void ML_(addDiCfSI) ( struct _SegInfo* si, DiCfSI* cfsi )
 }
 
 
+Int ML_(CfiExpr_Undef)( XArray* dst )
+{
+   CfiExpr e;
+   VG_(memset)( &e, 0, sizeof(e) );
+   e.tag = Cex_Undef;
+   return VG_(addToXA)( dst, &e );
+}
+Int ML_(CfiExpr_Deref)( XArray* dst, Int ixAddr )
+{
+   CfiExpr e;
+   VG_(memset)( &e, 0, sizeof(e) );
+   e.tag = Cex_Deref;
+   e.Cex.Deref.ixAddr = ixAddr;
+   return VG_(addToXA)( dst, &e );
+}
+Int ML_(CfiExpr_Const)( XArray* dst, UWord con )
+{
+   CfiExpr e;
+   VG_(memset)( &e, 0, sizeof(e) );
+   e.tag = Cex_Const;
+   e.Cex.Const.con = con;
+   return VG_(addToXA)( dst, &e );
+}
+Int ML_(CfiExpr_Binop)( XArray* dst, CfiOp op, Int ixL, Int ixR )
+{
+   CfiExpr e;
+   VG_(memset)( &e, 0, sizeof(e) );
+   e.tag = Cex_Binop;
+   e.Cex.Binop.op  = op;
+   e.Cex.Binop.ixL = ixL;
+   e.Cex.Binop.ixR = ixR;
+   return VG_(addToXA)( dst, &e );
+}
+Int ML_(CfiExpr_CfiReg)( XArray* dst, CfiReg reg )
+{
+   CfiExpr e;
+   VG_(memset)( &e, 0, sizeof(e) );
+   e.tag = Cex_CfiReg;
+   e.Cex.CfiReg.reg = reg;
+   return VG_(addToXA)( dst, &e );
+}
+Int ML_(CfiExpr_DwReg)( XArray* dst, Int reg )
+{
+   CfiExpr e;
+   VG_(memset)( &e, 0, sizeof(e) );
+   e.tag = Cex_DwReg;
+   e.Cex.DwReg.reg = reg;
+   return VG_(addToXA)( dst, &e );
+}
+
+static void ppCfiOp ( CfiOp op ) 
+{
+   switch (op) {
+      case Cop_Add: VG_(printf)("+"); break;
+      case Cop_Sub: VG_(printf)("-"); break;
+      case Cop_And: VG_(printf)("&"); break;
+      default:      vg_assert(0);
+   }
+}
+
+static void ppCfiReg ( CfiReg reg )
+{
+   switch (reg) {
+      case Creg_SP: VG_(printf)("SP"); break;
+      case Creg_FP: VG_(printf)("FP"); break;
+      case Creg_IP: VG_(printf)("IP"); break;
+      default: vg_assert(0);
+   }
+}
+
+void ML_(ppCfiExpr)( XArray* src, Int ix )
+{
+   /* VG_(indexXA) checks for invalid src/ix values, so we can
+      use it indiscriminately. */
+   CfiExpr* e = (CfiExpr*) VG_(indexXA)( src, ix );
+   switch (e->tag) {
+      case Cex_Undef: 
+         VG_(printf)("Undef"); 
+         break;
+      case Cex_Deref: 
+         VG_(printf)("*("); 
+         ML_(ppCfiExpr)(src, e->Cex.Deref.ixAddr);
+         VG_(printf)(")"); 
+         break;
+      case Cex_Const: 
+         VG_(printf)("0x%lx", e->Cex.Const.con); 
+         break;
+      case Cex_Binop: 
+         VG_(printf)("(");
+         ML_(ppCfiExpr)(src, e->Cex.Binop.ixL);
+         VG_(printf)(")");
+         ppCfiOp(e->Cex.Binop.op);
+         VG_(printf)("(");
+         ML_(ppCfiExpr)(src, e->Cex.Binop.ixR);
+         VG_(printf)(")");
+         break;
+      case Cex_CfiReg:
+         ppCfiReg(e->Cex.CfiReg.reg);
+         break;
+      case Cex_DwReg:
+         VG_(printf)("dwr%d", e->Cex.DwReg.reg);
+         break;
+      default: 
+         VG_(core_panic)("ML_(ppCfiExpr)"); 
+         /*NOTREACHED*/
+         break;
+   }
+}
+
+
 /*------------------------------------------------------------*/
 /*--- Canonicalisers                                       ---*/
 /*------------------------------------------------------------*/