Various improvements to DWARF handling to cope with changes in recent
versions of gcc as shipped with Fedora 12. Specific changes include:
- Vastly increase the number of opcodes we understand how to
evaluate when processing a location expression.
- Process frame unwind data from the debug_frame ELF section as
well as the eh_frame section.
- Handle version 3 CIEs in frame unwind data.
- Handle the compact form of DW_AT_data_member_location which just
gives a constant offset from the start of it's base type instead
of a full location expression.
Based on patches from Jakub Jelinek on bugs #210479 and #210566.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10939 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_debuginfo/d3basics.c b/coregrind/m_debuginfo/d3basics.c
index a61ed05..efaf5a4 100644
--- a/coregrind/m_debuginfo/d3basics.c
+++ b/coregrind/m_debuginfo/d3basics.c
@@ -387,12 +387,8 @@
if (regno == 7/*RSP*/) { *a = regs->sp; return True; }
# elif defined(VGP_ppc32_linux)
if (regno == 1/*SP*/) { *a = regs->sp; return True; }
- if (regno == 31) return False;
- vg_assert(0);
# elif defined(VGP_ppc64_linux)
if (regno == 1/*SP*/) { *a = regs->sp; return True; }
- if (regno == 31) return False;
- vg_assert(0);
# elif defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5)
vg_assert(0); /* this function should never be called */
# else
@@ -443,7 +439,7 @@
/* Evaluate a standard DWARF3 expression. See detailed description in
- priv_d3basics.h. */
+ priv_d3basics.h. Doesn't handle DW_OP_piece/DW_OP_bit_piece yet. */
GXResult ML_(evaluate_Dwarf3_Expr) ( UChar* expr, UWord exprszB,
GExpr* fbGX, RegSummary* regs,
const DebugInfo* di,
@@ -482,8 +478,8 @@
Addr stack[N_EXPR_STACK]; /* stack of addresses, as per D3 spec */
GXResult fbval, res;
Addr a1;
- Word sw1;
- UWord uw1;
+ Word sw1, sw2;
+ UWord uw1, uw2;
Bool ok;
sp = -1;
@@ -568,12 +564,15 @@
switch (fbval.kind) {
case GXR_Failure:
return fbval; /* propagate failure */
- case GXR_Value:
+ case GXR_Addr:
a1 = fbval.word; break; /* use as-is */
case GXR_RegNo:
ok = get_Dwarf_Reg( &a1, fbval.word, regs );
if (!ok) return fbval; /* propagate failure */
break;
+ case GXR_Value:
+ FAIL("evaluate_Dwarf3_Expr: DW_OP_{implicit,stack}_value "
+ "in DW_AT_frame_base");
default:
vg_assert(0);
}
@@ -599,11 +598,23 @@
a1 += sw1;
PUSH( a1 );
break;
+ case DW_OP_bregx:
+ if (!regs)
+ FAIL("evaluate_Dwarf3_Expr: DW_OP_bregx but no reg info");
+ a1 = 0;
+ uw1 = (UWord)read_leb128U( &expr );
+ if (!get_Dwarf_Reg( &a1, uw1, regs ))
+ FAIL("evaluate_Dwarf3_Expr: unhandled DW_OP_bregx reg value");
+ sw1 = (Word)read_leb128S( &expr );
+ a1 += sw1;
+ PUSH( a1 );
+ break;
/* As per comment on DW_OP_breg*, the following denote that
the value in question is in a register, not in memory. So
we simply return failure. (iow, the expression is
malformed). */
case DW_OP_reg0 ... DW_OP_reg31:
+ case DW_OP_regx:
FAIL("evaluate_Dwarf3_Expr: DW_OP_reg* "
"whilst evaluating for a value");
break;
@@ -637,6 +648,238 @@
"address not valid for client");
}
break;
+ case DW_OP_deref_size:
+ POP(uw1);
+ uw2 = *expr++;
+ if (VG_(am_is_valid_for_client)( (Addr)uw1, uw2,
+ VKI_PROT_READ )) {
+ switch (uw2) {
+ case 1: uw1 = *(UChar*)uw1; break;
+ case 2: uw1 = *(UShort*)uw1; break;
+ case 4: uw1 = *(UInt*)uw1; break;
+ case 8: uw1 = *(ULong*)uw1; break;
+ default:
+ FAIL("warning: evaluate_Dwarf3_Expr: unhandled "
+ "DW_OP_deref_size size");
+ }
+ PUSH(uw1);
+ } else {
+ FAIL("warning: evaluate_Dwarf3_Expr: DW_OP_deref_size: "
+ "address not valid for client");
+ }
+ break;
+ case DW_OP_lit0 ... DW_OP_lit31:
+ PUSH(opcode - DW_OP_lit0);
+ break;
+ case DW_OP_const1u:
+ uw1 = *expr++;
+ PUSH(uw1);
+ break;
+ case DW_OP_const2u:
+ uw1 = *(UShort *)expr;
+ expr += 2;
+ PUSH(uw1);
+ break;
+ case DW_OP_const4u:
+ uw1 = *(UInt *)expr;
+ expr += 4;
+ PUSH(uw1);
+ break;
+ case DW_OP_const8u:
+ uw1 = *(ULong *)expr;
+ expr += 8;
+ PUSH(uw1);
+ break;
+ case DW_OP_constu:
+ uw1 = read_leb128U( &expr );
+ PUSH(uw1);
+ break;
+ case DW_OP_const1s:
+ uw1 = *(Char *)expr;
+ expr++;
+ PUSH(uw1);
+ break;
+ case DW_OP_const2s:
+ uw1 = *(Short *)expr;
+ expr += 2;
+ PUSH(uw1);
+ break;
+ case DW_OP_const4s:
+ uw1 = *(Int *)expr;
+ expr += 4;
+ PUSH(uw1);
+ break;
+ case DW_OP_const8s:
+ uw1 = *(Long *)expr;
+ expr += 8;
+ PUSH(uw1);
+ break;
+ case DW_OP_consts:
+ uw1 = read_leb128S( &expr );
+ PUSH(uw1);
+ break;
+ case DW_OP_dup:
+ POP(uw1);
+ PUSH(uw1);
+ PUSH(uw1);
+ break;
+ case DW_OP_drop:
+ POP(uw1);
+ break;
+ case DW_OP_over:
+ uw1 = 1;
+ goto do_pick;
+ case DW_OP_pick:
+ uw1 = *expr++;
+ do_pick:
+ if (sp < (Int)uw1)
+ FAIL("evaluate_Dwarf3_Expr: stack underflow");
+ uw1 = stack[sp - uw1];
+ PUSH(uw1);
+ break;
+ case DW_OP_swap:
+ if (sp < 1)
+ FAIL("evaluate_Dwarf3_Expr: stack underflow");
+ uw1 = stack[sp];
+ stack[sp] = stack[sp - 1];
+ stack[sp - 1] = uw1;
+ break;
+ case DW_OP_rot:
+ if (sp < 2)
+ FAIL("evaluate_Dwarf3_Expr: stack underflow");
+ uw1 = stack[sp];
+ stack[sp] = stack[sp - 1];
+ stack[sp - 1] = stack[sp - 2];
+ stack[sp - 2] = uw1;
+ break;
+ case DW_OP_abs:
+ POP(sw1);
+ if (sw1 < 0)
+ sw1 = -sw1;
+ PUSH(sw1);
+ break;
+ case DW_OP_div:
+ POP(sw2);
+ if (sw2 == 0)
+ FAIL("evaluate_Dwarf3_Expr: division by zero");
+ POP(sw1);
+ sw1 /= sw2;
+ PUSH(sw1);
+ break;
+ case DW_OP_mod:
+ POP(sw2);
+ if (sw2 == 0)
+ FAIL("evaluate_Dwarf3_Expr: division by zero");
+ POP(sw1);
+ sw1 %= sw2;
+ PUSH(sw1);
+ break;
+#define BINARY(name, op, s) \
+ case DW_OP_##name: \
+ POP(s##w2); \
+ POP(s##w1); \
+ s##w1 = s##w1 op s##w2; \
+ PUSH(s##w1); \
+ break
+#define UNARY(name, op, s) \
+ case DW_OP_##name: \
+ POP(s##w1); \
+ s##w1 = op s##w1; \
+ PUSH(s##w1); \
+ break
+ BINARY (and, &, u);
+ BINARY (minus, -, u);
+ BINARY (mul, *, u);
+ UNARY (neg, -, u);
+ UNARY (not, ~, u);
+ BINARY (or, |, u);
+ BINARY (plus, +, u);
+ BINARY (shl, <<, u);
+ BINARY (shr, >>, u);
+ BINARY (shra, >>, s);
+ BINARY (xor, ^, u);
+ BINARY (le, <=, s);
+ BINARY (lt, <, s);
+ BINARY (ge, >=, s);
+ BINARY (gt, >, s);
+ BINARY (ne, !=, u);
+ BINARY (eq, ==, u);
+#undef UNARY
+#undef BINARY
+ case DW_OP_skip:
+ sw1 = *(Short *)expr;
+ expr += 2;
+ if (expr + sw1 < limit - exprszB)
+ FAIL("evaluate_Dwarf3_Expr: DW_OP_skip before start of expr");
+ if (expr + sw1 >= limit)
+ FAIL("evaluate_Dwarf3_Expr: DW_OP_skip after end of expr");
+ expr += sw1;
+ break;
+ case DW_OP_bra:
+ sw1 = *(Short *)expr;
+ expr += 2;
+ if (expr + sw1 < limit - exprszB)
+ FAIL("evaluate_Dwarf3_Expr: DW_OP_bra before start of expr");
+ if (expr + sw1 >= limit)
+ FAIL("evaluate_Dwarf3_Expr: DW_OP_bra after end of expr");
+ POP(uw1);
+ if (uw1)
+ expr += sw1;
+ break;
+ case DW_OP_nop:
+ break;
+ case DW_OP_call_frame_cfa:
+ if (!regs)
+ FAIL("evaluate_Dwarf3_Expr: DW_OP_call_frame_cfa but no reg info");
+#if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
+ /* Valgrind on ppc32/ppc64 currently doesn't use unwind info. */
+ uw1 = *(Addr *)(regs->sp);
+#else
+ uw1 = ML_(get_CFA)(regs->ip, regs->sp, regs->fp, 0, ~(UWord) 0);
+#endif
+ if (!uw1)
+ FAIL("evaluate_Dwarf3_Expr: Could not resolve "
+ "DW_OP_call_frame_cfa");
+ PUSH(uw1);
+ break;
+ case DW_OP_implicit_value:
+ sw1 = (Word)read_leb128S( &expr );
+ uw1 = 0;
+ switch (sw1) {
+ case 1:
+ uw1 = *(UChar *)expr;
+ expr += 1;
+ break;
+ case 2:
+ uw1 = *(UShort *)expr;
+ expr += 2;
+ break;
+ case 4:
+ uw1 = *(UInt *)expr;
+ expr += 4;
+ break;
+ case 8:
+ uw1 = *(ULong *)expr;
+ expr += 8;
+ break;
+ default:
+ FAIL("evaluate_Dwarf3_Expr: Unhandled "
+ "DW_OP_implicit_value size");
+ }
+ if (expr != limit)
+ FAIL("evaluate_Dwarf3_Expr: DW_OP_implicit_value "
+ "does not terminate expression");
+ res.word = uw1;
+ res.kind = GXR_Value;
+ return res;
+ case DW_OP_stack_value:
+ POP (uw1);
+ res.word = uw1;
+ res.kind = GXR_Value;
+ if (expr != limit)
+ FAIL("evaluate_Dwarf3_Expr: DW_OP_stack_value "
+ "does not terminate expression");
+ break;
default:
if (!VG_(clo_xml))
VG_(message)(Vg_DebugMsg,
@@ -650,7 +893,7 @@
vg_assert(sp >= 0 && sp < N_EXPR_STACK);
res.word = stack[sp];
- res.kind = GXR_Value;
+ res.kind = GXR_Addr;
return res;
# undef POP
@@ -829,12 +1072,15 @@
if (!badness)
badness = "trivial GExpr denotes register (2)";
}
- else {
+ else if (0) {
VG_(printf)(" ML_(evaluate_trivial_GX): unhandled:\n ");
ML_(pp_GX)( gx );
VG_(printf)("\n");
tl_assert(0);
}
+ else
+ if (!badness)
+ badness = "non-trivial GExpr";
VG_(addToXA)( results, &thisResult );
@@ -884,7 +1130,7 @@
/* Well, we have success. All subexpressions evaluated, and
they all agree. Hurrah. */
- res.kind = GXR_Value;
+ res.kind = GXR_Addr;
res.word = (UWord)mul->ul; /* NB: narrowing from ULong */
VG_(deleteXA)( results );
return res;
@@ -896,6 +1142,8 @@
switch (res.kind) {
case GXR_Failure:
VG_(printf)("GXR_Failure(%s)", (HChar*)res.word); break;
+ case GXR_Addr:
+ VG_(printf)("GXR_Addr(0x%lx)", res.word); break;
case GXR_Value:
VG_(printf)("GXR_Value(0x%lx)", res.word); break;
case GXR_RegNo:
diff --git a/coregrind/m_debuginfo/debuginfo.c b/coregrind/m_debuginfo/debuginfo.c
index 66ae82b..106d398 100644
--- a/coregrind/m_debuginfo/debuginfo.c
+++ b/coregrind/m_debuginfo/debuginfo.c
@@ -2003,6 +2003,94 @@
}
+static CFSICacheEnt* cfsi_cache__find ( Addr ip )
+{
+ UWord hash = ip % N_CFSI_CACHE;
+ CFSICacheEnt* ce = &cfsi_cache[hash];
+ static UWord n_q = 0, n_m = 0;
+
+ n_q++;
+ if (0 && 0 == (n_q & 0x1FFFFF))
+ VG_(printf)("QQQ %lu %lu\n", n_q, n_m);
+
+ if (LIKELY(ce->ip == ip) && LIKELY(ce->di != NULL)) {
+ /* found an entry in the cache .. */
+ } else {
+ /* not found in cache. Search and update. */
+ n_m++;
+ ce->ip = ip;
+ find_DiCfSI( &ce->di, &ce->ix, ip );
+ }
+
+ if (UNLIKELY(ce->di == (DebugInfo*)1)) {
+ /* no DiCfSI for this address */
+ return NULL;
+ } else {
+ /* found a DiCfSI for this address */
+ return ce;
+ }
+}
+
+
+static Addr compute_cfa ( Addr ip, Addr sp, Addr fp,
+ Addr min_accessible, Addr max_accessible,
+ DebugInfo* di, DiCfSI* cfsi )
+{
+ CfiExprEvalContext eec;
+ Addr cfa;
+ Bool ok;
+
+ /* Compute the CFA. */
+ cfa = 0;
+ switch (cfsi->cfa_how) {
+ case CFIC_SPREL:
+ cfa = sp + cfsi->cfa_off;
+ break;
+ case CFIC_FPREL:
+ cfa = fp + cfsi->cfa_off;
+ break;
+ case CFIC_EXPR:
+ if (0) {
+ VG_(printf)("CFIC_EXPR: ");
+ ML_(ppCfiExpr)(di->cfsi_exprs, cfsi->cfa_off);
+ VG_(printf)("\n");
+ }
+ eec.ipHere = ip;
+ eec.spHere = sp;
+ eec.fpHere = fp;
+ eec.min_accessible = min_accessible;
+ eec.max_accessible = max_accessible;
+ ok = True;
+ cfa = evalCfiExpr(di->cfsi_exprs, cfsi->cfa_off, &eec, &ok );
+ if (!ok) return 0;
+ break;
+ default:
+ vg_assert(0);
+ }
+ return cfa;
+}
+
+
+/* Get the call frame address (CFA) given an IP/SP/FP triple. */
+Addr ML_(get_CFA) ( Addr ip, Addr sp, Addr fp,
+ Addr min_accessible, Addr max_accessible )
+{
+ CFSICacheEnt* ce;
+ DebugInfo* di;
+ DiCfSI* cfsi;
+
+ ce = cfsi_cache__find(ip);
+
+ if (UNLIKELY(ce == NULL))
+ return 0; /* no info. Nothing we can do. */
+
+ di = ce->di;
+ cfsi = &di->cfsi[ ce->ix ];
+
+ return compute_cfa(ip, sp, fp, min_accessible, max_accessible, di, cfsi);
+}
+
+
/* 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. */
@@ -2015,44 +2103,21 @@
Addr min_accessible,
Addr max_accessible )
{
- Bool ok;
- DebugInfo* di;
- DiCfSI* cfsi = NULL;
- Addr cfa, ipHere, spHere, fpHere, ipPrev, spPrev, fpPrev;
-
+ Bool ok;
+ DebugInfo* di;
+ DiCfSI* cfsi = NULL;
+ Addr cfa, ipHere, spHere, fpHere, ipPrev, spPrev, fpPrev;
+ CFSICacheEnt* ce;
CfiExprEvalContext eec;
- static UWord n_q = 0, n_m = 0;
- n_q++;
- if (0 && 0 == (n_q & 0x1FFFFF))
- VG_(printf)("QQQ %lu %lu\n", n_q, n_m);
+ ce = cfsi_cache__find(*ipP);
- { UWord hash = (*ipP) % N_CFSI_CACHE;
- CFSICacheEnt* ce = &cfsi_cache[hash];
-
- if (LIKELY(ce->ip == *ipP) && 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 );
- }
-
- if (UNLIKELY(ce->di == (DebugInfo*)1)) {
- /* no DiCfSI for this address */
- cfsi = NULL;
- di = NULL;
- } else {
- /* found a DiCfSI for this address */
- di = ce->di;
- cfsi = &di->cfsi[ ce->ix ];
- }
- }
-
- if (UNLIKELY(cfsi == NULL))
+ if (UNLIKELY(ce == NULL))
return False; /* no info. Nothing we can do. */
+ di = ce->di;
+ cfsi = &di->cfsi[ ce->ix ];
+
if (0) {
VG_(printf)("found cfisi: ");
ML_(ppDiCfSI)(di->cfsi_exprs, cfsi);
@@ -2065,32 +2130,10 @@
fpHere = *fpP;
/* First compute the CFA. */
- cfa = 0;
- switch (cfsi->cfa_how) {
- case CFIC_SPREL:
- cfa = cfsi->cfa_off + spHere;
- break;
- case CFIC_FPREL:
- cfa = cfsi->cfa_off + fpHere;
- 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.min_accessible = min_accessible;
- eec.max_accessible = max_accessible;
- ok = True;
- cfa = evalCfiExpr(di->cfsi_exprs, cfsi->cfa_off, &eec, &ok );
- if (!ok) return False;
- break;
- default:
- vg_assert(0);
- }
+ cfa = compute_cfa(ipHere, spHere, fpHere,
+ min_accessible, max_accessible, di, cfsi);
+ if (UNLIKELY(cfa == 0))
+ return False;
/* Now we know the CFA, use it to roll back the registers we're
interested in. */
@@ -2346,7 +2389,7 @@
VG_(printf)("\n");
}
- if (res.kind == GXR_Value
+ if (res.kind == GXR_Addr
&& res.word <= data_addr
&& data_addr < res.word + var_szB) {
*offset = data_addr - res.word;
@@ -3057,7 +3100,7 @@
vg_assert(res_sp_6k.kind == res_fp_6k.kind);
vg_assert(res_sp_6k.kind == res_fp_7k.kind);
- if (res_sp_6k.kind == GXR_Value) {
+ if (res_sp_6k.kind == GXR_Addr) {
StackBlock block;
GXResult res;
UWord sp_delta = res_sp_7k.word - res_sp_6k.word;
@@ -3074,7 +3117,7 @@
regs.sp = regs.fp = 0;
regs.ip = ip;
res = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di );
- tl_assert(res.kind == GXR_Value);
+ tl_assert(res.kind == GXR_Addr);
if (debug)
VG_(printf)(" %5ld .. %5ld (sp) %s\n",
res.word, res.word + ((UWord)mul.ul) - 1, var->name);
@@ -3093,7 +3136,7 @@
regs.sp = regs.fp = 0;
regs.ip = ip;
res = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di );
- tl_assert(res.kind == GXR_Value);
+ tl_assert(res.kind == GXR_Addr);
if (debug)
VG_(printf)(" %5ld .. %5ld (FP) %s\n",
res.word, res.word + ((UWord)mul.ul) - 1, var->name);
@@ -3308,7 +3351,7 @@
res = ML_(evaluate_trivial_GX)( var->gexpr, di );
/* Not a constant address => not interesting */
- if (res.kind != GXR_Value) {
+ if (res.kind != GXR_Addr) {
if (0) VG_(printf)("FAIL\n");
continue;
}
diff --git a/coregrind/m_debuginfo/priv_d3basics.h b/coregrind/m_debuginfo/priv_d3basics.h
index 1368e0d..81f46ef 100644
--- a/coregrind/m_debuginfo/priv_d3basics.h
+++ b/coregrind/m_debuginfo/priv_d3basics.h
@@ -522,6 +522,9 @@
DW_OP_form_tls_address = 0x9b,
DW_OP_call_frame_cfa = 0x9c,
DW_OP_bit_piece = 0x9d,
+ /* DWARF 4 extensions. */
+ DW_OP_implicit_value = 0x9e,
+ DW_OP_stack_value = 0x9f,
/* GNU extensions. */
DW_OP_GNU_push_tls_address = 0xe0,
/* HP extensions. */
@@ -596,12 +599,13 @@
/* This describes the result of evaluating a DWARF3 expression.
GXR_Failure: failed; .word is an asciiz string summarising why
+ GXR_Addr: evaluated to an address of the object, in .word
GXR_Value: evaluated to a value, in .word
GXR_RegNo: evaluated to a DWARF3 register number, in .word
*/
typedef
struct {
- enum { GXR_Failure, GXR_Value, GXR_RegNo } kind;
+ enum { GXR_Failure, GXR_Addr, GXR_Value, GXR_RegNo } kind;
UWord word;
}
GXResult;
@@ -644,6 +648,10 @@
covered by the guard is also ignored. */
GXResult ML_(evaluate_trivial_GX)( GExpr* gx, const DebugInfo* di );
+/* Compute call frame address (CFA) for IP/SP/FP. */
+Addr ML_(get_CFA) ( Addr ip, Addr sp, Addr fp,
+ Addr min_accessible, Addr max_accessible );
+
#endif /* ndef __PRIV_D3BASICS_H */
/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_debuginfo/priv_readdwarf.h b/coregrind/m_debuginfo/priv_readdwarf.h
index dc9fb99..299daf4 100644
--- a/coregrind/m_debuginfo/priv_readdwarf.h
+++ b/coregrind/m_debuginfo/priv_readdwarf.h
@@ -62,7 +62,7 @@
-------------------- */
extern
void ML_(read_callframe_info_dwarf3)
- ( /*OUT*/struct _DebugInfo* di, UChar* ehframe );
+ ( /*OUT*/struct _DebugInfo* di, UChar* frame, SizeT frame_sz, Bool for_eh );
#endif /* ndef __PRIV_READDWARF_H */
diff --git a/coregrind/m_debuginfo/priv_storage.h b/coregrind/m_debuginfo/priv_storage.h
index cff91f7..b790001 100644
--- a/coregrind/m_debuginfo/priv_storage.h
+++ b/coregrind/m_debuginfo/priv_storage.h
@@ -649,6 +649,11 @@
this after finishing adding entries to these tables. */
extern void ML_(canonicaliseTables) ( struct _DebugInfo* di );
+/* Canonicalise the call-frame-info table held by 'di', in preparation
+ for use. This is called by ML_(canonicaliseTables) but can also be
+ called on it's own to sort just this table. */
+extern void ML_(canonicaliseCFI) ( struct _DebugInfo* di );
+
/* ------ Searching ------ */
/* Find a symbol-table index containing the specified pointer, or -1
diff --git a/coregrind/m_debuginfo/priv_tytypes.h b/coregrind/m_debuginfo/priv_tytypes.h
index 880aa0e..354cf60 100644
--- a/coregrind/m_debuginfo/priv_tytypes.h
+++ b/coregrind/m_debuginfo/priv_tytypes.h
@@ -78,8 +78,13 @@
struct {
UChar* name; /* in mallocville */
UWord typeR; /* should be Te_TyXXXX */
- UChar* loc; /* location expr, in mallocville */
- UWord nLoc; /* number of bytes in .loc */
+ union {
+ UChar* loc; /* location expr, in mallocville */
+ Word offset; /* or offset from the beginning of containing
+ entity */
+ } pos;
+ Word nLoc; /* number of bytes in .pos.loc if >= 0, or -1
+ if .pos.offset should be used instead */
Bool isStruct;
} Field;
struct {
diff --git a/coregrind/m_debuginfo/readdwarf.c b/coregrind/m_debuginfo/readdwarf.c
index 15cf70a..bde2cf1 100644
--- a/coregrind/m_debuginfo/readdwarf.c
+++ b/coregrind/m_debuginfo/readdwarf.c
@@ -37,6 +37,7 @@
#include "pub_core_libcprint.h"
#include "pub_core_options.h"
#include "pub_core_xarray.h"
+#include "pub_core_tooliface.h" /* VG_(needs) */
#include "priv_misc.h" /* dinfo_zalloc/free/strdup */
#include "priv_d3basics.h"
#include "priv_tytypes.h"
@@ -1777,11 +1778,11 @@
#elif defined(VGP_ppc32_linux)
# define FP_REG 1
# define SP_REG 1
-# define RA_REG_DEFAULT 8 // CAB: What's a good default ?
+# define RA_REG_DEFAULT 65
#elif defined(VGP_ppc64_linux)
# define FP_REG 1
# define SP_REG 1
-# define RA_REG_DEFAULT 8 // CAB: What's a good default ?
+# define RA_REG_DEFAULT 65
#elif defined(VGP_x86_darwin)
# define FP_REG 5
# define SP_REG 4
@@ -1795,7 +1796,11 @@
#endif
/* the number of regs we are prepared to unwind */
-#define N_CFI_REGS 20
+#if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
+# define N_CFI_REGS 72
+#else
+# define N_CFI_REGS 20
+#endif
/* Instructions for the automaton */
enum dwarf_cfa_primary_ops
@@ -3446,23 +3451,33 @@
void ML_(read_callframe_info_dwarf3)
- ( /*OUT*/struct _DebugInfo* di, UChar* ehframe_image )
+ ( /*OUT*/struct _DebugInfo* di, UChar* frame_image, SizeT frame_size,
+ Bool for_eh )
{
Int nbytes;
HChar* how = NULL;
Int n_CIEs = 0;
- UChar* data = ehframe_image;
+ UChar* data = frame_image;
+ UWord ehframe_cfsis = 0;
+ Addr frame_avma = for_eh ? di->ehframe_avma : 0;
# if defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux)
- /* These targets don't use CFI-based stack unwinding. */
+ /* These targets don't use CFI-based stack unwinding. */
return;
# endif
+ /* If we are reading .debug_frame after .eh_frame has been read, only
+ add FDEs which weren't covered in .eh_frame. To be able to quickly
+ search the FDEs, the records must be sorted. */
+ if ( ! for_eh && di->ehframe_size && di->cfsi_used ) {
+ ML_(canonicaliseCFI) ( di );
+ ehframe_cfsis = di->cfsi_used;
+ }
+
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 );
+ frame_size, frame_avma, frame_image );
VG_(printf)("CFI info: name %s\n",
di->filename );
}
@@ -3495,11 +3510,11 @@
Bool dw64;
/* Are we done? */
- if (data == ehframe_image + di->ehframe_size)
+ if (data == frame_image + frame_size)
return;
/* Overshot the end? Means something is wrong */
- if (data > ehframe_image + di->ehframe_size) {
+ if (data > frame_image + frame_size) {
how = "overran the end of .eh_frame";
goto bad;
}
@@ -3509,9 +3524,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 (frame_image + 0x%lx)\n",
ciefde_start,
- ciefde_start - ehframe_image + 0UL);
+ ciefde_start - frame_image + 0UL);
ciefde_len = (ULong) read_UInt(data); data += sizeof(UInt);
if (di->trace_cfi)
@@ -3524,7 +3539,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)frame_image));
return;
}
@@ -3550,8 +3565,10 @@
if (di->trace_cfi)
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 is zero for .eh_frame or all ones for .debug_frame,
+ we've got a CIE; else it's an FDE. */
+ if (cie_pointer == (for_eh ? 0ULL
+ : dw64 ? 0xFFFFFFFFFFFFFFFFULL : 0xFFFFFFFFULL)) {
Int this_CIE;
UChar cie_version;
@@ -3575,11 +3592,11 @@
/* 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);
+ the_CIEs[this_CIE].offset = (ULong)(ciefde_start - frame_image);
if (di->ddump_frames)
VG_(printf)("%08lx %08lx %08lx CIE\n",
- ((Addr)ciefde_start) - ((Addr)ehframe_image),
+ ((Addr)ciefde_start) - ((Addr)frame_image),
(Addr)ciefde_len,
(Addr)(UWord)cie_pointer );
@@ -3623,8 +3640,13 @@
VG_(printf)(" Data alignment factor: %d\n",
(Int)the_CIEs[this_CIE].data_a_f);
- the_CIEs[this_CIE].ra_reg = (Int)read_UChar(data);
- data += sizeof(UChar);
+ if (cie_version == 1) {
+ the_CIEs[this_CIE].ra_reg = (Int)read_UChar(data);
+ data += sizeof(UChar);
+ } else {
+ the_CIEs[this_CIE].ra_reg = read_leb128( data, &nbytes, 0);
+ data += nbytes;
+ }
if (di->trace_cfi)
VG_(printf)("cie.ra_reg = %d\n",
the_CIEs[this_CIE].ra_reg);
@@ -3702,7 +3724,7 @@
}
if (the_CIEs[this_CIE].ilen < 0
- || the_CIEs[this_CIE].ilen > di->ehframe_size) {
+ || the_CIEs[this_CIE].ilen > frame_size) {
how = "implausible # cie initial insns";
goto bad;
}
@@ -3717,8 +3739,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.ehframe_image = frame_image;
+ adi.ehframe_avma = frame_avma;
adi.text_bias = di->text_debug_bias;
show_CF_instructions( the_CIEs[this_CIE].instrs,
the_CIEs[this_CIE].ilen, &adi,
@@ -3747,9 +3769,12 @@
cie_pointer bytes back from here. */
/* re sizeof(UInt) / sizeof(ULong), matches XXX above. */
- look_for = (data - (dw64 ? sizeof(ULong) : sizeof(UInt))
- - ehframe_image)
- - cie_pointer;
+ if (for_eh)
+ look_for = (data - (dw64 ? sizeof(ULong) : sizeof(UInt))
+ - frame_image)
+ - cie_pointer;
+ else
+ look_for = cie_pointer;
for (cie = 0; cie < n_CIEs; cie++) {
if (0) VG_(printf)("look for %lld %lld\n",
@@ -3764,8 +3789,8 @@
}
adi.encoding = the_CIEs[cie].address_encoding;
- adi.ehframe_image = ehframe_image;
- adi.ehframe_avma = di->ehframe_avma;
+ adi.ehframe_image = frame_image;
+ adi.ehframe_avma = frame_avma;
adi.text_bias = di->text_debug_bias;
fde_initloc = read_encoded_Addr(&nbytes, &adi, data);
data += nbytes;
@@ -3773,8 +3798,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.ehframe_image = frame_image;
+ adi.ehframe_avma = frame_avma;
adi.text_bias = di->text_debug_bias;
/* WAS (incorrectly):
@@ -3800,7 +3825,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)frame_image),
(Addr)ciefde_len,
(Addr)(UWord)cie_pointer,
(Addr)look_for,
@@ -3827,16 +3852,43 @@
VG_(printf)("fde.ilen = %d\n", (Int)fde_ilen);
}
- if (fde_ilen < 0 || fde_ilen > di->ehframe_size) {
+ if (fde_ilen < 0 || fde_ilen > frame_size) {
how = "implausible # fde insns";
goto bad;
}
data += fde_ilen;
+ if (ehframe_cfsis) {
+ Addr a_mid_lo, a_mid_hi;
+ Word mid, size,
+ lo = 0,
+ hi = ehframe_cfsis-1;
+ while (True) {
+ /* current unsearched space is from lo to hi, inclusive. */
+ if (lo > hi) break; /* not found */
+ mid = (lo + hi) / 2;
+ a_mid_lo = di->cfsi[mid].base;
+ size = di->cfsi[mid].len;
+ a_mid_hi = a_mid_lo + size - 1;
+ vg_assert(a_mid_hi >= a_mid_lo);
+ if (fde_initloc + fde_arange <= a_mid_lo) {
+ hi = mid-1; continue;
+ }
+ if (fde_initloc > a_mid_hi) { lo = mid+1; continue; }
+ break;
+ }
+
+ /* The range this .debug_frame FDE covers has been already
+ covered in .eh_frame section. Don't add it from .debug_frame
+ section again. */
+ if (lo <= hi)
+ continue;
+ }
+
adi.encoding = the_CIEs[cie].address_encoding;
- adi.ehframe_image = ehframe_image;
- adi.ehframe_avma = di->ehframe_avma;
+ adi.ehframe_image = frame_image;
+ adi.ehframe_avma = frame_avma;
adi.text_bias = di->text_debug_bias;
if (di->trace_cfi)
diff --git a/coregrind/m_debuginfo/readdwarf3.c b/coregrind/m_debuginfo/readdwarf3.c
index 174b9d2..e9ec841 100644
--- a/coregrind/m_debuginfo/readdwarf3.c
+++ b/coregrind/m_debuginfo/readdwarf3.c
@@ -2356,9 +2356,16 @@
if (attr == DW_AT_type && ctsSzB > 0) {
fieldE.Te.Field.typeR = (UWord)cts;
}
- if (attr == DW_AT_data_member_location && ctsMemSzB > 0) {
+ /* There are 2 different cases for DW_AT_data_member_location.
+ If it is a constant class attribute, it contains byte offset
+ from the beginning of the containing entity.
+ Otherwise it is a location expression. */
+ if (attr == DW_AT_data_member_location && ctsSzB > 0) {
+ fieldE.Te.Field.nLoc = -1;
+ fieldE.Te.Field.pos.offset = cts;
+ } else if (attr == DW_AT_data_member_location && ctsMemSzB > 0) {
fieldE.Te.Field.nLoc = (UWord)ctsMemSzB;
- fieldE.Te.Field.loc
+ fieldE.Te.Field.pos.loc
= ML_(dinfo_memdup)( "di.readdwarf3.ptD.member.2",
(UChar*)(UWord)cts,
(SizeT)fieldE.Te.Field.nLoc );
@@ -2385,13 +2392,14 @@
vg_assert(fieldE.Te.Field.name);
if (fieldE.Te.Field.typeR == D3_INVALID_CUOFF)
goto bad_DIE;
- if (fieldE.Te.Field.loc) {
+ if (fieldE.Te.Field.nLoc) {
if (!parent_is_struct) {
/* If this is a union type, pretend we haven't seen the data
member location expression, as it is by definition
redundant (it must be zero). */
- ML_(dinfo_free)(fieldE.Te.Field.loc);
- fieldE.Te.Field.loc = NULL;
+ if (fieldE.Te.Field.nLoc > 0)
+ ML_(dinfo_free)(fieldE.Te.Field.pos.loc);
+ fieldE.Te.Field.pos.loc = NULL;
fieldE.Te.Field.nLoc = 0;
}
/* Record this child in the parent */
@@ -2616,10 +2624,10 @@
/* For union members, Expr should be absent */
if (0) VG_(printf)("YYYY Acquire Field\n");
vg_assert(fieldE.tag == Te_Field);
- vg_assert( (fieldE.Te.Field.nLoc > 0 && fieldE.Te.Field.loc != NULL)
- || (fieldE.Te.Field.nLoc == 0 && fieldE.Te.Field.loc == NULL) );
+ vg_assert(fieldE.Te.Field.nLoc <= 0 || fieldE.Te.Field.pos.loc != NULL);
+ vg_assert(fieldE.Te.Field.nLoc != 0 || fieldE.Te.Field.pos.loc == NULL);
if (fieldE.Te.Field.isStruct) {
- vg_assert(fieldE.Te.Field.nLoc > 0);
+ vg_assert(fieldE.Te.Field.nLoc != 0);
} else {
vg_assert(fieldE.Te.Field.nLoc == 0);
}
diff --git a/coregrind/m_debuginfo/readelf.c b/coregrind/m_debuginfo/readelf.c
index 4011852..df7bcb5 100644
--- a/coregrind/m_debuginfo/readelf.c
+++ b/coregrind/m_debuginfo/readelf.c
@@ -1691,6 +1691,7 @@
UChar* debug_str_img = NULL; /* .debug_str (dwarf2) */
UChar* debug_ranges_img = NULL; /* .debug_ranges (dwarf2) */
UChar* debug_loc_img = NULL; /* .debug_loc (dwarf2) */
+ UChar* debug_frame_img = NULL; /* .debug_frame (dwarf2) */
UChar* dwarf1d_img = NULL; /* .debug (dwarf1) */
UChar* dwarf1l_img = NULL; /* .line (dwarf1) */
UChar* ehframe_img = NULL; /* .eh_frame (dwarf2) */
@@ -1710,6 +1711,7 @@
SizeT debug_str_sz = 0;
SizeT debug_ranges_sz = 0;
SizeT debug_loc_sz = 0;
+ SizeT debug_frame_sz = 0;
SizeT dwarf1d_sz = 0;
SizeT dwarf1l_sz = 0;
SizeT ehframe_sz = 0;
@@ -1767,6 +1769,7 @@
FIND(".debug_str", debug_str_sz, debug_str_img)
FIND(".debug_ranges", debug_ranges_sz, debug_ranges_img)
FIND(".debug_loc", debug_loc_sz, debug_loc_img)
+ FIND(".debug_frame", debug_frame_sz, debug_frame_img)
FIND(".debug", dwarf1d_sz, dwarf1d_img)
FIND(".line", dwarf1l_sz, dwarf1l_img)
@@ -1959,6 +1962,8 @@
FIND(need_dwarf2, ".debug_ranges", debug_ranges_sz,
debug_ranges_img)
FIND(need_dwarf2, ".debug_loc", debug_loc_sz, debug_loc_img)
+ FIND(need_dwarf2, ".debug_frame", debug_frame_sz,
+ debug_frame_img)
FIND(need_dwarf1, ".debug", dwarf1d_sz, dwarf1d_img)
FIND(need_dwarf1, ".line", dwarf1l_sz, dwarf1l_img)
@@ -1996,10 +2001,14 @@
False, opd_img);
}
- /* Read .eh_frame (call-frame-info) if any */
+ /* Read .eh_frame and .debug_frame (call-frame-info) 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, ehframe_img, ehframe_sz, True );
+ }
+ if (debug_frame_sz) {
+ ML_(read_callframe_info_dwarf3)( di, debug_frame_img,
+ debug_frame_sz, False );
}
/* Read the stabs and/or dwarf2 debug information, if any. It
diff --git a/coregrind/m_debuginfo/storage.c b/coregrind/m_debuginfo/storage.c
index 5e318fd..eab3280 100644
--- a/coregrind/m_debuginfo/storage.c
+++ b/coregrind/m_debuginfo/storage.c
@@ -1435,7 +1435,7 @@
return 0;
}
-static void canonicaliseCFI ( struct _DebugInfo* di )
+void ML_(canonicaliseCFI) ( struct _DebugInfo* di )
{
Word i, j;
const Addr minAvma = 0;
@@ -1528,7 +1528,7 @@
{
canonicaliseSymtab ( di );
canonicaliseLoctab ( di );
- canonicaliseCFI ( di );
+ ML_(canonicaliseCFI) ( di );
canonicaliseVarInfo ( di );
}
diff --git a/coregrind/m_debuginfo/tytypes.c b/coregrind/m_debuginfo/tytypes.c
index 4e3c9ee..46edf97 100644
--- a/coregrind/m_debuginfo/tytypes.c
+++ b/coregrind/m_debuginfo/tytypes.c
@@ -98,10 +98,15 @@
te->Te.Atom.value, te->Te.Atom.name);
break;
case Te_Field:
- VG_(printf)("Te_Field(ty=0x%05lx,nLoc=%lu,loc=%p,\"%s\")",
- te->Te.Field.typeR, te->Te.Field.nLoc,
- te->Te.Field.loc,
- te->Te.Field.name ? te->Te.Field.name : (UChar*)"");
+ if (te->Te.Field.nLoc == -1)
+ VG_(printf)("Te_Field(ty=0x%05lx,pos.offset=%ld,\"%s\")",
+ te->Te.Field.typeR, te->Te.Field.pos.offset,
+ te->Te.Field.name ? te->Te.Field.name : (UChar*)"");
+ else
+ VG_(printf)("Te_Field(ty=0x%05lx,nLoc=%lu,pos.loc=%p,\"%s\")",
+ te->Te.Field.typeR, te->Te.Field.nLoc,
+ te->Te.Field.pos.loc,
+ te->Te.Field.name ? te->Te.Field.name : (UChar*)"");
break;
case Te_Bound:
VG_(printf)("Te_Bound[");
@@ -476,8 +481,11 @@
if (r != 0) return r;
r = UWord__cmp(te1->Te.Field.nLoc, te2->Te.Field.nLoc);
if (r != 0) return r;
- r = Bytevector__cmp(te1->Te.Field.loc, te2->Te.Field.loc,
- te1->Te.Field.nLoc);
+ if (te1->Te.Field.nLoc == -1)
+ r = Long__cmp(te1->Te.Field.pos.offset, te2->Te.Field.pos.offset);
+ else
+ r = Bytevector__cmp(te1->Te.Field.pos.loc, te2->Te.Field.pos.loc,
+ te1->Te.Field.nLoc);
return r;
case Te_Bound:
r = Bool__cmp(te1->Te.Bound.knownL, te2->Te.Bound.knownL);
@@ -568,7 +576,8 @@
break;
case Te_Field:
if (te->Te.Field.name) ML_(dinfo_free)(te->Te.Field.name);
- if (te->Te.Field.loc) ML_(dinfo_free)(te->Te.Field.loc);
+ if (te->Te.Field.nLoc > 0 && te->Te.Field.pos.loc)
+ ML_(dinfo_free)(te->Te.Field.pos.loc);
break;
case Te_Bound:
break;
@@ -747,26 +756,32 @@
field = ML_(TyEnts__index_by_cuOff)(tyents, NULL, fieldR);
vg_assert(field);
vg_assert(field->tag == Te_Field);
- vg_assert(field->Te.Field.loc);
- vg_assert(field->Te.Field.nLoc > 0);
- /* Re data_bias in this call, we should really send in
- a legitimate value. But the expression is expected
- to be a constant expression, evaluation of which
- will not need to use DW_OP_addr and hence we can
- avoid the trouble of plumbing the data bias through
- to this point (if, indeed, it has any meaning; from
- which DebugInfo would we take the data bias? */
- res = ML_(evaluate_Dwarf3_Expr)(
- field->Te.Field.loc, field->Te.Field.nLoc,
- NULL/*fbGX*/, NULL/*RegSummary*/,
- 0/*data_bias*/,
- True/*push_initial_zero*/);
- if (0) {
- VG_(printf)("QQQ ");
- ML_(pp_GXResult)(res);
- VG_(printf)("\n");
+ vg_assert(field->Te.Field.nLoc < 0
+ || (field->Te.Field.nLoc > 0
+ && field->Te.Field.pos.loc));
+ if (field->Te.Field.nLoc == -1) {
+ res.kind = GXR_Addr;
+ res.word = field->Te.Field.pos.offset;
+ } else {
+ /* Re data_bias in this call, we should really send in
+ a legitimate value. But the expression is expected
+ to be a constant expression, evaluation of which
+ will not need to use DW_OP_addr and hence we can
+ avoid the trouble of plumbing the data bias through
+ to this point (if, indeed, it has any meaning; from
+ which DebugInfo would we take the data bias? */
+ res = ML_(evaluate_Dwarf3_Expr)(
+ field->Te.Field.pos.loc, field->Te.Field.nLoc,
+ NULL/*fbGX*/, NULL/*RegSummary*/,
+ 0/*data_bias*/,
+ True/*push_initial_zero*/);
+ if (0) {
+ VG_(printf)("QQQ ");
+ ML_(pp_GXResult)(res);
+ VG_(printf)("\n");
+ }
}
- if (res.kind != GXR_Value)
+ if (res.kind != GXR_Addr)
continue;
mul = ML_(sizeOfType)( tyents, field->Te.Field.typeR );
if (mul.b != True)