]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Initial implementation of CFI based stack unwinding for arm64-linux.
authorJulian Seward <jseward@acm.org>
Mon, 13 Jan 2014 00:21:09 +0000 (00:21 +0000)
committerJulian Seward <jseward@acm.org>
Mon, 13 Jan 2014 00:21:09 +0000 (00:21 +0000)
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13774

coregrind/m_debuginfo/debuginfo.c
coregrind/m_debuginfo/priv_storage.h
coregrind/m_debuginfo/readdwarf.c
coregrind/m_debuginfo/storage.c
coregrind/m_stacktrace.c
coregrind/pub_core_basics.h
coregrind/pub_core_debuginfo.h

index 8a48929d97c36668924b48a78e021005c9386cb1..bc8418dad4ee50a4e0f2e75354d2ba6edd9f10ac 100644 (file)
@@ -2115,7 +2115,7 @@ UWord evalCfiExpr ( XArray* exprs, Int ix,
             case Creg_MIPS_RA: return eec->uregs->ra;
 #           elif defined(VGA_ppc32) || defined(VGA_ppc64)
 #           elif defined(VGP_arm64_linux)
-            I_die_here;
+            case Creg_ARM64_X30: return eec->uregs->x30;
 #           else
 #             error "Unsupported arch"
 #           endif
@@ -2361,7 +2361,12 @@ static Addr compute_cfa ( D3UnwindRegs* uregs,
          break;
 #     elif defined(VGA_ppc32) || defined(VGA_ppc64)
 #     elif defined(VGP_arm64_linux)
-      I_die_here;
+      case CFIC_ARM64_SPREL: 
+         cfa = cfsi->cfa_off + uregs->sp;
+         break;
+      case CFIC_ARM64_X29REL: 
+         cfa = cfsi->cfa_off + uregs->x29;
+         break;
 #     else
 #       error "Unsupported arch"
 #     endif
@@ -2437,6 +2442,8 @@ Addr ML_(get_CFA) ( Addr ip, Addr sp, Addr fp,
    {E,R}SP, {E,R}BP.
 
    For arm, the unwound registers are: R7 R11 R12 R13 R14 R15.
+
+   For arm64, the unwound registers are: X29(FP) X30(LR) SP PC.
 */
 Bool VG_(use_CF_info) ( /*MOD*/D3UnwindRegs* uregsHere,
                         Addr min_accessible,
@@ -2459,7 +2466,7 @@ Bool VG_(use_CF_info) ( /*MOD*/D3UnwindRegs* uregsHere,
    ipHere = uregsHere->pc;
 #  elif defined(VGA_ppc32) || defined(VGA_ppc64)
 #  elif defined(VGP_arm64_linux)
-   I_die_here;
+   ipHere = uregsHere->pc;
 #  else
 #    error "Unknown arch"
 #  endif
@@ -2541,7 +2548,10 @@ Bool VG_(use_CF_info) ( /*MOD*/D3UnwindRegs* uregsHere,
    COMPUTE(uregsPrev.fp, uregsHere->fp, cfsi->fp_how, cfsi->fp_off);
 #  elif defined(VGA_ppc32) || defined(VGA_ppc64)
 #  elif defined(VGP_arm64_linux)
-   I_die_here;
+   COMPUTE(uregsPrev.pc,  uregsHere->pc,  cfsi->ra_how,  cfsi->ra_off);
+   COMPUTE(uregsPrev.sp,  uregsHere->sp,  cfsi->sp_how,  cfsi->sp_off);
+   COMPUTE(uregsPrev.x30, uregsHere->x30, cfsi->x30_how, cfsi->x30_off);
+   COMPUTE(uregsPrev.x29, uregsHere->x29, cfsi->x29_how, cfsi->x29_off);
 #  else
 #    error "Unknown arch"
 #  endif
index 2012043b2636d89ac99fcd002dd0ef27fa83d98c..200949d55f148bb94e84aff92773a19e733b9fd3 100644 (file)
@@ -133,7 +133,7 @@ typedef
      cfa = case cfa_how of
               CFIC_IA_SPREL -> {e,r}sp + cfa_off
               CFIC_IA_BPREL -> {e,r}bp + cfa_off
-              CFIR_IA_EXPR  -> expr whose index is in cfa_off
+              CFIC_EXPR     -> expr whose index is in cfa_off
 
    Once that is done, the previous frame's {e,r}sp/{e,r}bp values and
    this frame's {e,r}ra value can be calculated like this:
@@ -150,11 +150,11 @@ typedef
    keep track of:
 
      cfa = case cfa_how of
-              CFIC_R13REL -> r13 + cfa_off
-              CFIC_R12REL -> r12 + cfa_off
-              CFIC_R11REL -> r11 + cfa_off
-              CFIC_R7REL  -> r7  + cfa_off
-              CFIR_EXPR   -> expr whose index is in cfa_off
+              CFIC_ARM_R13REL -> r13 + cfa_off
+              CFIC_ARM_R12REL -> r12 + cfa_off
+              CFIC_ARM_R11REL -> r11 + cfa_off
+              CFIC_ARM_R7REL  -> r7  + cfa_off
+              CFIR_EXPR       -> expr whose index is in cfa_off
 
      old_r14/r13/r12/r11/r7/ra
          = case r14/r13/r12/r11/r7/ra_how of
@@ -164,13 +164,28 @@ typedef
               CFIR_MEMCFAREL -> *( cfa + r14/r13/r12/r11/r7/ra_off )
               CFIR_EXPR      -> expr whose index is in r14/r13/r12/r11/r7/ra_off
 
+   On ARM64:
+
+     cfa = case cfa_how of
+              CFIC_ARM64_SPREL  -> sp + cfa_off
+              CFIC_ARM64_X29REL -> x29 + cfa_off
+              CFIC_EXPR         -> expr whose index is in cfa_off
+
+     old_sp/x30/x29/ra
+         = case sp/x30/x29/ra_how of
+              CFIR_UNKNOWN   -> we don't know, sorry
+              CFIR_SAME      -> same as it was before
+              CFIR_CFAREL    -> cfa + sp/x30/x29/ra_how
+              CFIR_MEMCFAREL -> *( cfa + sp/x30/x29/ra_how )
+              CFIR_EXPR      -> expr whose index is in sp/x30/x29/ra_off
+
    On s390x we have a similar logic as x86 or amd64. We need the stack pointer
    (r15), the frame pointer r11 (like BP) and together with the instruction
    address in the PSW we can calculate the previous values:
      cfa = case cfa_how of
               CFIC_IA_SPREL -> r15 + cfa_off
               CFIC_IA_BPREL -> r11 + cfa_off
-              CFIR_IA_EXPR  -> expr whose index is in cfa_off
+              CFIC_EXPR     -> expr whose index is in cfa_off
 
      old_sp/fp/ra
          = case sp/fp/ra_how of
@@ -183,12 +198,13 @@ typedef
 
 #define CFIC_IA_SPREL     ((UChar)1)
 #define CFIC_IA_BPREL     ((UChar)2)
-#define CFIC_IA_EXPR      ((UChar)3)
-#define CFIC_ARM_R13REL   ((UChar)4)
-#define CFIC_ARM_R12REL   ((UChar)5)
-#define CFIC_ARM_R11REL   ((UChar)6)
-#define CFIC_ARM_R7REL    ((UChar)7)
-#define CFIC_EXPR         ((UChar)8)  /* all targets */
+#define CFIC_ARM_R13REL   ((UChar)3)
+#define CFIC_ARM_R12REL   ((UChar)4)
+#define CFIC_ARM_R11REL   ((UChar)5)
+#define CFIC_ARM_R7REL    ((UChar)6)
+#define CFIC_ARM64_SPREL  ((UChar)7)
+#define CFIC_ARM64_X29REL ((UChar)8)
+#define CFIC_EXPR         ((UChar)9)  /* all targets */
 
 #define CFIR_UNKNOWN      ((UChar)64)
 #define CFIR_SAME         ((UChar)65)
@@ -232,6 +248,23 @@ typedef
       Int   r7_off;
    }
    DiCfSI;
+#elif defined(VGA_arm64)
+typedef
+   struct {
+      Addr  base;
+      UInt  len;
+      UChar cfa_how; /* a CFIC_ value */
+      UChar ra_how;  /* a CFIR_ value */
+      UChar sp_how;  /* a CFIR_ value */ /*dw31=SP*/
+      UChar x30_how; /* a CFIR_ value */ /*dw30=LR*/
+      UChar x29_how; /* a CFIR_ value */ /*dw29=FP*/
+      Int   cfa_off;
+      Int   ra_off;
+      Int   sp_off;
+      Int   x30_off;
+      Int   x29_off;
+   }
+   DiCfSI;
 #elif defined(VGA_ppc32) || defined(VGA_ppc64)
 /* Just have a struct with the common fields in, so that code that
    processes the common fields doesn't have to be ifdef'd against
@@ -277,18 +310,6 @@ typedef
       Int   fp_off;
    }
    DiCfSI;
-#elif defined(VGA_arm64)
-/* Be generic until we know more about what's needed. */
-typedef
-   struct {
-      Addr  base;
-      UInt  len;
-      UChar cfa_how; /* a CFIC_ value */
-      UChar ra_how;  /* a CFIR_ value */
-      Int   cfa_off;
-      Int   ra_off;
-   }
-   DiCfSI;
 #else
 #  error "Unknown arch"
 #endif
@@ -328,6 +349,7 @@ typedef
       Creg_ARM_R12,
       Creg_ARM_R15,
       Creg_ARM_R14,
+      Creg_ARM64_X30,
       Creg_S390_R14,
       Creg_MIPS_RA
    }
index df4bf376c0a33f33d396437cd38744d4e739f4fe..1036856532137fe3bb578e163a17b6c2d882d49d 100644 (file)
@@ -1840,11 +1840,11 @@ void ML_(read_debuginfo_dwarf1) (
 #elif defined(VGP_arm_linux)
 #  define FP_REG         12
 #  define SP_REG         13
-#  define RA_REG_DEFAULT 14    //???
+#  define RA_REG_DEFAULT 14
 #elif defined(VGP_arm64_linux)
-#  define FP_REG         29    //???
-#  define SP_REG         31    //???
-#  define RA_REG_DEFAULT 30    //???
+#  define FP_REG         29
+#  define SP_REG         31
+#  define RA_REG_DEFAULT 30
 #elif defined(VGP_x86_darwin)
 #  define FP_REG         5
 #  define SP_REG         4
@@ -1878,6 +1878,8 @@ void ML_(read_debuginfo_dwarf1) (
 # define N_CFI_REGS 72
 #elif defined(VGP_arm_linux)
 # define N_CFI_REGS 320
+#elif defined(VGP_arm64_linux)
+# define N_CFI_REGS 128
 #else
 # define N_CFI_REGS 20
 #endif
@@ -2100,6 +2102,11 @@ static void initUnwindContext ( /*OUT*/UnwindContext* ctx )
       ctx->state[j].reg[12].tag = RR_Same;
       ctx->state[j].reg[7].tag  = RR_Same;
       /* this can't be right though: R12 (IP) isn't callee saved. */
+#     elif defined(VGA_arm64)
+      /* Callee-saved registers (that we are interested in) should
+         start out as RR_Same. */
+      ctx->state[j].reg[29/*FP*/].tag = RR_Same;
+      ctx->state[j].reg[30/*LR*/].tag = RR_Same;
 #     endif
    }
 }
@@ -2121,7 +2128,7 @@ typedef
 
 static void initCfiSI ( DiCfSI* si )
 {
-   VG_(memset)(si, 0, sizeof(*si));
+   VG_(bzero_inline)(si, sizeof(*si));
 }
 
 
@@ -2184,7 +2191,7 @@ static Bool summarise_context( /*OUT*/DiCfSI* si,
 #     elif defined(VGA_arm)
       si->cfa_how = CFIC_ARM_R13REL;
 #     elif defined(VGA_arm64)
-      I_die_here;
+      si->cfa_how = CFIC_ARM64_SPREL;
 #     else
       si->cfa_how = 0; /* invalid */
 #     endif
@@ -2197,6 +2204,8 @@ static Bool summarise_context( /*OUT*/DiCfSI* si,
       si->cfa_how = CFIC_IA_BPREL;
 #     elif defined(VGA_arm)
       si->cfa_how = CFIC_ARM_R12REL;
+#     elif defined(VGA_arm64)
+      si->cfa_how = CFIC_ARM64_X29REL;
 #     else
       si->cfa_how = 0; /* invalid */
 #     endif
@@ -2213,7 +2222,7 @@ static Bool summarise_context( /*OUT*/DiCfSI* si,
       si->cfa_off = ctxs->cfa_off;
    }
 #  elif defined(VGA_arm64)
-   if (1) { I_die_here; } // do we need any arm64 specifics here?
+   // do we need any arm64 specifics here?
 #  endif
    else {
       why = 1;
@@ -2348,6 +2357,48 @@ static Bool summarise_context( /*OUT*/DiCfSI* si,
 
    return True;
 
+#  elif defined(VGA_arm64)
+
+   /* --- entire tail of this fn specialised for arm64 --- */
+
+   SUMMARISE_HOW(si->x30_how, si->x30_off, ctxs->reg[30/*LR*/]);
+   SUMMARISE_HOW(si->x29_how, si->x29_off, ctxs->reg[29/*FP*/]);
+
+   if (ctxs->reg[30/*LR*/].tag == RR_Same
+       && ctx->ra_reg == 30/*as we expect it always to be*/) {
+      /* Generate a trivial CfiExpr, which merely says "x30".  First
+         ensure this DebugInfo has a cfsi_expr array in which to park
+         it. */
+      if (!debuginfo->cfsi_exprs)
+         debuginfo->cfsi_exprs = VG_(newXA)( ML_(dinfo_zalloc),
+                                             "di.ccCt.2a-arm64",
+                                             ML_(dinfo_free),
+                                             sizeof(CfiExpr) );
+      si->ra_off = ML_(CfiExpr_CfiReg)( debuginfo->cfsi_exprs,
+                                        Creg_ARM64_X30);
+      si->ra_how = CFIR_EXPR;
+   } else {
+      /* Just summarise it in the normal way */
+      SUMMARISE_HOW(si->ra_how, si->ra_off, ctxs->reg[ctx->ra_reg]);
+   }
+
+   /* on arm64, it seems the old SP value before the call is always
+      the same as the CFA.  Therefore ... */
+   si->sp_how = CFIR_CFAREL;
+   si->sp_off = 0;
+
+   /* bogus looking range?  Note, we require that the difference is
+      representable in 32 bits. */
+   if (loc_start >= ctx->loc) 
+      { why = 4; goto failed; }
+   if (ctx->loc - loc_start > 10000000 /* let's say */)
+      { why = 5; goto failed; }
+
+   si->base = loc_start + ctx->initloc;
+   si->len  = (UInt)(ctx->loc - loc_start);
+
+   return True;
+
 #  elif defined(VGA_s390x)
 
    /* --- entire tail of this fn specialised for s390 --- */
@@ -2440,9 +2491,6 @@ static Bool summarise_context( /*OUT*/DiCfSI* si,
 
    return True;
 
-#  elif defined(VGA_arm64)
-   I_die_here;
-
 #  elif defined(VGA_ppc32) || defined(VGA_ppc64)
    /* These don't use CFI based unwinding (is that really true?) */
 
@@ -2450,6 +2498,8 @@ static Bool summarise_context( /*OUT*/DiCfSI* si,
 #    error "Unknown arch"
 #  endif
 
+   /* --- non-specialised code after this point --- */
+
 #  undef SUMMARISE_HOW
 
   failed:
index 5ffa7386885ef84b166462abda2297ab4f8e079d..0a4716fff421ba9e34df24f506024f80e7e6d0dc 100644 (file)
@@ -162,6 +162,12 @@ void ML_(ppDiCfSI) ( XArray* /* of CfiExpr */ exprs, DiCfSI* si )
       case CFIC_ARM_R7REL: 
          VG_(printf)("let cfa=oldR7+%d", si->cfa_off); 
          break;
+      case CFIC_ARM64_SPREL: 
+         VG_(printf)("let cfa=oldSP+%d", si->cfa_off); 
+         break;
+      case CFIC_ARM64_X29REL: 
+         VG_(printf)("let cfa=oldX29+%d", si->cfa_off); 
+         break;
       case CFIC_EXPR: 
          VG_(printf)("let cfa={"); 
          ML_(ppCfiExpr)(exprs, si->cfa_off);
@@ -196,7 +202,12 @@ void ML_(ppDiCfSI) ( XArray* /* of CfiExpr */ exprs, DiCfSI* si )
    VG_(printf)(" FP=");
    SHOW_HOW(si->fp_how, si->fp_off);
 #  elif defined(VGA_arm64)
-   I_die_here;
+   VG_(printf)(" SP=");
+   SHOW_HOW(si->sp_how, si->sp_off);
+   VG_(printf)(" X30=");
+   SHOW_HOW(si->x30_how, si->x30_off);
+   VG_(printf)(" X29=");
+   SHOW_HOW(si->x29_how, si->x29_off);
 #  else
 #    error "Unknown arch"
 #  endif
@@ -671,15 +682,16 @@ static void ppCfiBinop ( CfiBinop op )
 static void ppCfiReg ( CfiReg reg )
 {
    switch (reg) {
-      case Creg_IA_SP:   VG_(printf)("xSP"); break;
-      case Creg_IA_BP:   VG_(printf)("xBP"); break;
-      case Creg_IA_IP:   VG_(printf)("xIP"); break;
-      case Creg_ARM_R13: VG_(printf)("R13"); break;
-      case Creg_ARM_R12: VG_(printf)("R12"); break;
-      case Creg_ARM_R15: VG_(printf)("R15"); break;
-      case Creg_ARM_R14: VG_(printf)("R14"); break;
-      case Creg_MIPS_RA: VG_(printf)("RA"); break;
-      case Creg_S390_R14: VG_(printf)("R14"); break;
+      case Creg_IA_SP:     VG_(printf)("xSP"); break;
+      case Creg_IA_BP:     VG_(printf)("xBP"); break;
+      case Creg_IA_IP:     VG_(printf)("xIP"); break;
+      case Creg_ARM_R13:   VG_(printf)("R13"); break;
+      case Creg_ARM_R12:   VG_(printf)("R12"); break;
+      case Creg_ARM_R15:   VG_(printf)("R15"); break;
+      case Creg_ARM_R14:   VG_(printf)("R14"); break;
+      case Creg_ARM64_X30: VG_(printf)("X30"); break;
+      case Creg_MIPS_RA:   VG_(printf)("RA"); break;
+      case Creg_S390_R14:  VG_(printf)("R14"); break;
       default: vg_assert(0);
    }
 }
index f998f4c4ae40fa0df57a14e57fdcf793f617dbbf..6f2428963b5aca3fe6a3ff4adbe6c4f6af33d344 100644 (file)
@@ -1044,10 +1044,88 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
                                UnwindStartRegs* startRegs,
                                Addr fp_max_orig )
 {
-   ips[0] = startRegs->r_pc;
-   if (sps) sps[0] = startRegs->r_sp;
-   if (fps) fps[0] = startRegs->misc.ARM64.x29;
-   return 1;
+   Bool  debug = False;
+   Int   i;
+   Addr  fp_max;
+   UInt  n_found = 0;
+   const Int cmrf = VG_(clo_merge_recursive_frames);
+
+   vg_assert(sizeof(Addr) == sizeof(UWord));
+   vg_assert(sizeof(Addr) == sizeof(void*));
+
+   D3UnwindRegs uregs;
+   uregs.pc = startRegs->r_pc;
+   uregs.sp = startRegs->r_sp;
+   uregs.x30 = startRegs->misc.ARM64.x30;
+   uregs.x29 = startRegs->misc.ARM64.x29;
+   Addr fp_min = uregs.sp;
+
+   /* Snaffle IPs from the client's stack into ips[0 .. max_n_ips-1],
+      stopping when the trail goes cold, which we guess to be
+      when FP is not a reasonable stack location. */
+
+   // JRS 2002-sep-17: hack, to round up fp_max to the end of the
+   // current page, at least.  Dunno if it helps.
+   // NJN 2002-sep-17: seems to -- stack traces look like 1.0.X again
+   fp_max = VG_PGROUNDUP(fp_max_orig);
+   if (fp_max >= sizeof(Addr))
+      fp_max -= sizeof(Addr);
+
+   if (debug)
+      VG_(printf)("\nmax_n_ips=%d fp_min=0x%lx fp_max_orig=0x%lx, "
+                  "fp_max=0x%lx PC=0x%lx SP=0x%lx\n",
+                  max_n_ips, fp_min, fp_max_orig, fp_max,
+                  uregs.pc, uregs.sp);
+
+   /* Assertion broken before main() is reached in pthreaded programs;  the
+    * offending stack traces only have one item.  --njn, 2002-aug-16 */
+   /* vg_assert(fp_min <= fp_max);*/
+   // On Darwin, this kicks in for pthread-related stack traces, so they're
+   // only 1 entry long which is wrong.
+   if (fp_min + 512 >= fp_max) {
+      /* If the stack limits look bogus, don't poke around ... but
+         don't bomb out either. */
+      if (sps) sps[0] = uregs.sp;
+      if (fps) fps[0] = uregs.x29;
+      ips[0] = uregs.pc;
+      return 1;
+   } 
+
+   /* */
+
+   if (sps) sps[0] = uregs.sp;
+   if (fps) fps[0] = uregs.x29;
+   ips[0] = uregs.pc;
+   i = 1;
+
+   /* Loop unwinding the stack, using CFI. */
+   while (True) {
+      if (debug) {
+         VG_(printf)("i: %d, pc: 0x%lx, sp: 0x%lx\n",
+                     i, uregs.pc, uregs.sp);
+      }
+
+      if (i >= max_n_ips)
+         break;
+
+      if (VG_(use_CF_info)( &uregs, fp_min, fp_max )) {
+         if (sps) sps[i] = uregs.sp;
+         if (fps) fps[i] = uregs.x29;
+         ips[i++] = uregs.pc - 1;
+         if (debug)
+            VG_(printf)("USING CFI: pc: 0x%lx, sp: 0x%lx\n",
+                        uregs.pc, uregs.sp);
+         uregs.pc = uregs.pc - 1;
+         if (UNLIKELY(cmrf > 0)) {RECURSIVE_MERGE(cmrf,ips,i);};
+         continue;
+      }
+
+      /* No luck.  We have to give up. */
+      break;
+   }
+
+   n_found = i;
+   return n_found;
 }
 
 #endif
index 4e50517d159481d54daf5ca86fb621a61545352e..29590153446ca9753d8ea20e5b0d0e1868a04836 100644 (file)
@@ -111,7 +111,6 @@ typedef
             UInt r7;
          } ARM;
          struct {
-            // FIXME ARM64 is this correct?
             ULong x29; /* FP */
             ULong x30; /* LR */
          } ARM64;
index ed78c5c12d48b630b9f8179ad8d625bb94da005a..81298c9aab64e3c8b881939561e6706e4588f9f7 100644 (file)
@@ -114,7 +114,7 @@ typedef
    D3UnwindRegs;
 #elif defined(VGA_arm64)
 typedef
-   struct { Addr pc; Addr sp; Addr lr; Addr fp; } /* PC, 31, 30, 29 */
+   struct { Addr pc; Addr sp; Addr x30; Addr x29; } /* PC, SP, LR, FP */
    D3UnwindRegs;
 #elif defined(VGA_ppc32) || defined(VGA_ppc64)
 typedef