From: Tom Hughes Date: Thu, 12 Nov 2009 13:28:34 +0000 (+0000) Subject: Various improvements to DWARF handling to cope with changes in recent X-Git-Tag: svn/VALGRIND_3_6_0~473 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ee70926b34ca0575531a5a022528ce35766308d0;p=thirdparty%2Fvalgrind.git 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 --- diff --git a/coregrind/m_debuginfo/d3basics.c b/coregrind/m_debuginfo/d3basics.c index a61ed05cb2..efaf5a4085 100644 --- a/coregrind/m_debuginfo/d3basics.c +++ b/coregrind/m_debuginfo/d3basics.c @@ -387,12 +387,8 @@ static Bool get_Dwarf_Reg( /*OUT*/Addr* a, Word regno, RegSummary* regs ) 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 @@ static Bool bias_address( Addr* a, const DebugInfo* di ) /* 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 @@ GXResult ML_(evaluate_Dwarf3_Expr) ( UChar* expr, UWord exprszB, 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 @@ GXResult ML_(evaluate_Dwarf3_Expr) ( UChar* expr, UWord exprszB, 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 @@ GXResult ML_(evaluate_Dwarf3_Expr) ( UChar* expr, UWord exprszB, 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 @@ GXResult ML_(evaluate_Dwarf3_Expr) ( UChar* expr, UWord exprszB, "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 @@ GXResult ML_(evaluate_Dwarf3_Expr) ( UChar* expr, UWord exprszB, 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 @@ GXResult ML_(evaluate_trivial_GX)( GExpr* gx, const DebugInfo* di ) 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 @@ GXResult ML_(evaluate_trivial_GX)( GExpr* gx, const DebugInfo* di ) /* 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 @@ void ML_(pp_GXResult) ( GXResult res ) 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 66ae82b702..106d3987da 100644 --- a/coregrind/m_debuginfo/debuginfo.c +++ b/coregrind/m_debuginfo/debuginfo.c @@ -2003,75 +2003,51 @@ static void cfsi_cache__invalidate ( void ) { } -/* 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. */ -/* 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, - Addr min_accessible, - Addr max_accessible ) +static CFSICacheEnt* cfsi_cache__find ( Addr ip ) { - Bool ok; - DebugInfo* di; - DiCfSI* cfsi = NULL; - Addr cfa, ipHere, spHere, fpHere, ipPrev, spPrev, fpPrev; - - CfiExprEvalContext eec; + UWord hash = ip % N_CFSI_CACHE; + CFSICacheEnt* ce = &cfsi_cache[hash]; + static UWord n_q = 0, n_m = 0; - 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); - { 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)) - return False; /* no info. Nothing we can do. */ + 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 (0) { - VG_(printf)("found cfisi: "); - ML_(ppDiCfSI)(di->cfsi_exprs, cfsi); + if (UNLIKELY(ce->di == (DebugInfo*)1)) { + /* no DiCfSI for this address */ + return NULL; + } else { + /* found a DiCfSI for this address */ + return ce; } +} - ipPrev = spPrev = fpPrev = 0; - ipHere = *ipP; - spHere = *spP; - fpHere = *fpP; +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; - /* First compute the CFA. */ + /* Compute the CFA. */ cfa = 0; switch (cfsi->cfa_how) { case CFIC_SPREL: - cfa = cfsi->cfa_off + spHere; + cfa = sp + cfsi->cfa_off; break; case CFIC_FPREL: - cfa = cfsi->cfa_off + fpHere; + cfa = fp + cfsi->cfa_off; break; case CFIC_EXPR: if (0) { @@ -2079,18 +2055,85 @@ Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP, ML_(ppCfiExpr)(di->cfsi_exprs, cfsi->cfa_off); VG_(printf)("\n"); } - eec.ipHere = ipHere; - eec.spHere = spHere; - eec.fpHere = fpHere; + 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 False; + 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. */ +/* Returns True if OK. If not OK, *{ip,sp,fp}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, + Addr min_accessible, + Addr max_accessible ) +{ + Bool ok; + DebugInfo* di; + DiCfSI* cfsi = NULL; + Addr cfa, ipHere, spHere, fpHere, ipPrev, spPrev, fpPrev; + CFSICacheEnt* ce; + CfiExprEvalContext eec; + + ce = cfsi_cache__find(*ipP); + + 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); + } + + ipPrev = spPrev = fpPrev = 0; + + ipHere = *ipP; + spHere = *spP; + fpHere = *fpP; + + /* First compute the CFA. */ + 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 @@ static Bool data_address_is_in_var ( /*OUT*/PtrdiffT* offset, 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 @@ void analyse_deps ( /*MOD*/XArray* /* of FrameBlock */ blocks, 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 @@ void analyse_deps ( /*MOD*/XArray* /* of FrameBlock */ blocks, 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 @@ void analyse_deps ( /*MOD*/XArray* /* of FrameBlock */ blocks, 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 @@ void* /* really, XArray* of GlobalBlock */ 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 1368e0d485..81f46ef95c 100644 --- a/coregrind/m_debuginfo/priv_d3basics.h +++ b/coregrind/m_debuginfo/priv_d3basics.h @@ -522,6 +522,9 @@ typedef enum 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 @@ typedef /* 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 @@ GXResult ML_(evaluate_Dwarf3_Expr) ( UChar* expr, UWord exprszB, 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 dc9fb99504..299daf4cf3 100644 --- a/coregrind/m_debuginfo/priv_readdwarf.h +++ b/coregrind/m_debuginfo/priv_readdwarf.h @@ -62,7 +62,7 @@ void ML_(read_debuginfo_dwarf1) ( struct _DebugInfo* di, -------------------- */ 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 cff91f7d14..b790001c38 100644 --- a/coregrind/m_debuginfo/priv_storage.h +++ b/coregrind/m_debuginfo/priv_storage.h @@ -649,6 +649,11 @@ extern void ML_(addVar)( struct _DebugInfo* di, 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 880aa0e69e..354cf60cf4 100644 --- a/coregrind/m_debuginfo/priv_tytypes.h +++ b/coregrind/m_debuginfo/priv_tytypes.h @@ -78,8 +78,13 @@ typedef 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 15cf70aac0..bde2cf1f3f 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 @@ void ML_(read_debuginfo_dwarf1) ( #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 @@ void ML_(read_debuginfo_dwarf1) ( #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 @@ static CIE the_CIEs[N_CIEs]; 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 @@ void ML_(read_callframe_info_dwarf3) 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 @@ void ML_(read_callframe_info_dwarf3) 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 @@ void ML_(read_callframe_info_dwarf3) 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 @@ void ML_(read_callframe_info_dwarf3) 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 @@ void ML_(read_callframe_info_dwarf3) /* 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 @@ void ML_(read_callframe_info_dwarf3) 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 @@ void ML_(read_callframe_info_dwarf3) } 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 @@ void ML_(read_callframe_info_dwarf3) 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 @@ void ML_(read_callframe_info_dwarf3) 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 @@ void ML_(read_callframe_info_dwarf3) } 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 @@ void ML_(read_callframe_info_dwarf3) 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 @@ void ML_(read_callframe_info_dwarf3) 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 @@ void ML_(read_callframe_info_dwarf3) 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 174b9d2c80..e9ec841d22 100644 --- a/coregrind/m_debuginfo/readdwarf3.c +++ b/coregrind/m_debuginfo/readdwarf3.c @@ -2356,9 +2356,16 @@ static void parse_type_DIE ( /*MOD*/XArray* /* of TyEnt */ tyents, 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 @@ static void parse_type_DIE ( /*MOD*/XArray* /* of TyEnt */ tyents, 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 @@ static void parse_type_DIE ( /*MOD*/XArray* /* of TyEnt */ tyents, /* 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 4011852c63..df7bcb576a 100644 --- a/coregrind/m_debuginfo/readelf.c +++ b/coregrind/m_debuginfo/readelf.c @@ -1691,6 +1691,7 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di ) 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 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di ) 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 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di ) 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 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di ) 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 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di ) 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 5e318fd8bc..eab3280313 100644 --- a/coregrind/m_debuginfo/storage.c +++ b/coregrind/m_debuginfo/storage.c @@ -1435,7 +1435,7 @@ static Int compare_DiCfSI ( void* va, void* vb ) 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 @@ void ML_(canonicaliseTables) ( struct _DebugInfo* di ) { 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 4e3c9eea20..46edf9779c 100644 --- a/coregrind/m_debuginfo/tytypes.c +++ b/coregrind/m_debuginfo/tytypes.c @@ -98,10 +98,15 @@ void ML_(pp_TyEnt)( TyEnt* te ) 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 @@ Word ML_(TyEnt__cmp_by_all_except_cuOff) ( TyEnt* te1, TyEnt* te2 ) 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 @@ void ML_(TyEnt__make_EMPTY) ( TyEnt* te ) 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 @@ XArray* /*UChar*/ ML_(describe_type)( /*OUT*/PtrdiffT* residual_offset, 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)