|
From: <sv...@va...> - 2009-11-13 17:21:18
|
Author: sewardj
Date: 2009-11-13 17:21:04 +0000 (Fri, 13 Nov 2009)
New Revision: 10940
Log:
Make CFI based stack unwinding (apparently) work on ARM. This
completely breaks the amd64 unwinder for the time being, and contains
just as many arm-specific kludges as the amd64 version contains
amd64-specific kludges. This will be tidied up in future commits. It
also doesn't take into account recent work by Jacob Jelink, and will
need rebasing before merging to trunk.
Changes:
* an abstraction level upgrade. In many places to do with unwinding,
we pass around values for the instruction pointer, stack pointer,
frame pointer and link register. Considering that the only fields
common to all platforms are the first two (IP and SP), this rapidly
becomes confusing (eg, what value should we pass for the link
register on amd64? for the frame pointer on arm? does it matter?)
Such ad-hoc-ery is now replaced with a struct UnwindStartRegs, which
holds the specific values needed for unwinding on a given platform.
Much passing around of ip/sp/fp/lr is now replaced by a single
UnwindStartRegs*.
In the places where unwinding is started, we now have to create an
UnwindStartRegs rather than said values.
* Dwarf CFI reading for ARM: it's apparently necessary to
simultaneously unwind r15, r14, r13, r12 and r11. Omitting any of
those can cause the next level of the unwind to fail. The CFI
unwinder and associated data types have been changed to match this
(is platform specific; need to figure out a way to merge this
cleanly with the amd64 unwind stuff).
* ML_(read_callframe_info_dwarf3): handle .debug_frame sections (for
arm-ELF). These are unfortunately slightly different from the
.eh_frame sections used for amd64-ELF.
* coregrind/m_stacktrace.c: split the main function,
VG_(get_StackTrace_wrk) into completely separate instances for the
different targets. Maintaining just one big function is too
unwieldy.
Also (on ARM) remove lengthy machinery for trying to do unwinding by
analysing the machine code. I think that's not necessary because
the CFI mechanism should handle all required cases.
Modified:
branches/ARM/coregrind/m_debuginfo/debuginfo.c
branches/ARM/coregrind/m_debuginfo/priv_readdwarf.h
branches/ARM/coregrind/m_debuginfo/priv_storage.h
branches/ARM/coregrind/m_debuginfo/readdwarf.c
branches/ARM/coregrind/m_debuginfo/readelf.c
branches/ARM/coregrind/m_debuginfo/storage.c
branches/ARM/coregrind/m_libcassert.c
branches/ARM/coregrind/m_machine.c
branches/ARM/coregrind/m_signals.c
branches/ARM/coregrind/m_stacktrace.c
branches/ARM/coregrind/pub_core_basics.h
branches/ARM/coregrind/pub_core_debuginfo.h
branches/ARM/coregrind/pub_core_libcassert.h
branches/ARM/coregrind/pub_core_machine.h
branches/ARM/coregrind/pub_core_stacktrace.h
Modified: branches/ARM/coregrind/m_debuginfo/debuginfo.c
===================================================================
--- branches/ARM/coregrind/m_debuginfo/debuginfo.c 2009-11-12 13:28:34 UTC (rev 10939)
+++ branches/ARM/coregrind/m_debuginfo/debuginfo.c 2009-11-13 17:21:04 UTC (rev 10940)
@@ -1821,9 +1821,10 @@
a CfiExpr into one convenient struct. */
typedef
struct {
- Addr ipHere;
- Addr spHere;
- Addr fpHere;
+ Addr r15Here;
+ Addr r14Here;
+ Addr r13Here;
+ Addr r12Here;
Addr min_accessible;
Addr max_accessible;
}
@@ -1855,9 +1856,10 @@
/*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;
+ case Creg_R15: return (Addr)eec->r15Here;
+ case Creg_R14: return (Addr)eec->r14Here;
+ case Creg_R13: return (Addr)eec->r13Here;
+ case Creg_R12: return (Addr)eec->r12Here;
default: goto unhandled;
}
/*NOTREACHED*/
@@ -2003,22 +2005,26 @@
}
-/* 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. */
+/* The main function for DWARF2/3 CFI-based stack unwinding. Given an
+ R15/R14/R13/R12/R11 set, produce the R15/R14/R13/R12/R11 values for
+ the previous frame, if possible. */
+/* Returns True if OK. If not OK, *{r15,r14,r13,r12,r11}P are not
+ changed. */
/* NOTE: this function may rearrange the order of entries in the
DebugInfo list. */
-Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP,
- /*MOD*/Addr* spP,
- /*MOD*/Addr* fpP,
+Bool VG_(use_CF_info) ( /*MOD*/Addr* r15P,
+ /*MOD*/Addr* r14P,
+ /*MOD*/Addr* r13P,
+ /*MOD*/Addr* r12P,
+ /*MOD*/Addr* r11P,
Addr min_accessible,
Addr max_accessible )
{
Bool ok;
DebugInfo* di;
DiCfSI* cfsi = NULL;
- Addr cfa, ipHere, spHere, fpHere, ipPrev, spPrev, fpPrev;
+ Addr r15Here, r14Here, r13Here, r12Here, r11Here, cfa;
+ Addr r15Prev, r14Prev, r13Prev, r12Prev, r11Prev;
CfiExprEvalContext eec;
@@ -2027,16 +2033,16 @@
if (0 && 0 == (n_q & 0x1FFFFF))
VG_(printf)("QQQ %lu %lu\n", n_q, n_m);
- { UWord hash = (*ipP) % N_CFSI_CACHE;
+ { UWord hash = (*r15P) % N_CFSI_CACHE;
CFSICacheEnt* ce = &cfsi_cache[hash];
- if (LIKELY(ce->ip == *ipP) && LIKELY(ce->di != NULL)) {
+ if (LIKELY(ce->ip == *r15P) && LIKELY(ce->di != NULL)) {
/* found an entry in the cache .. */
} else {
/* not found in cache. Search and update. */
n_m++;
- ce->ip = *ipP;
- find_DiCfSI( &ce->di, &ce->ix, *ipP );
+ ce->ip = *r15P;
+ find_DiCfSI( &ce->di, &ce->ix, *r15P );
}
if (UNLIKELY(ce->di == (DebugInfo*)1)) {
@@ -2058,30 +2064,36 @@
ML_(ppDiCfSI)(di->cfsi_exprs, cfsi);
}
- ipPrev = spPrev = fpPrev = 0;
+ r15Prev = r14Prev = r13Prev = r12Prev = r11Prev = 0;
- ipHere = *ipP;
- spHere = *spP;
- fpHere = *fpP;
+ r15Here = *r15P;
+ r14Here = *r14P;
+ r13Here = *r13P;
+ r12Here = *r12P;
+ r11Here = *r11P;
/* First compute the CFA. */
cfa = 0;
switch (cfsi->cfa_how) {
- case CFIC_SPREL:
- cfa = cfsi->cfa_off + spHere;
+ case CFIC_R13REL:
+ cfa = cfsi->cfa_off + r13Here;
break;
- case CFIC_FPREL:
- cfa = cfsi->cfa_off + fpHere;
+ case CFIC_R12REL:
+ cfa = cfsi->cfa_off + r12Here;
break;
+ case CFIC_R11REL:
+ cfa = cfsi->cfa_off + r11Here;
+ break;
case CFIC_EXPR:
if (0) {
VG_(printf)("CFIC_EXPR: ");
ML_(ppCfiExpr)(di->cfsi_exprs, cfsi->cfa_off);
VG_(printf)("\n");
}
- eec.ipHere = ipHere;
- eec.spHere = spHere;
- eec.fpHere = fpHere;
+ eec.r15Here = r15Here;
+ eec.r14Here = r14Here;
+ eec.r13Here = r13Here;
+ eec.r12Here = r12Here;
eec.min_accessible = min_accessible;
eec.max_accessible = max_accessible;
ok = True;
@@ -2116,9 +2128,10 @@
case CFIR_EXPR: \
if (0) \
ML_(ppCfiExpr)(di->cfsi_exprs,_off); \
- eec.ipHere = ipHere; \
- eec.spHere = spHere; \
- eec.fpHere = fpHere; \
+ eec.r15Here = r15Here; \
+ eec.r14Here = r14Here; \
+ eec.r13Here = r13Here; \
+ eec.r12Here = r12Here; \
eec.min_accessible = min_accessible; \
eec.max_accessible = max_accessible; \
ok = True; \
@@ -2130,15 +2143,19 @@
} \
} while (0)
- COMPUTE(ipPrev, ipHere, cfsi->ra_how, cfsi->ra_off);
- COMPUTE(spPrev, spHere, cfsi->sp_how, cfsi->sp_off);
- COMPUTE(fpPrev, fpHere, cfsi->fp_how, cfsi->fp_off);
+ COMPUTE(r15Prev, r15Here, cfsi->ra_how, cfsi->ra_off);
+ COMPUTE(r14Prev, r14Here, cfsi->r14_how, cfsi->r14_off);
+ COMPUTE(r13Prev, r13Here, cfsi->r13_how, cfsi->r13_off);
+ COMPUTE(r12Prev, r12Here, cfsi->r12_how, cfsi->r12_off);
+ COMPUTE(r11Prev, r11Here, cfsi->r11_how, cfsi->r11_off);
# undef COMPUTE
- *ipP = ipPrev;
- *spP = spPrev;
- *fpP = fpPrev;
+ *r15P = r15Prev;
+ *r14P = r14Prev;
+ *r13P = r13Prev;
+ *r12P = r12Prev;
+ *r11P = r11Prev;
return True;
}
Modified: branches/ARM/coregrind/m_debuginfo/priv_readdwarf.h
===================================================================
--- branches/ARM/coregrind/m_debuginfo/priv_readdwarf.h 2009-11-12 13:28:34 UTC (rev 10939)
+++ branches/ARM/coregrind/m_debuginfo/priv_readdwarf.h 2009-11-13 17:21:04 UTC (rev 10940)
@@ -62,9 +62,10 @@
-------------------- */
extern
void ML_(read_callframe_info_dwarf3)
- ( /*OUT*/struct _DebugInfo* di, UChar* ehframe );
+ ( /*OUT*/struct _DebugInfo* di,
+ HChar* xxframe_name,
+ UChar* xxframe_image, SizeT xxframe_size, Addr xxframe_avma );
-
#endif /* ndef __PRIV_READDWARF_H */
/*--------------------------------------------------------------------*/
Modified: branches/ARM/coregrind/m_debuginfo/priv_storage.h
===================================================================
--- branches/ARM/coregrind/m_debuginfo/priv_storage.h 2009-11-12 13:28:34 UTC (rev 10939)
+++ branches/ARM/coregrind/m_debuginfo/priv_storage.h 2009-11-13 17:21:04 UTC (rev 10940)
@@ -98,39 +98,42 @@
/* --------------------- CF INFO --------------------- */
/* A structure to summarise DWARF2/3 CFA info for the code address
- range [base .. base+len-1]. In short, if you know (sp,fp,ip) at
- some point and ip is in the range [base .. base+len-1], it tells
- you how to calculate (sp,fp) for the caller of the current frame
- and also ra, the return address of the current frame.
+ range [base .. base+len-1]. In short, if you know
+ (r15,r14,r13,r12,r11) at some point and r15 is in the range [base
+ .. base+len-1], it tells you how to calculate (r14,r13,r12,r11) for
+ the caller of the current frame and also ra, the return address of
+ the current frame.
First off, calculate CFA, the Canonical Frame Address, thusly:
cfa = case cfa_how of
- CFIC_SPREL -> sp + cfa_off
- CFIC_FPREL -> fp + cfa_off
- CFIR_EXPR -> expr whose index is in cfa_off
+ CFIC_R13REL -> r13 + cfa_off
+ CFIC_R12REL -> r12 + cfa_off
+ CFIC_R11REL -> r11 + 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:
- old_sp/fp/ra
- = case sp/fp/ra_how of
+ old_r14/r13/r12/r11/ra
+ = case r14/r13/r12/r11/ra_how of
CFIR_UNKNOWN -> we don't know, sorry
- 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
+ CFIR_SAME -> same as it was before (r14/r13/r12/r11 only)
+ CFIR_CFAREL -> cfa + r14/r13/r12/r11/ra_off
+ CFIR_MEMCFAREL -> *( cfa + r14/r13/r12/r11/ra_off )
+ CFIR_EXPR -> expr whose index is in r14/r13/r12/r11/ra_off
*/
-#define CFIC_SPREL ((UChar)1)
-#define CFIC_FPREL ((UChar)2)
-#define CFIC_EXPR ((UChar)3)
+#define CFIC_R13REL ((UChar)1)
+#define CFIC_R12REL ((UChar)2)
+#define CFIC_R11REL ((UChar)3)
+#define CFIC_EXPR ((UChar)4)
-#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)
+#define CFIR_UNKNOWN ((UChar)5)
+#define CFIR_SAME ((UChar)6)
+#define CFIR_CFAREL ((UChar)7)
+#define CFIR_MEMCFAREL ((UChar)8)
+#define CFIR_EXPR ((UChar)9)
typedef
struct {
@@ -138,12 +141,16 @@
UInt len;
UChar cfa_how; /* a CFIC_ value */
UChar ra_how; /* a CFIR_ value */
- UChar sp_how; /* a CFIR_ value */
- UChar fp_how; /* a CFIR_ value */
+ UChar r14_how; /* a CFIR_ value */
+ UChar r13_how; /* a CFIR_ value */
+ UChar r12_how; /* a CFIR_ value */
+ UChar r11_how; /* a CFIR_ value */
Int cfa_off;
Int ra_off;
- Int sp_off;
- Int fp_off;
+ Int r14_off;
+ Int r13_off;
+ Int r12_off;
+ Int r11_off;
}
DiCfSI;
@@ -159,9 +166,10 @@
typedef
enum {
- Creg_SP=0x213,
- Creg_FP,
- Creg_IP
+ Creg_R13=0x213,
+ Creg_R12,
+ Creg_R15,
+ Creg_R14
}
CfiReg;
Modified: branches/ARM/coregrind/m_debuginfo/readdwarf.c
===================================================================
--- branches/ARM/coregrind/m_debuginfo/readdwarf.c 2009-11-12 13:28:34 UTC (rev 10939)
+++ branches/ARM/coregrind/m_debuginfo/readdwarf.c 2009-11-13 17:21:04 UTC (rev 10940)
@@ -1493,9 +1493,26 @@
/*------------------------------------------------------------*/
-/*--- Read call-frame info from an .eh_frame section ---*/
+/*--- Read call-frame info from .{eh,debug}_frame sections ---*/
/*------------------------------------------------------------*/
+/* Note that various variables are called xx_frame. This means they
+ refer to either .eh_frame things or .debug_frame things. Which one
+ is taken care of by the caller of the main routine,
+ ML_(read_callframe_info_dwarf3) and so the rest of the frame
+ reading code does not have to worry about which it is. This also
+ means it's wrong to look at the .eh_frame specific fields in the
+ DebugInfo* which we're passed; and none of the code should do that.
+
+ FIXME: actually we need to know, because .eh_frame is slightly
+ different (and extended) from .debug_frame.
+
+ On amd64-linux, unwind info is in .eh_frame. If we're using it for
+ x86-linux, then it's also in .eh_frame.
+
+ On arm-linux, unwind info is in .debug_frame.
+*/
+
/* Sources of info:
The DWARF3 spec, available from http://www.dwarfstd.org/Download.php
@@ -1783,7 +1800,7 @@
# define SP_REG 1
# define RA_REG_DEFAULT 8 // CAB: What's a good default ?
#elif defined(VGP_arm_linux)
-# define FP_REG 11
+# define FP_REG 12
# define SP_REG 13
# define RA_REG_DEFAULT 14 //???
#elif defined(VGP_x86_darwin)
@@ -2006,10 +2023,18 @@
ctx->state[j].cfa_off = 0;
ctx->state[j].cfa_expr_ix = 0; */
for (i = 0; i < N_CFI_REGS; i++) {
- if (RR_Undef != 0)
- ctx->state[j].reg[i].tag = RR_Undef;
+ ctx->state[j].reg[i].tag = RR_Undef;
/* ctx->state[j].reg[i].arg = 0; */
}
+// all callee-saved registers (or at least the ones we are summarising for)
+// should start out as RR_Same, on arm
+ctx->state[j].reg[11].tag = RR_Same;
+
+//ctx->state[j].reg[13].tag = RR_Same;
+
+ctx->state[j].reg[14].tag = RR_Same;
+ ctx->state[j].reg[12].tag = RR_Same; // this can't be right though.
+ // R12 (IP) isn't callee saved.
}
}
@@ -2019,8 +2044,8 @@
typedef
struct {
UChar encoding;
- UChar* ehframe_image;
- Addr ehframe_avma;
+ UChar* xxframe_image;
+ Addr xxframe_avma;
Addr text_bias;
}
AddressDecodingInfo;
@@ -2034,12 +2059,16 @@
si->len = 0;
si->cfa_how = 0;
si->ra_how = 0;
- si->sp_how = 0;
- si->fp_how = 0;
+ si->r14_how = 0;
+ si->r13_how = 0;
+ si->r12_how = 0;
+ si->r11_how = 0;
si->cfa_off = 0;
si->ra_off = 0;
- si->sp_off = 0;
- si->fp_off = 0;
+ si->r14_off = 0;
+ si->r13_off = 0;
+ si->r12_off = 0;
+ si->r11_off = 0;
}
@@ -2094,12 +2123,16 @@
ML_(ppCfiExpr)(dst, conv);
} else
if (ctxs->cfa_is_regoff && ctxs->cfa_reg == SP_REG) {
- si->cfa_how = CFIC_SPREL;
+ si->cfa_how = CFIC_R13REL;
si->cfa_off = ctxs->cfa_off;
} else
if (ctxs->cfa_is_regoff && ctxs->cfa_reg == FP_REG) {
- si->cfa_how = CFIC_FPREL;
+ si->cfa_how = CFIC_R12REL;
si->cfa_off = ctxs->cfa_off;
+ } else
+ if (ctxs->cfa_is_regoff && ctxs->cfa_reg == 11/*??_REG*/) {
+ si->cfa_how = CFIC_R11REL;
+ si->cfa_off = ctxs->cfa_off;
} else {
why = 1;
goto failed;
@@ -2142,26 +2175,51 @@
why = 2; goto failed; /* otherwise give up */ \
}
- SUMMARISE_HOW(si->ra_how, si->ra_off,
- ctxs->reg[ctx->ra_reg] );
- SUMMARISE_HOW(si->fp_how, si->fp_off,
- ctxs->reg[FP_REG] );
+ SUMMARISE_HOW(si->r14_how, si->r14_off,
+ ctxs->reg[14] );
+ //SUMMARISE_HOW(si->r13_how, si->r13_off,
+ // ctxs->reg[13] );
+
+ SUMMARISE_HOW(si->r12_how, si->r12_off,
+ ctxs->reg[FP_REG] );
+
+ SUMMARISE_HOW(si->r11_how, si->r11_off,
+ ctxs->reg[11/*FP_REG*/] );
+
+ if (ctxs->reg[14/*LR*/].tag == RR_Same
+ && ctx->ra_reg == 14/*as we expect it always to be*/) {
+ /* Generate a trivial CfiExpr, which merely says "r14". 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",
+ ML_(dinfo_free),
+ sizeof(CfiExpr) );
+ si->ra_off = ML_(CfiExpr_CfiReg)( debuginfo->cfsi_exprs, Creg_R14);
+ 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] );
+ }
+
# undef SUMMARISE_HOW
/* on x86/amd64, it seems the old %{e,r}sp value before the call is
always the same as the CFA. Therefore ... */
- si->sp_how = CFIR_CFAREL;
- si->sp_off = 0;
+ si->r13_how = CFIR_CFAREL;
+ si->r13_off = 0;
/* also, gcc says "Undef" for %{e,r}bp when it is unchanged. So
.. */
- if (ctxs->reg[FP_REG].tag == RR_Undef)
- si->fp_how = CFIR_SAME;
+ //if (ctxs->reg[FP_REG].tag == RR_Undef)
+ // si->r12_how = CFIR_SAME;
/* knock out some obviously stupid cases */
- if (si->ra_how == CFIR_SAME)
- { why = 3; goto failed; }
+ //if (si->ra_how == CFIR_SAME)
+ // { why = 3; goto failed; }
/* bogus looking range? Note, we require that the difference is
representable in 32 bits. */
@@ -2228,11 +2286,11 @@
/* 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 );
+ return ML_(CfiExpr_CfiReg)( dstxa, Creg_R13 );
if (dwreg == FP_REG)
- return ML_(CfiExpr_CfiReg)( dstxa, Creg_FP );
+ return ML_(CfiExpr_CfiReg)( dstxa, Creg_R12 );
if (dwreg == srcuc->ra_reg)
- return ML_(CfiExpr_CfiReg)( dstxa, Creg_IP ); /* correct? */
+ return ML_(CfiExpr_CfiReg)( dstxa, Creg_R15 ); /* correct? */
/* else we must fail - can't represent the reg */
return -1;
default:
@@ -2422,8 +2480,8 @@
Addr base;
Word offset;
UChar encoding = adi->encoding;
- UChar* ehframe_image = adi->ehframe_image;
- Addr ehframe_avma = adi->ehframe_avma;
+ UChar* xxframe_image = adi->xxframe_image;
+ Addr xxframe_avma = adi->xxframe_avma;
vg_assert((encoding & DW_EH_PE_indirect) == 0);
@@ -2434,7 +2492,7 @@
base = adi->text_bias;
break;
case DW_EH_PE_pcrel:
- base = ehframe_avma + ( data - ehframe_image );
+ base = xxframe_avma + ( data - xxframe_image );
break;
case DW_EH_PE_datarel:
vg_assert(0);
@@ -2449,7 +2507,7 @@
break;
case DW_EH_PE_aligned:
base = 0;
- offset = data - ehframe_image;
+ offset = data - xxframe_image;
if ((offset % sizeof(Addr)) != 0) {
*nbytes = sizeof(Addr) - (offset % sizeof(Addr));
data += *nbytes;
@@ -3450,23 +3508,57 @@
void ML_(read_callframe_info_dwarf3)
- ( /*OUT*/struct _DebugInfo* di, UChar* ehframe_image )
+ ( /*OUT*/struct _DebugInfo* di,
+ HChar* xxframe_name,
+ UChar* xxframe_image, SizeT xxframe_size, Addr xxframe_avma )
{
Int nbytes;
HChar* how = NULL;
Int n_CIEs = 0;
- UChar* data = ehframe_image;
-# if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
- /* These targets don't use CFI-based stack unwinding. */
- return;
+ /* Read Dwarf3 CFI unwind data from the section described by
+ xxframe_{image,size,avma}. In practice that means either
+ .eh_frame or .debug_frame. It appears that .eh_frame is
+ sometimes mapped, but .debug_frame never is. Hence for
+ .eh_frame, xxframe_avma can be nonzero, but for .debug_frame it
+ must be zero.
+
+ A consequence of this is that, inside this CFI reader, we must
+ not longer refer directly to any di->ehframe_ or di->debugframe_
+ fields in the reader, after this point. */
+
+ /* On amd64-linux and x86-linux, we read .eh_frame and ignore
+ .debug_frame. On arm-linux, we read .debug_frame and read
+ .eh_frame. We could loosen these restrictions so as to allow
+ reading from both sections, if required. For the time being,
+ though, just use one or the other. FIXME: is this correct for
+ Darwin too? */
+# if defined(VGP_amd64_linux) || defined(VGP_x86_linux) \
+ || defined(VGP_x86_darwin) || defined(VGP_amd64_darwin)
+ if (0 != VG_(strcmp)(xxframe_name, ".eh_frame"))
+ return; /* skip anything except .eh_frame */
+
+# elif defined(VGP_arm_linux)
+ if (0 != VG_(strcmp)(xxframe_name, ".debug_frame"))
+ return; /* skip anything except .debug_frame */
+ vg_assert(xxframe_avma == 0);
+
+# elif defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
+ return; /* these don't use CFI-based stack unwinding */
+
+# else
+ /* new platform; need to make a decision about it. */
+# error "Unsupported platform"
# endif
+ /* Right. Finally, we're all set up to read the CFI described by
+ xxframe_{image,size,avma}. */
+ UChar* data = xxframe_image;
+
if (di->trace_cfi) {
VG_(printf)("\n-----------------------------------------------\n");
VG_(printf)("CFI info: szB %ld, _avma %#lx, _image %p\n",
- di->ehframe_size, di->ehframe_avma,
- ehframe_image );
+ xxframe_size, xxframe_avma, xxframe_image );
VG_(printf)("CFI info: name %s\n",
di->filename );
}
@@ -3499,12 +3591,12 @@
Bool dw64;
/* Are we done? */
- if (data == ehframe_image + di->ehframe_size)
+ if (data == xxframe_image + xxframe_size)
return;
/* Overshot the end? Means something is wrong */
- if (data > ehframe_image + di->ehframe_size) {
- how = "overran the end of .eh_frame";
+ if (data > xxframe_image + xxframe_size) {
+ how = "overran the end of .eh_frame or .debug_frame";
goto bad;
}
@@ -3513,9 +3605,9 @@
ciefde_start = data;
if (di->trace_cfi)
- VG_(printf)("\ncie/fde.start = %p (ehframe_image + 0x%lx)\n",
+ VG_(printf)("\ncie/fde.start = %p (xxframe_image + 0x%lx)\n",
ciefde_start,
- ciefde_start - ehframe_image + 0UL);
+ ciefde_start - xxframe_image + 0UL);
ciefde_len = (ULong) read_UInt(data); data += sizeof(UInt);
if (di->trace_cfi)
@@ -3528,7 +3620,7 @@
if (ciefde_len == 0) {
if (di->ddump_frames)
VG_(printf)("%08lx ZERO terminator\n\n",
- ((Addr)ciefde_start) - ((Addr)ehframe_image));
+ ((Addr)ciefde_start) - ((Addr)xxframe_image));
return;
}
@@ -3555,7 +3647,8 @@
VG_(printf)("cie.pointer = %lld\n", cie_pointer);
/* If cie_pointer is zero, we've got a CIE; else it's an FDE. */
- if (cie_pointer == 0) {
+ if (//cie_pointer == 0 || // XXX eh_frame
+ cie_pointer == 0xFFFFFFFFULL) { //XXX debug_frame
Int this_CIE;
UChar cie_version;
@@ -3577,13 +3670,14 @@
n_CIEs++;
init_CIE( &the_CIEs[this_CIE] );
- /* Record its offset. This is how we will find it again
- later when looking at an FDE. */
- the_CIEs[this_CIE].offset = (ULong)(ciefde_start - ehframe_image);
+ /* Record its offset relative to the start of the image.
+ This is how we will find it again later when looking at an
+ FDE. */
+ the_CIEs[this_CIE].offset = (ULong)(ciefde_start - xxframe_image);
if (di->ddump_frames)
VG_(printf)("%08lx %08lx %08lx CIE\n",
- ((Addr)ciefde_start) - ((Addr)ehframe_image),
+ ((Addr)ciefde_start) - ((Addr)xxframe_image),
(Addr)ciefde_len,
(Addr)(UWord)cie_pointer );
@@ -3706,7 +3800,7 @@
}
if (the_CIEs[this_CIE].ilen < 0
- || the_CIEs[this_CIE].ilen > di->ehframe_size) {
+ || the_CIEs[this_CIE].ilen > xxframe_size) {
how = "implausible # cie initial insns";
goto bad;
}
@@ -3721,8 +3815,8 @@
if (di->trace_cfi || di->ddump_frames) {
AddressDecodingInfo adi;
adi.encoding = the_CIEs[this_CIE].address_encoding;
- adi.ehframe_image = ehframe_image;
- adi.ehframe_avma = di->ehframe_avma;
+ adi.xxframe_image = xxframe_image;
+ adi.xxframe_avma = xxframe_avma;
adi.text_bias = di->text_debug_bias;
show_CF_instructions( the_CIEs[this_CIE].instrs,
the_CIEs[this_CIE].ilen, &adi,
@@ -3750,10 +3844,13 @@
/* Find the relevant CIE. The CIE we want is located
cie_pointer bytes back from here. */
+ // XXX eh_frame
/* re sizeof(UInt) / sizeof(ULong), matches XXX above. */
- look_for = (data - (dw64 ? sizeof(ULong) : sizeof(UInt))
- - ehframe_image)
- - cie_pointer;
+ // look_for = (data - (dw64 ? sizeof(ULong) : sizeof(UInt))
+ // - xxframe_image)
+ // - cie_pointer;
+ // XXX debug_frame
+ look_for = cie_pointer;
for (cie = 0; cie < n_CIEs; cie++) {
if (0) VG_(printf)("look for %lld %lld\n",
@@ -3768,8 +3865,8 @@
}
adi.encoding = the_CIEs[cie].address_encoding;
- adi.ehframe_image = ehframe_image;
- adi.ehframe_avma = di->ehframe_avma;
+ adi.xxframe_image = xxframe_image;
+ adi.xxframe_avma = xxframe_avma;
adi.text_bias = di->text_debug_bias;
fde_initloc = read_encoded_Addr(&nbytes, &adi, data);
data += nbytes;
@@ -3777,8 +3874,8 @@
VG_(printf)("fde.initloc = %#lx\n", fde_initloc);
adi.encoding = the_CIEs[cie].address_encoding & 0xf;
- adi.ehframe_image = ehframe_image;
- adi.ehframe_avma = di->ehframe_avma;
+ adi.xxframe_image = xxframe_image;
+ adi.xxframe_avma = xxframe_avma;
adi.text_bias = di->text_debug_bias;
/* WAS (incorrectly):
@@ -3804,7 +3901,7 @@
if (di->ddump_frames)
VG_(printf)("%08lx %08lx %08lx FDE cie=%08lx pc=%08lx..%08lx\n",
- ((Addr)ciefde_start) - ((Addr)ehframe_image),
+ ((Addr)ciefde_start) - ((Addr)xxframe_image),
(Addr)ciefde_len,
(Addr)(UWord)cie_pointer,
(Addr)look_for,
@@ -3831,7 +3928,7 @@
VG_(printf)("fde.ilen = %d\n", (Int)fde_ilen);
}
- if (fde_ilen < 0 || fde_ilen > di->ehframe_size) {
+ if (fde_ilen < 0 || fde_ilen > xxframe_size) {
how = "implausible # fde insns";
goto bad;
}
@@ -3839,8 +3936,8 @@
data += fde_ilen;
adi.encoding = the_CIEs[cie].address_encoding;
- adi.ehframe_image = ehframe_image;
- adi.ehframe_avma = di->ehframe_avma;
+ adi.xxframe_image = xxframe_image;
+ adi.xxframe_avma = xxframe_avma;
adi.text_bias = di->text_debug_bias;
if (di->trace_cfi)
Modified: branches/ARM/coregrind/m_debuginfo/readelf.c
===================================================================
--- branches/ARM/coregrind/m_debuginfo/readelf.c 2009-11-12 13:28:34 UTC (rev 10939)
+++ branches/ARM/coregrind/m_debuginfo/readelf.c 2009-11-13 17:21:04 UTC (rev 10940)
@@ -1657,7 +1657,7 @@
}
}
-# undef BAD
+# undef BAD
}
@@ -2002,11 +2002,21 @@
False, opd_img);
}
- /* Read .eh_frame (call-frame-info) if any */
+ /* Read call-frame-info in .eh_frame and/or .debug_frame,
+ if any */
if (ehframe_img) {
vg_assert(ehframe_sz == di->ehframe_size);
- ML_(read_callframe_info_dwarf3)( di, ehframe_img );
+ ML_(read_callframe_info_dwarf3)(
+ di, ".eh_frame",
+ ehframe_img, ehframe_sz, di->ehframe_avma
+ );
}
+ if (debug_frame_img) {
+ ML_(read_callframe_info_dwarf3)(
+ di, ".debug_frame",
+ debug_frame_img, debug_frame_sz, 0/*AVMA*/
+ );
+ }
/* Read the stabs and/or dwarf2 debug information, if any. It
appears reading stabs stuff on amd64-linux doesn't work, so
Modified: branches/ARM/coregrind/m_debuginfo/storage.c
===================================================================
--- branches/ARM/coregrind/m_debuginfo/storage.c 2009-11-12 13:28:34 UTC (rev 10939)
+++ branches/ARM/coregrind/m_debuginfo/storage.c 2009-11-13 17:21:04 UTC (rev 10940)
@@ -126,12 +126,15 @@
VG_(printf)("[%#lx .. %#lx]: ", si->base,
si->base + (UWord)si->len - 1);
switch (si->cfa_how) {
- case CFIC_SPREL:
- VG_(printf)("let cfa=oldSP+%d", si->cfa_off);
+ case CFIC_R13REL:
+ VG_(printf)("let cfa=oldR13+%d", si->cfa_off);
break;
- case CFIC_FPREL:
- VG_(printf)("let cfa=oldFP+%d", si->cfa_off);
+ case CFIC_R12REL:
+ VG_(printf)("let cfa=oldR12+%d", si->cfa_off);
break;
+ case CFIC_R11REL:
+ VG_(printf)("let cfa=oldR11+%d", si->cfa_off);
+ break;
case CFIC_EXPR:
VG_(printf)("let cfa={");
ML_(ppCfiExpr)(exprs, si->cfa_off);
@@ -143,10 +146,14 @@
VG_(printf)(" in RA=");
SHOW_HOW(si->ra_how, si->ra_off);
- VG_(printf)(" SP=");
- SHOW_HOW(si->sp_how, si->sp_off);
- VG_(printf)(" FP=");
- SHOW_HOW(si->fp_how, si->fp_off);
+ VG_(printf)(" R14=");
+ SHOW_HOW(si->r14_how, si->r14_off);
+ VG_(printf)(" R13=");
+ SHOW_HOW(si->r13_how, si->r13_off);
+ VG_(printf)(" R12=");
+ SHOW_HOW(si->r12_how, si->r12_off);
+ VG_(printf)(" R11=");
+ SHOW_HOW(si->r11_how, si->r11_off);
VG_(printf)("\n");
# undef SHOW_HOW
}
@@ -574,9 +581,10 @@
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;
+ case Creg_R13: VG_(printf)("R13"); break;
+ case Creg_R12: VG_(printf)("R12"); break;
+ case Creg_R15: VG_(printf)("R15"); break;
+ case Creg_R14: VG_(printf)("R14"); break;
default: vg_assert(0);
}
}
Modified: branches/ARM/coregrind/m_libcassert.c
===================================================================
--- branches/ARM/coregrind/m_libcassert.c 2009-11-12 13:28:34 UTC (rev 10939)
+++ branches/ARM/coregrind/m_libcassert.c 2009-11-13 17:21:04 UTC (rev 10940)
@@ -45,59 +45,94 @@
Assertery.
------------------------------------------------------------------ */
-#if defined(VGP_x86_linux) || defined(VGP_x86_darwin)
-# define GET_REAL_PC_SP_AND_FP(pc, sp, fp) \
- asm("call 0f;" \
- "0: popl %0;" \
- "movl %%esp, %1;" \
- "movl %%ebp, %2;" \
- : "=r" (pc),\
- "=r" (sp),\
- "=r" (fp));
-#elif defined(VGP_amd64_linux) || defined(VGP_amd64_darwin)
-# define GET_REAL_PC_SP_AND_FP(pc, sp, fp) \
- asm("leaq 0(%%rip), %0;" \
- "movq %%rsp, %1;" \
- "movq %%rbp, %2;" \
- : "=r" (pc),\
- "=r" (sp),\
- "=r" (fp));
+#if defined(VGP_x86_linux) || defined(VGP_x86_darwin)
+# define GET_STARTREGS(srP) \
+ { UInt eip, esp, ebp; \
+ __asm__ __volatile__( \
+ "call 0f;" \
+ "0: popl %0;" \
+ "movl %%esp, %1;" \
+ "movl %%ebp, %2;" \
+ : "=r" (eip), "=r" (esp), "=r" (ebp) \
+ : /* reads none */ \
+ : "memory" \
+ ); \
+ srP->r_pc = (ULong)eip; \
+ srP->r_sp = (ULong)esp; \
+ srP->misc.X86.r_ebp = ebp; \
+ }
+#elif defined(VGP_amd64_linux) || defined(VGP_amd64_darwin)
+# define GET_STARTREGS(srP) \
+ { ULong rip, rsp, rbp; \
+ __asm__ __volatile__( \
+ "leaq 0(%%rip), %0;" \
+ "movq %%rsp, %1;" \
+ "movq %%rbp, %2;" \
+ : "=r" (rip), "=r" (rsp), "=r" (rbp) \
+ : /* reads none */ \
+ : "memory" \
+ ); \
+ srP->r_pc = rip; \
+ srP->r_sp = rsp; \
+ srP->misc.AMD64.r_rbp = rbp; \
+ }
#elif defined(VGP_ppc32_linux) || defined(VGP_ppc32_aix5)
-# define GET_REAL_PC_SP_AND_FP(pc, sp, fp) \
- asm("mflr 0;" /* r0 = lr */ \
- "bl m_libcassert_get_ip;" /* lr = pc */ \
- "m_libcassert_get_ip:\n" \
- "mflr %0;" \
- "mtlr 0;" /* restore lr */ \
- "mr %1,1;" \
- "mr %2,1;" \
- : "=r" (pc), \
- "=r" (sp), \
- "=r" (fp) \
- : /* reads none */ \
- : "r0" /* trashed */ );
+# define GET_STARTREGS(srP) \
+ { UInt cia, r1, lr; \
+ __asm__ __volatile__( \
+ "mflr 0;" /* r0 = lr */ \
+ "bl m_libcassert_get_ip;" /* lr = pc */ \
+ "m_libcassert_get_ip:\n" \
+ "mflr %0;" /* %0 = pc */ \
+ "mtlr 0;" /* restore lr */ \
+ "mr %1,1;" /* %1 = r1 */ \
+ "mr %2,0;" /* %2 = lr */ \
+ : "=r" (cia), "=r" (r1), "=r" (lr) \
+ : /* reads none */ \
+ : "r0" /* trashed */ \
+ ); \
+ srP->r_pc = (ULong)cia; \
+ srP->r_sp = (ULong)r1; \
+ srP->misc.PPC32.lr = lr; \
+ }
#elif defined(VGP_ppc64_linux) || defined(VGP_ppc64_aix5)
-# define GET_REAL_PC_SP_AND_FP(pc, sp, fp) \
- asm("mflr 0;" /* r0 = lr */ \
- "bl .m_libcassert_get_ip;" /* lr = pc */ \
- ".m_libcassert_get_ip:\n" \
- "mflr %0;" \
- "mtlr 0;" /* restore lr */ \
- "mr %1,1;" \
- "mr %2,1;" \
- : "=r" (pc), \
- "=r" (sp), \
- "=r" (fp) \
- : /* reads none */ \
- : "r0" /* trashed */ );
+# define GET_STARTREGS(srP) \
+ { ULong cia, r1, lr; \
+ __asm__ __volatile__( \
+ "mflr 0;" /* r0 = lr */ \
+ "bl .m_libcassert_get_ip;" /* lr = pc */ \
+ ".m_libcassert_get_ip:\n" \
+ "mflr %0;" /* %0 = pc */ \
+ "mtlr 0;" /* restore lr */ \
+ "mr %1,1;" /* %1 = r1 */ \
+ "mr %2,0;" /* %2 = lr */ \
+ : "=r" (cia), "=r" (r1), "=r" (lr) \
+ : /* reads none */ \
+ : "r0" /* trashed */ \
+ ); \
+ srP->r_pc = cia; \
+ srP->r_sp = r1; \
+ srP->misc.PPC64.lr = lr; \
+ }
#elif defined(VGP_arm_linux)
-# define GET_REAL_PC_SP_AND_FP(pc, sp, fp) \
- asm("mov %0, pc;" \
- "mov %1, sp;" \
- "mov %2, fp;" \
- : "=r" (pc), \
- "=r" (sp), \
- "=r" (fp));
+# define GET_STARTREGS(srP) \
+ { UInt block[5]; \
+ __asm__ __volatile__( \
+ "str r15, [%0, #+0];" \
+ "str r14, [%0, #+4];" \
+ "str r13, [%0, #+8];" \
+ "str r12, [%0, #+12];" \
+ "str r11, [%0, #+16];" \
+ : /* out */ \
+ : /* in */ "r"(&block[0]) \
+ : /* trash */ "memory" \
+ ); \
+ (srP)->r_pc = block[0] - 8; \
+ (srP)->r_sp = block[1]; \
+ (srP)->misc.ARM.r14 = block[2]; \
+ (srP)->misc.ARM.r12 = block[3]; \
+ (srP)->misc.ARM.r11 = block[4]; \
+ }
#else
# error Unknown platform
#endif
@@ -137,8 +172,8 @@
}
__attribute__ ((noreturn))
-static void report_and_quit ( const Char* report,
- Addr ip, Addr sp, Addr fp, Addr lr )
+static void report_and_quit ( const Char* report,
+ UnwindStartRegs* startRegsIN )
{
Addr stacktop;
Addr ips[BACKTRACE_DEPTH];
@@ -149,8 +184,13 @@
// If necessary, fake up an ExeContext which is of our actual real CPU
// state. Could cause problems if we got the panic/exception within the
// execontext/stack dump/symtab code. But it's better than nothing.
- if (0 == ip && 0 == sp && 0 == fp) {
- GET_REAL_PC_SP_AND_FP(ip, sp, fp);
+ UnwindStartRegs startRegs;
+ VG_(memset)(&startRegs, 0, sizeof(startRegs));
+
+ if (startRegsIN == NULL) {
+ GET_STARTREGS(&startRegs);
+ } else {
+ startRegs = *startRegsIN;
}
stacktop = tst->os_state.valgrind_stack_init_SP;
@@ -161,7 +201,7 @@
ips, BACKTRACE_DEPTH,
NULL/*array to dump SP values in*/,
NULL/*array to dump FP values in*/,
- ip, sp, fp, lr, sp, stacktop
+ &startRegs, stacktop
);
VG_(clo_xml) = False;
VG_(pp_StackTrace) (ips, n_ips);
@@ -222,32 +262,32 @@
if (!VG_STREQ(buf, ""))
VG_(printf)("%s: %s\n", component, buf );
- report_and_quit(bugs_to, 0,0,0,0);
+ report_and_quit(bugs_to, NULL);
}
__attribute__ ((noreturn))
static void panic ( Char* name, Char* report, Char* str,
- Addr ip, Addr sp, Addr fp, Addr lr )
+ UnwindStartRegs* startRegs )
{
if (VG_(clo_xml))
VG_(printf_xml)("</valgrindoutput>\n");
VG_(printf)("\n%s: the 'impossible' happened:\n %s\n", name, str);
- report_and_quit(report, ip, sp, fp, lr);
+ report_and_quit(report, startRegs);
}
-void VG_(core_panic_at) ( Char* str, Addr ip, Addr sp, Addr fp, Addr lr )
+void VG_(core_panic_at) ( Char* str, UnwindStartRegs* startRegs )
{
- panic("valgrind", VG_BUGS_TO, str, ip, sp, fp, lr);
+ panic("valgrind", VG_BUGS_TO, str, startRegs);
}
void VG_(core_panic) ( Char* str )
{
- VG_(core_panic_at)(str, 0,0,0,0);
+ VG_(core_panic_at)(str, NULL);
}
void VG_(tool_panic) ( Char* str )
{
- panic(VG_(details).name, VG_(details).bug_reports_to, str, 0,0,0,0);
+ panic(VG_(details).name, VG_(details).bug_reports_to, str, NULL);
}
/* Print some helpful-ish text about unimplemented things, and give up. */
Modified: branches/ARM/coregrind/m_machine.c
===================================================================
--- branches/ARM/coregrind/m_machine.c 2009-11-12 13:28:34 UTC (rev 10939)
+++ branches/ARM/coregrind/m_machine.c 2009-11-13 17:21:04 UTC (rev 10940)
@@ -81,6 +81,45 @@
INSTR_PTR( VG_(threads)[tid].arch ) = ip;
}
+
+void VG_(get_UnwindStartRegs) ( /*OUT*/UnwindStartRegs* regs,
+ ThreadId tid )
+{
+# if defined(VGA_x86)
+ regs->r_pc = (ULong)VG_(threads)[tid].arch.vex.guest_EIP;
+ regs->r_sp = (ULong)VG_(threads)[tid].arch.vex.guest_ESP;
+ regs->misc.X86.r_ebp
+ = VG_(threads)[tid].arch.vex.guest_EBP;
+# elif defined(VGA_amd64)
+ regs->r_pc = VG_(threads)[tid].arch.vex.guest_RIP;
+ regs->r_pc = VG_(threads)[tid].arch.vex.guest_RSP;
+ regs->misc.AMD64.r_rbp
+ = VG_(threads)[tid].arch.vex.guest_RBP;
+# elif defined(VGA_ppc32)
+ regs->r_pc = (ULong)VG_(threads)[tid].arch.vex.guest_CIA;
+ regs->r_sp = (ULong)VG_(threads)[tid].arch.vex.guest_R1;
+ regs->misc.PPC32.r_lr
+ = VG_(threads)[tid].arch.vex.guest_LR;
+# elif defined(VGA_ppc64)
+ regs->r_pc = VG_(threads)[tid].arch.vex.guest_CIA;
+ regs->r_sp = VG_(threads)[tid].arch.vex.guest_R1;
+ regs->misc.PPC64.r_lr
+ = VG_(threads)[tid].arch.vex.guest_LR;
+# elif defined(VGA_arm)
+ regs->r_pc = (ULong)VG_(threads)[tid].arch.vex.guest_R15;
+ regs->r_sp = (ULong)VG_(threads)[tid].arch.vex.guest_R13;
+ regs->misc.ARM.r14
+ = VG_(threads)[tid].arch.vex.guest_R14;
+ regs->misc.ARM.r12
+ = VG_(threads)[tid].arch.vex.guest_R12;
+ regs->misc.ARM.r11
+ = VG_(threads)[tid].arch.vex.guest_R11;
+# else
+# error "Unknown arch"
+# endif
+}
+
+
void VG_(set_syscall_return_shadows) ( ThreadId tid,
/* shadow vals for the result */
UWord s1res, UWord s2res,
Modified: branches/ARM/coregrind/m_signals.c
===================================================================
--- branches/ARM/coregrind/m_signals.c 2009-11-12 13:28:34 UTC (rev 10939)
+++ branches/ARM/coregrind/m_signals.c 2009-11-13 17:21:04 UTC (rev 10940)
@@ -360,11 +360,16 @@
#elif defined(VGP_arm_linux)
# define VG_UCONTEXT_INSTR_PTR(uc) ((uc)->uc_mcontext.arm_pc)
# define VG_UCONTEXT_STACK_PTR(uc) ((uc)->uc_mcontext.arm_sp)
-# define VG_UCONTEXT_FRAME_PTR(uc) ((uc)->uc_mcontext.arm_fp)
# define VG_UCONTEXT_SYSCALL_SYSRES(uc) \
/* Convert the value in uc_mcontext.rax into a SysRes. */ \
VG_(mk_SysRes_arm_linux)( (uc)->uc_mcontext.arm_r0 )
-# define VG_UCONTEXT_LINK_REG(uc) ((uc)->uc_mcontext.arm_lr)
+# define VG_UCONTEXT_TO_UnwindStartRegs(srP, uc) \
+ { (srP)->r_pc = (uc)->uc_mcontext.arm_pc; \
+ (srP)->r_sp = (uc)->uc_mcontext.arm_sp; \
+ (srP)->misc.ARM.r14 = (uc)->uc_mcontext.arm_lr; \
+ (srP)->misc.ARM.r12 = (uc)->uc_mcontext.arm_ip; \
+ (srP)->misc.ARM.r11 = (uc)->uc_mcontext.arm_fp; \
+ }
#elif defined(VGP_ppc32_aix5)
@@ -2312,11 +2317,11 @@
// tid = VG_(master_tid);
vg_assert(tid != 0);
- VG_(core_panic_at)("Killed by fatal signal",
- VG_UCONTEXT_INSTR_PTR(uc),
- VG_UCONTEXT_STACK_PTR(uc),
- VG_UCONTEXT_FRAME_PTR(uc),
- VG_UCONTEXT_LINK_REG(uc));
+ UnwindStartRegs startRegs;
+ VG_(memset)(&startRegs, 0, sizeof(startRegs));
+
+ VG_UCONTEXT_TO_UnwindStartRegs(&startRegs, uc);
+ VG_(core_panic_at)("Killed by fatal signal", &startRegs);
}
}
Modified: branches/ARM/coregrind/m_stacktrace.c
===================================================================
--- branches/ARM/coregrind/m_stacktrace.c 2009-11-12 13:28:34 UTC (rev 10939)
+++ branches/ARM/coregrind/m_stacktrace.c 2009-11-13 17:21:04 UTC (rev 10940)
@@ -44,8 +44,11 @@
#include "pub_core_clientstate.h" // VG_(client__dl_sysinfo_int80)
#include "pub_core_trampoline.h"
+
/*------------------------------------------------------------*/
-/*--- Exported functions. ---*/
+/*--- ---*/
+/*--- BEGIN platform-dependent unwinder worker functions ---*/
+/*--- ---*/
/*------------------------------------------------------------*/
/* Take a snapshot of the client's stack, putting up to 'max_n_ips'
@@ -57,23 +60,17 @@
first parameter, else send zero. This helps generate better stack
traces on ppc64-linux and has no effect on other platforms.
*/
+
+/* ------------------------ x86 ------------------------- */
+
+#if defined(VGP_x86_linux) || defined(VGP_x86_darwin)
+
UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
/*OUT*/Addr* ips, UInt max_n_ips,
/*OUT*/Addr* sps, /*OUT*/Addr* fps,
Addr ip, Addr sp, Addr fp, Addr lr,
Addr fp_min, Addr fp_max_orig )
{
-# if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux) \
- || defined(VGP_ppc32_aix5) \
- || defined(VGP_ppc64_aix5)
- Bool lr_is_first_RA = False;
-# endif
-# if defined(VGP_ppc64_linux) || defined(VGP_ppc64_aix5) \
- || defined(VGP_ppc32_aix5)
- Word redir_stack_size = 0;
- Word redirs_used = 0;
-# endif
-
Bool debug = False;
Int i;
Addr fp_max;
@@ -103,7 +100,7 @@
/* 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 !defined(VGO_darwin)
+# if !defined(VGO_darwin)
if (fp_min + 512 >= fp_max) {
/* If the stack limits look bogus, don't poke around ... but
don't bomb out either. */
@@ -112,17 +109,8 @@
ips[0] = ip;
return 1;
}
-#endif
+# endif
- /* Otherwise unwind the stack in a platform-specific way. Trying
- to merge the x86, amd64, ppc32 and ppc64 logic into a single
- piece of code is just too confusing and difficult to
- performance-tune. */
-
-# if defined(VGP_x86_linux) || defined(VGP_x86_darwin)
-
- /*--------------------- x86 ---------------------*/
-
/* fp is %ebp. sp is %esp. ip is %eip. */
if (sps) sps[0] = sp;
@@ -213,10 +201,62 @@
break;
}
-# elif defined(VGP_amd64_linux) || defined(VGP_amd64_darwin)
+ n_found = i;
+ return n_found;
+}
- /*--------------------- amd64 ---------------------*/
+#endif
+/* ----------------------- amd64 ------------------------ */
+
+#if defined(VGP_amd64_linux) || defined(VGP_amd64_darwin)
+
+UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
+ /*OUT*/Addr* ips, UInt max_n_ips,
+ /*OUT*/Addr* sps, /*OUT*/Addr* fps,
+ Addr ip, Addr sp, Addr fp, Addr lr,
+ Addr fp_min, Addr fp_max_orig )
+{
+ Bool debug = False;
+ Int i;
+ Addr fp_max;
+ UInt n_found = 0;
+
+ vg_assert(sizeof(Addr) == sizeof(UWord));
+ vg_assert(sizeof(Addr) == sizeof(void*));
+
+ /* 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)("max_n_ips=%d fp_min=0x%lx fp_max_orig=0x%lx, "
+ "fp_max=0x%lx ip=0x%lx fp=0x%lx\n",
+ max_n_ips, fp_min, fp_max_orig, fp_max, ip, fp);
+
+ /* 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 !defined(VGO_darwin)
+ 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] = sp;
+ if (fps) fps[0] = fp;
+ ips[0] = ip;
+ return 1;
+ }
+# endif
+
/* fp is %rbp. sp is %rsp. ip is %rip. */
ips[0] = ip;
@@ -319,11 +359,65 @@
break;
}
-# elif defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux) \
- || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5)
+ n_found = i;
+ return n_found;
+}
- /*--------------------- ppc32/64 ---------------------*/
+#endif
+/* -----------------------ppc32/64 ---------------------- */
+
+#if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux) \
+ || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5)
+
+UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
+ /*OUT*/Addr* ips, UInt max_n_ips,
+ /*OUT*/Addr* sps, /*OUT*/Addr* fps,
+ Addr ip, Addr sp, Addr fp, Addr lr,
+ Addr fp_min, Addr fp_max_orig )
+{
+ Bool lr_is_first_RA = False;
+# if defined(VG_PLAT_USES_PPCTOC)
+ Word redir_stack_size = 0;
+ Word redirs_used = 0;
+# endif
+
+ Bool debug = False;
+ Int i;
+ Addr fp_max;
+ UInt n_found = 0;
+
+ vg_assert(sizeof(Addr) == sizeof(UWord));
+ vg_assert(sizeof(Addr) == sizeof(void*));
+
+ /* 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)("max_n_ips=%d fp_min=0x%lx fp_max_orig=0x%lx, "
+ "fp_max=0x%lx ip=0x%lx fp=0x%lx\n",
+ max_n_ips, fp_min, fp_max_orig, fp_max, ip, fp);
+
+ /* 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);*/
+ 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] = sp;
+ if (fps) fps[0] = fp;
+ ips[0] = ip;
+ return 1;
+ }
+
/* fp is %r1. ip is %cia. Note, ppc uses r1 as both the stack and
frame pointers. */
@@ -386,8 +480,7 @@
/* On ppc64-linux (ppc64-elf, really), and on AIX, the lr save
slot is 2 words back from sp, whereas on ppc32-elf(?) it's
only one word back. */
-# if defined(VGP_ppc64_linux) \
- || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5)
+# if defined(VG_PLAT_USES_PPCTOC)
const Int lr_offset = 2;
# else
const Int lr_offset = 1;
@@ -447,304 +540,124 @@
}
}
+ n_found = i;
+ return n_found;
+}
-# elif defined(VGP_arm_linux)
- if (sps) sps[0] = sp;
- if (fps) fps[0] = fp;
- ips[0] = ip;
- i = 1;
+#endif
+/* ------------------------ arm ------------------------- */
- while (True) {
- Addr prologue;
- Addr scanaddr;
- UInt *idx;
- Int sp_delta = 0; //Offset to old SP from SP, used to recover SP for most functions
+#if defined(VGP_arm_linux)
- //Set if we spot LR being pushed on the stack
- Bool tracking_lr = False;
- Int lr_sp_delta = 0; // <input sp - lr_sp_delta = lr>, only valid if tracking_lr is true
+UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
+ /*OUT*/Addr* ips, UInt max_n_ips,
+ /*OUT*/Addr* sps, /*OUT*/Addr* fps,
+ UnwindStartRegs* startRegs,
+ Addr fp_max_orig )
+{
+ Bool debug = False;
+ Int i;
+ Addr fp_max;
+ UInt n_found = 0;
- //Set if we spot FP being pushed on the stack.
- Bool tracking_fp = False;
- Int fp_sp_delta = 0; //
+ vg_assert(sizeof(Addr) == sizeof(UWord));
+ vg_assert(sizeof(Addr) == sizeof(void*));
- //We also save offsets from FP
- //We use this if we can, as variadic functions will fail with SP method.
- Int fp_lr_offset = 0; // Offset to LR from FP.
- Int fp_sp_offset = 0; // Offset to old SP from FP.
- Int fp_fp_offset = 0; // Offset to old FP from FP.
+ Addr r15 = startRegs->r_pc;
+ Addr r13 = startRegs->r_sp;
+ Addr r14 = startRegs->misc.ARM.r14;
+ Addr r12 = startRegs->misc.ARM.r12;
+ Addr r11 = startRegs->misc.ARM.r11;
+ Addr fp_min = r13;
- Bool fp_modified = False; //did anything in the prologue write to FP?
+ /* 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. */
- /* This one is pretty dirty
- * It seems libc has a tendency of not using standard prologues at all, when it is about to execute a syscall.
- * In many cases following LR is enough, but we also need to know if the stack was modified.
- *
- * If we detect we are trying to generate a stacktrace from a syscall AND we didn't find any "standard" prologue,
- * try instead to search backwards from IP, to locate any operations that modify SP (and pick up LR,FP if we find them)
- *
- * Not being able to do stacktraces from syscalls decreases the usefulness of memcheck quite a bit, so we go to length to
- * hack around it. (gdb does not)
- *
- */
- Bool reverse = 0;
- Bool svc_hack = 0;
+ // 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)("i: %d, ip: 0x%x, sp: 0x%x, fp: 0x%x\n",i,(unsigned int)ip,(unsigned int)sp,(unsigned int)fp);
- }
+ if (debug)
+ VG_(printf)("max_n_ips=%d fp_min=0x%lx fp_max_orig=0x%lx, "
+ "fp_max=0x%lx r15=0x%lx r13=0x%lx\n",
+ max_n_ips, fp_min, fp_max_orig, fp_max, r15, r13);
- if (i >= max_n_ips)
- break;
+ /* 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] = r13;
+ if (fps) fps[0] = 0;
+ ips[0] = r15;
+ return 1;
+ }
- prologue = 0; //VG_(get_fnaddr)(ip);
- if(prologue != 0){
- scanaddr = prologue;
- /* Stack unwinding on ARM is EXTREMELY gnarley. We scan
- * through the function looking for stuff that affects the stack
- * pointer in order to figure out what the frame looks like. A lot of this
- * has been taken from GDB and then hacked up. We really only care about
- * the EABI calling convention here (feel free to support more ^_^) which is
- *
- * push {..., lr}
- * ...
- * sub sp, sp, #??
- *
- * for functions of fixed arity, and
- *
- * xxx
- * xxx
- * xxx
- *
- * for variadic functions. To make sure we don't slam into the next function,
- * we only scan the range between the beginning of the function and the point at
- * which we called the child, since in order to call the child, the prologue must
- * have executed to make sure the function can get back. If this is the lowest frame,
- * we do the same only between [the function's entry, the current pc)
- */
+ /* */
-//See comment above regarding retry, 'reverse' and svc_hack
- if(*(UInt*)(ip-4) == 0xEF000000) {
- svc_hack = True;
- }
-retry:
+ if (sps) sps[0] = r13;
+ if (fps) fps[0] = 0;
+ ips[0] = r15;
+ i = 1;
- for(idx=(UInt *)scanaddr; idx<(UInt *)ip ;(reverse ? idx-- : idx++)){
- UInt insn = *idx;
+ /* Loop unwinding the stack. */
- if(svc_hack && idx < (UInt*)prologue) {
- break;
- }
- if(0)
- VG_(printf)("Decoding 0x%x, insn: 0x%x\n",(UInt)idx,insn);
- if (insn == 0xe52de004 || (i == 1 && (insn & 0xe52d0004) == 0xe52d0004) ) /* str ??, [sp, #-4]! a.k.a. push {one register} */
- {
- if(tracking_lr) {
- lr_sp_delta+=4;
- }
- if(tracking_fp)
- fp_sp_delta+=4;
-
- if(insn == 0xe52de004) { /* str lr, [sp, #-4]! */
- tracking_lr = True;
- if(debug)
- VG_(printf)("Backtrace: str lr,[sp, #-4]!\n");
- }
- if(insn == 0xe52db004) { /* str fp, [sp, #-4]! */
- tracking_fp = True;
- if(de...
[truncated message content] |
|
From: Tom H. <to...@co...> - 2009-11-13 17:28:10
|
On 13/11/09 17:21, sv...@va... wrote: > * an abstraction level upgrade. In many places to do with unwinding, > we pass around values for the instruction pointer, stack pointer, > frame pointer and link register. Considering that the only fields > common to all platforms are the first two (IP and SP), this rapidly > becomes confusing (eg, what value should we pass for the link > register on amd64? for the frame pointer on arm? does it matter?) > > Such ad-hoc-ery is now replaced with a struct UnwindStartRegs, which > holds the specific values needed for unwinding on a given platform. > Much passing around of ip/sp/fp/lr is now replaced by a single > UnwindStartRegs*. > > In the places where unwinding is started, we now have to create an > UnwindStartRegs rather than said values. That's certainly good, and I suspect may help with getting it working with PPC as well. > * Dwarf CFI reading for ARM: it's apparently necessary to > simultaneously unwind r15, r14, r13, r12 and r11. Omitting any of > those can cause the next level of the unwind to fail. The CFI > unwinder and associated data types have been changed to match this > (is platform specific; need to figure out a way to merge this > cleanly with the amd64 unwind stuff). I think you're supposed to unwind all the registers to be honest which is probably where the problem starts. It doesn't surprise me that you need all those on ARM though - well except r12 maybe (that's the static base pointer isn't it?). This is also I think the main problem that Jakub was having with PPC of it not unwinding enough registers and it not being easy to add extra registers to the set being unwound. > * ML_(read_callframe_info_dwarf3): handle .debug_frame sections (for > arm-ELF). These are unfortunately slightly different from the > .eh_frame sections used for amd64-ELF. Jakub's stuff that I committed also does this so there will definitely be some merging to do there. > * coregrind/m_stacktrace.c: split the main function, > VG_(get_StackTrace_wrk) into completely separate instances for the > different targets. Maintaining just one big function is too > unwieldy. Yes, that bent my brain when I was trying to understand that one line change to the PPC unwind logic... Splitting it is definitely a good idea. Tom -- Tom Hughes (to...@co...) http://www.compton.nu/ |
|
From: Julian S. <js...@ac...> - 2009-11-13 17:54:49
|
> > * Dwarf CFI reading for ARM: it's apparently necessary to > > simultaneously unwind r15, r14, r13, r12 and r11. Omitting any of > > those can cause the next level of the unwind to fail. The CFI > > unwinder and associated data types have been changed to match this > > (is platform specific; need to figure out a way to merge this > > cleanly with the amd64 unwind stuff). > > I think you're supposed to unwind all the registers to be honest which > is probably where the problem starts. Yes, indeed. I've been reluctant to go down that road because it increases the costs of unwinding, which start to become significant on memcheck in malloc/free intensive code and are very evident in Helgrind, which unwinds very often. It's a dog of a problem. If you can think of a better way to be both general and fast ... > need all those on ARM though - well except r12 maybe (that's the static > base pointer isn't it?). I get the impression frame pointers on arm went the way of the dodo around the time Thumb was introduced. Possibly because Thumb only allows 8 registers, so tying one up becomes a more expensive proposition. > This is also I think the main problem that Jakub was having with PPC of > it not unwinding enough registers and it not being easy to add extra > registers to the set being unwound. Uh, so we're trying to support CFI unwinding on PPC too, now? This seems like a good thing to me. However, I've been scratching my head, trying to figure out how to generalise the type DiCfSI (the unwind summary record) and VG_(use_CF_info) (the function that uses it) so as to be more general, yet not more expensive in time or space. For a large program, there are zillions of DiCfSI records sloshing around, and looking them up causes noticable L2 misses. > Jakub's stuff that I committed also does this so there will definitely > be some merging to do there. Yes, I saw that too. For some reason I didn't yet figure out, the .debug_frame reading stuff works for .so's even though I didn't make any effort to add text_bias values or any such onto the address range covered by each DiCfSI. J |
|
From: Tom H. <to...@co...> - 2009-11-13 17:58:42
|
On 13/11/09 18:08, Julian Seward wrote: >> This is also I think the main problem that Jakub was having with PPC of >> it not unwinding enough registers and it not being easy to add extra >> registers to the set being unwound. > > Uh, so we're trying to support CFI unwinding on PPC too, now? This > seems like a good thing to me. However, I've been scratching my head, > trying to figure out how to generalise the type DiCfSI (the unwind > summary record) and VG_(use_CF_info) (the function that uses it) > so as to be more general, yet not more expensive in time or space. > For a large program, there are zillions of DiCfSI records sloshing > around, and looking them up causes noticable L2 misses. Jakub tried to get CFI unwinding working and gave up - he talks about it on one of the bugs. There are a few bits of the work he did in the patches I committed but most of it he reverted and just fixed up that one unwind bug in the traditional unwinder. Tom -- Tom Hughes (to...@co...) http://www.compton.nu/ |