Initial implementation of CFI based stack unwinding for arm64-linux.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13774
This commit is contained in:
Julian Seward 2014-01-13 00:21:09 +00:00
parent 0a13c57c35
commit 68a2a4ce01
7 changed files with 226 additions and 55 deletions

View 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

View 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
}

View 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:

View 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);
}
}

View 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

View File

@ -111,7 +111,6 @@ typedef
UInt r7;
} ARM;
struct {
// FIXME ARM64 is this correct?
ULong x29; /* FP */
ULong x30; /* LR */
} ARM64;

View 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