Author: sewardj
Date: Mon Jan 13 00:21:09 2014
New Revision: 13774
Log:
Initial implementation of CFI based stack unwinding for arm64-linux.
Modified:
trunk/coregrind/m_debuginfo/debuginfo.c
trunk/coregrind/m_debuginfo/priv_storage.h
trunk/coregrind/m_debuginfo/readdwarf.c
trunk/coregrind/m_debuginfo/storage.c
trunk/coregrind/m_stacktrace.c
trunk/coregrind/pub_core_basics.h
trunk/coregrind/pub_core_debuginfo.h
Modified: trunk/coregrind/m_debuginfo/debuginfo.c
==============================================================================
--- trunk/coregrind/m_debuginfo/debuginfo.c (original)
+++ trunk/coregrind/m_debuginfo/debuginfo.c Mon Jan 13 00:21:09 2014
@@ -2115,7 +2115,7 @@
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 @@
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 @@
{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 @@
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 @@
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
Modified: trunk/coregrind/m_debuginfo/priv_storage.h
==============================================================================
--- trunk/coregrind/m_debuginfo/priv_storage.h (original)
+++ trunk/coregrind/m_debuginfo/priv_storage.h Mon Jan 13 00:21:09 2014
@@ -133,7 +133,7 @@
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 @@
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 @@
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 @@
#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 @@
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 @@
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 @@
Creg_ARM_R12,
Creg_ARM_R15,
Creg_ARM_R14,
+ Creg_ARM64_X30,
Creg_S390_R14,
Creg_MIPS_RA
}
Modified: trunk/coregrind/m_debuginfo/readdwarf.c
==============================================================================
--- trunk/coregrind/m_debuginfo/readdwarf.c (original)
+++ trunk/coregrind/m_debuginfo/readdwarf.c Mon Jan 13 00:21:09 2014
@@ -1840,11 +1840,11 @@
#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 @@
# 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 @@
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 @@
static void initCfiSI ( DiCfSI* si )
{
- VG_(memset)(si, 0, sizeof(*si));
+ VG_(bzero_inline)(si, sizeof(*si));
}
@@ -2184,7 +2191,7 @@
# 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 @@
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 @@
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 @@
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 @@
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 @@
# error "Unknown arch"
# endif
+ /* --- non-specialised code after this point --- */
+
# undef SUMMARISE_HOW
failed:
Modified: trunk/coregrind/m_debuginfo/storage.c
==============================================================================
--- trunk/coregrind/m_debuginfo/storage.c (original)
+++ trunk/coregrind/m_debuginfo/storage.c Mon Jan 13 00:21:09 2014
@@ -162,6 +162,12 @@
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 @@
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 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);
}
}
Modified: trunk/coregrind/m_stacktrace.c
==============================================================================
--- trunk/coregrind/m_stacktrace.c (original)
+++ trunk/coregrind/m_stacktrace.c Mon Jan 13 00:21:09 2014
@@ -1044,10 +1044,88 @@
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
Modified: trunk/coregrind/pub_core_basics.h
==============================================================================
--- trunk/coregrind/pub_core_basics.h (original)
+++ trunk/coregrind/pub_core_basics.h Mon Jan 13 00:21:09 2014
@@ -111,7 +111,6 @@
UInt r7;
} ARM;
struct {
- // FIXME ARM64 is this correct?
ULong x29; /* FP */
ULong x30; /* LR */
} ARM64;
Modified: trunk/coregrind/pub_core_debuginfo.h
==============================================================================
--- trunk/coregrind/pub_core_debuginfo.h (original)
+++ trunk/coregrind/pub_core_debuginfo.h Mon Jan 13 00:21:09 2014
@@ -114,7 +114,7 @@
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
|