]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Handle DW_CFA_{remember,restore}_state. This requires having a stack
authorJulian Seward <jseward@acm.org>
Thu, 21 May 2009 15:33:36 +0000 (15:33 +0000)
committerJulian Seward <jseward@acm.org>
Thu, 21 May 2009 15:33:36 +0000 (15:33 +0000)
of currently on-the-go register rules, rather than just one.

gcc doesn't appear to generate these (it's pretty darn obscure), but
they do turn up a piece of handwritten assembly somewhere in the
depths of Python-2.6 on amd64-linux.

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

coregrind/m_debuginfo/readdwarf.c

index 3bb776ec3763110ff85f438348db0d6bae9fa7f9..30df436dc948cc6676d1b8758e680aef240d24e5 100644 (file)
@@ -1922,6 +1922,11 @@ static void ppRegRule ( XArray* exprs, RegRule* rrule )
 }
 
 
+/* Size of the stack of register unwind rules.  This is only
+   exceedingly rarely used, so a stack of size 1 should actually work
+   with almost all compiler-generated CFA. */
+#define N_RR_STACK 4
+
 typedef
    struct {
       /* Read-only fields (set by the CIE) */
@@ -1938,8 +1943,12 @@ typedef
       Int     cfa_reg;
       Int     cfa_off;  /* in bytes */
       Int     cfa_expr_ix; /* index into cfa_exprs */
-      /* register unwind rules */
-      RegRule reg[N_CFI_REGS];
+      /* A stack of register unwind rules.  We need a stack of them,
+         rather than just one set of rules, in order to handle
+         DW_CFA_{remember,restore}_state. */
+      RegRule reg[N_RR_STACK][N_CFI_REGS];
+      Int     reg_sp; /* 0 <= reg_sp < N_RR_STACK; points at the
+                         currently-in-use rule set. */
       /* array of CfiExpr, shared by reg[] and cfa_expr_ix */
       XArray* exprs;
    }
@@ -1947,7 +1956,7 @@ typedef
 
 static void ppUnwindContext ( UnwindContext* ctx )
 {
-   Int i;
+   Int j, i;
    VG_(printf)("0x%llx: ", (ULong)ctx->loc);
    if (ctx->cfa_is_regoff) {
       VG_(printf)("%d(r%d) ",  ctx->cfa_off, ctx->cfa_reg);
@@ -1957,14 +1966,19 @@ static void ppUnwindContext ( UnwindContext* ctx )
       ML_(ppCfiExpr)( ctx->exprs, ctx->cfa_expr_ix );
       VG_(printf)("} ");
    }
-   for (i = 0; i < N_CFI_REGS; i++)
-      ppRegRule(ctx->exprs, &ctx->reg[i]);
+   for (j = 0; j <= ctx->reg_sp; j++) {
+      VG_(printf)("%s[%d]={ ", j > 0 ? " " : "", j);
+      for (i = 0; i < N_CFI_REGS; i++)
+         ppRegRule(ctx->exprs, &ctx->reg[j][i]);
+      VG_(printf)("}");
+   }
    VG_(printf)("\n");
 }
 
 static void initUnwindContext ( /*OUT*/UnwindContext* ctx )
 {
-   Int i;
+   Int j, i;
+   VG_(memset)(ctx, 0, sizeof(*ctx));
    ctx->code_a_f      = 0;
    ctx->data_a_f      = 0;
    ctx->initloc       = 0;
@@ -1975,9 +1989,12 @@ static void initUnwindContext ( /*OUT*/UnwindContext* ctx )
    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].arg = 0;
+   ctx->reg_sp        = 0;
+   for (j = 0; j < N_RR_STACK; j++) {
+      for (i = 0; i < N_CFI_REGS; i++) {
+         ctx->reg[j][i].tag = RR_Undef;
+         ctx->reg[j][i].arg = 0;
+      }
    }
 }
 
@@ -2103,8 +2120,15 @@ static Bool summarise_context( /*OUT*/DiCfSI* si,
          why = 2; goto failed; /* otherwise give up */        \
    }
 
-   SUMMARISE_HOW(si->ra_how, si->ra_off, ctx->reg[ctx->ra_reg] );
-   SUMMARISE_HOW(si->fp_how, si->fp_off, ctx->reg[FP_REG] );
+   /* Guard against obviously stupid settings of the reg-rule stack
+      pointer. */
+   if (ctx->reg_sp < 0)           { why = 8; goto failed; }
+   if (ctx->reg_sp >= N_RR_STACK) { why = 9; goto failed; }
+
+   SUMMARISE_HOW(si->ra_how, si->ra_off,
+                             ctx->reg[ctx->reg_sp][ctx->ra_reg] );
+   SUMMARISE_HOW(si->fp_how, si->fp_off,
+                             ctx->reg[ctx->reg_sp][FP_REG] );
 
 #  undef SUMMARISE_HOW
 
@@ -2115,7 +2139,7 @@ static Bool summarise_context( /*OUT*/DiCfSI* si,
 
    /* also, gcc says "Undef" for %{e,r}bp when it is unchanged.  So
       .. */
-   if (ctx->reg[FP_REG].tag == RR_Undef)
+   if (ctx->reg[ctx->reg_sp][FP_REG].tag == RR_Undef)
       si->fp_how = CFIR_SAME;
 
    /* knock out some obviously stupid cases */
@@ -2214,10 +2238,10 @@ static void ppUnwindContext_summary ( UnwindContext* ctx )
    }
 
    VG_(printf)("RA=");
-   ppRegRule( ctx->exprs, &ctx->reg[ctx->ra_reg] );
+   ppRegRule( ctx->exprs, &ctx->reg[ctx->reg_sp][ctx->ra_reg] );
 
    VG_(printf)("FP=");
-   ppRegRule( ctx->exprs, &ctx->reg[FP_REG] );
+   ppRegRule( ctx->exprs, &ctx->reg[ctx->reg_sp][FP_REG] );
    VG_(printf)("\n");
 }
 
@@ -2663,6 +2687,9 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
    Addr   printing_bias = ((Addr)ctx->initloc) - ((Addr)di->text_bias);
    i++;
 
+   if (ctx->reg_sp < 0 || ctx->reg_sp >= N_RR_STACK)
+      return 0; /* bogus reg-rule stack pointer */
+
    if (hi2 == DW_CFA_advance_loc) {
       delta = (UInt)lo6;
       ctx->loc += delta;
@@ -2679,12 +2706,13 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
       reg = (Int)lo6;
       if (reg < 0 || reg >= N_CFI_REGS) 
          return 0; /* fail */
-      ctx->reg[reg].tag = RR_CFAOff;
-      ctx->reg[reg].arg = off * ctx->data_a_f;
+      ctx->reg[ctx->reg_sp][reg].tag = RR_CFAOff;
+      ctx->reg[ctx->reg_sp][reg].arg = off * ctx->data_a_f;
       if (di->ddump_frames)
          VG_(printf)("  DW_CFA_offset: r%d at cfa%s%d\n",
-                     (Int)reg, ctx->reg[reg].arg < 0 ? "" : "+", 
-                     (Int)ctx->reg[reg].arg );
+                     (Int)reg,
+                     ctx->reg[ctx->reg_sp][reg].arg < 0 ? "" : "+", 
+                     (Int)ctx->reg[ctx->reg_sp][reg].arg );
       return i;
    }
 
@@ -2694,7 +2722,7 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          return 0; /* fail */
       if (restore_ctx == NULL)
          return 0; /* fail */
-      ctx->reg[reg] = restore_ctx->reg[reg];
+      ctx->reg[ctx->reg_sp][reg] = restore_ctx->reg[ctx->reg_sp][reg];
       if (di->ddump_frames)
          VG_(printf)("  DW_CFA_restore: r%d\n", (Int)reg);
       return i;
@@ -2780,8 +2808,8 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
             return 0; /* fail */
          if (reg2 < 0 || reg2 >= N_CFI_REGS) 
             return 0; /* fail */
-         ctx->reg[reg].tag = RR_Reg;
-         ctx->reg[reg].arg = reg2;
+         ctx->reg[ctx->reg_sp][reg].tag = RR_Reg;
+         ctx->reg[ctx->reg_sp][reg].arg = reg2;
          if (di->ddump_frames)
             VG_(printf)("  DW_CFA_register: r%d in r%d\n", 
                         (Int)reg, (Int)reg2);
@@ -2794,8 +2822,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].arg = off * ctx->data_a_f;
+         ctx->reg[ctx->reg_sp][reg].tag = RR_CFAOff;
+         ctx->reg[ctx->reg_sp][reg].arg = off * ctx->data_a_f;
          if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_offset_extended\n");
          break;
@@ -2807,12 +2835,13 @@ 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].arg = off * ctx->data_a_f;
+         ctx->reg[ctx->reg_sp][reg].tag = RR_CFAOff;
+         ctx->reg[ctx->reg_sp][reg].arg = off * ctx->data_a_f;
          if (di->ddump_frames)
             VG_(printf)("  DW_CFA_offset_extended_sf: r%d at cfa%s%d\n", 
-                        reg, ctx->reg[reg].arg < 0 ? "" : "+", 
-                        (Int)ctx->reg[reg].arg);
+                        reg,
+                        ctx->reg[ctx->reg_sp][reg].arg < 0 ? "" : "+", 
+                        (Int)ctx->reg[ctx->reg_sp][reg].arg);
          break;
 
       case DW_CFA_GNU_negative_offset_extended:
@@ -2822,8 +2851,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].arg = (-off) * ctx->data_a_f;
+         ctx->reg[ctx->reg_sp][reg].tag = RR_CFAOff;
+         ctx->reg[ctx->reg_sp][reg].arg = (-off) * ctx->data_a_f;
          if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_GNU_negative_offset_extended\n");
          break;
@@ -2835,7 +2864,7 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
             return 0; /* fail */
         if (restore_ctx == NULL)
            return 0; /* fail */
-        ctx->reg[reg] = restore_ctx->reg[reg];
+        ctx->reg[ctx->reg_sp][reg] = restore_ctx->reg[ctx->reg_sp][reg];
          if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_restore_extended\n");
          break;
@@ -2847,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_CFAValOff;
-         ctx->reg[reg].arg = off * ctx->data_a_f;
+         ctx->reg[ctx->reg_sp][reg].tag = RR_CFAValOff;
+         ctx->reg[ctx->reg_sp][reg].arg = off * ctx->data_a_f;
          if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_val_offset\n");
          break;
@@ -2860,8 +2889,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].arg = off * ctx->data_a_f;
+         ctx->reg[ctx->reg_sp][reg].tag = RR_CFAValOff;
+         ctx->reg[ctx->reg_sp][reg].arg = off * ctx->data_a_f;
          if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_val_offset_sf\n");
          break;
@@ -2906,8 +2935,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_Undef;
-         ctx->reg[reg].arg = 0;
+         ctx->reg[ctx->reg_sp][reg].tag = RR_Undef;
+         ctx->reg[ctx->reg_sp][reg].arg = 0;
          if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_undefined\n");
          break;
@@ -2917,8 +2946,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_Same;
-         ctx->reg[reg].arg = 0;
+         ctx->reg[ctx->reg_sp][reg].tag = RR_Same;
+         ctx->reg[ctx->reg_sp][reg].arg = 0;
          if (di->ddump_frames)
             VG_(printf)("  rci:DW_CFA_same_value\n");
          break;
@@ -2962,8 +2991,8 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
             return 0; /* fail */
          /* Add an extra dereference */
          j = ML_(CfiExpr_Deref)( ctx->exprs, j );
-         ctx->reg[reg].tag = RR_ValExpr;
-         ctx->reg[reg].arg = j;
+         ctx->reg[ctx->reg_sp][reg].tag = RR_ValExpr;
+         ctx->reg[ctx->reg_sp][reg].arg = j;
          break;
 
       case DW_CFA_val_expression:
@@ -2991,8 +3020,8 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          }
          if (j == -1)
             return 0; /* fail */
-         ctx->reg[reg].tag = RR_ValExpr;
-         ctx->reg[reg].arg = j;
+         ctx->reg[ctx->reg_sp][reg].tag = RR_ValExpr;
+         ctx->reg[ctx->reg_sp][reg].arg = j;
          break;
 
       case DW_CFA_def_cfa_expression:
@@ -3018,7 +3047,39 @@ static Int run_CF_instruction ( /*MOD*/UnwindContext* ctx,
          /* Ignored.  This appears to be sparc-specific; quite why it
             turns up in SuSE-supplied x86 .so's beats me. */
          if (di->ddump_frames)
-            VG_(printf)("DW_CFA_GNU_window_save\n");
+            VG_(printf)("  DW_CFA_GNU_window_save\n");
+         break;
+
+      case DW_CFA_remember_state:
+         if (di->ddump_frames)
+            VG_(printf)("  DW_CFA_remember_state\n");
+         /* we just checked this at entry, so: */
+         vg_assert(ctx->reg_sp >= 0 && ctx->reg_sp < N_RR_STACK);
+         ctx->reg_sp++;
+         if (ctx->reg_sp == N_RR_STACK) {
+            /* stack overflow.  We're hosed. */
+            VG_(message)(Vg_DebugMsg, "DWARF2 CFI reader: N_RR_STACK is "
+                                      "too low; increase and recompile.");
+            i = 0; /* indicate failure */
+         } else {
+            VG_(memcpy)(/*dst*/&ctx->reg[ctx->reg_sp],
+                        /*src*/&ctx->reg[ctx->reg_sp - 1],
+                        sizeof(ctx->reg[ctx->reg_sp]) );
+         }
+         break;
+
+      case DW_CFA_restore_state:
+         if (di->ddump_frames)
+            VG_(printf)("  DW_CFA_restore_state\n");
+         /* we just checked this at entry, so: */
+         vg_assert(ctx->reg_sp >= 0 && ctx->reg_sp < N_RR_STACK);
+         if (ctx->reg_sp == 0) {
+            /* stack overflow.  Give up. */
+            i = 0; /* indicate failure */
+         } else {
+            /* simply fall back to previous entry */
+            ctx->reg_sp--;
+         }
          break;
 
       default: