--- valgrind/coregrind/m_debuginfo/readdwarf.c.jj 2009-07-13 14:09:14.478329957 +0200 +++ valgrind/coregrind/m_debuginfo/readdwarf.c 2009-07-13 15:18:35.596080042 +0200 @@ -1923,6 +1923,11 @@ static void ppRegRule ( XArray* exprs, R } +/* Size of the stack of register unwind rules. This is only + exceedingly rarely used, so a stack of size 1 should actually work + with almost all compiler-generated CFA. */ +#define N_RR_STACK 4 + typedef struct { /* Read-only fields (set by the CIE) */ @@ -1939,8 +1944,12 @@ typedef Int cfa_reg; Int cfa_off; /* in bytes */ Int cfa_expr_ix; /* index into cfa_exprs */ - /* register unwind rules */ - RegRule reg[N_CFI_REGS]; + /* A stack of register unwind rules. We need a stack of them, + rather than just one set of rules, in order to handle + DW_CFA_{remember,restore}_state. */ + RegRule reg[N_RR_STACK][N_CFI_REGS]; + Int reg_sp; /* 0 <= reg_sp < N_RR_STACK; points at the + currently-in-use rule set. */ /* array of CfiExpr, shared by reg[] and cfa_expr_ix */ XArray* exprs; } @@ -1948,7 +1957,7 @@ typedef static void ppUnwindContext ( UnwindContext* ctx ) { - Int i; + Int j, i; VG_(printf)("0x%llx: ", (ULong)ctx->loc); if (ctx->cfa_is_regoff) { VG_(printf)("%d(r%d) ", ctx->cfa_off, ctx->cfa_reg); @@ -1958,14 +1967,19 @@ static void ppUnwindContext ( UnwindCont ML_(ppCfiExpr)( ctx->exprs, ctx->cfa_expr_ix ); VG_(printf)("} "); } - for (i = 0; i < N_CFI_REGS; i++) - ppRegRule(ctx->exprs, &ctx->reg[i]); + for (j = 0; j <= ctx->reg_sp; j++) { + VG_(printf)("%s[%d]={ ", j > 0 ? " " : "", j); + for (i = 0; i < N_CFI_REGS; i++) + ppRegRule(ctx->exprs, &ctx->reg[j][i]); + VG_(printf)("}"); + } VG_(printf)("\n"); } static void initUnwindContext ( /*OUT*/UnwindContext* ctx ) { - Int i; + Int j, i; + VG_(memset)(ctx, 0, sizeof(*ctx)); ctx->code_a_f = 0; ctx->data_a_f = 0; ctx->initloc = 0; @@ -1976,9 +1990,12 @@ static void initUnwindContext ( /*OUT*/U ctx->cfa_off = 0; ctx->cfa_expr_ix = 0; ctx->exprs = NULL; - for (i = 0; i < N_CFI_REGS; i++) { - ctx->reg[i].tag = RR_Undef; - ctx->reg[i].arg = 0; + ctx->reg_sp = 0; + for (j = 0; j < N_RR_STACK; j++) { + for (i = 0; i < N_CFI_REGS; i++) { + ctx->reg[j][i].tag = RR_Undef; + ctx->reg[j][i].arg = 0; + } } } @@ -2104,8 +2121,15 @@ static Bool summarise_context( /*OUT*/Di why = 2; goto failed; /* otherwise give up */ \ } - SUMMARISE_HOW(si->ra_how, si->ra_off, ctx->reg[ctx->ra_reg] ); - SUMMARISE_HOW(si->fp_how, si->fp_off, ctx->reg[FP_REG] ); + /* Guard against obviously stupid settings of the reg-rule stack + pointer. */ + if (ctx->reg_sp < 0) { why = 8; goto failed; } + if (ctx->reg_sp >= N_RR_STACK) { why = 9; goto failed; } + + SUMMARISE_HOW(si->ra_how, si->ra_off, + ctx->reg[ctx->reg_sp][ctx->ra_reg] ); + SUMMARISE_HOW(si->fp_how, si->fp_off, + ctx->reg[ctx->reg_sp][FP_REG] ); # undef SUMMARISE_HOW @@ -2116,7 +2140,7 @@ static Bool summarise_context( /*OUT*/Di /* also, gcc says "Undef" for %{e,r}bp when it is unchanged. So .. */ - if (ctx->reg[FP_REG].tag == RR_Undef) + if (ctx->reg[ctx->reg_sp][FP_REG].tag == RR_Undef) si->fp_how = CFIR_SAME; /* knock out some obviously stupid cases */ @@ -2215,10 +2239,10 @@ static void ppUnwindContext_summary ( Un } VG_(printf)("RA="); - ppRegRule( ctx->exprs, &ctx->reg[ctx->ra_reg] ); + ppRegRule( ctx->exprs, &ctx->reg[ctx->reg_sp][ctx->ra_reg] ); VG_(printf)("FP="); - ppRegRule( ctx->exprs, &ctx->reg[FP_REG] ); + ppRegRule( ctx->exprs, &ctx->reg[ctx->reg_sp][FP_REG] ); VG_(printf)("\n"); } @@ -2664,6 +2688,9 @@ static Int run_CF_instruction ( /*MOD*/U Addr printing_bias = ((Addr)ctx->initloc) - ((Addr)di->text_bias); i++; + if (ctx->reg_sp < 0 || ctx->reg_sp >= N_RR_STACK) + return 0; /* bogus reg-rule stack pointer */ + if (hi2 == DW_CFA_advance_loc) { delta = (UInt)lo6; ctx->loc += delta; @@ -2680,12 +2707,13 @@ static Int run_CF_instruction ( /*MOD*/U reg = (Int)lo6; if (reg < 0 || reg >= N_CFI_REGS) return 0; /* fail */ - ctx->reg[reg].tag = RR_CFAOff; - ctx->reg[reg].arg = off * ctx->data_a_f; + ctx->reg[ctx->reg_sp][reg].tag = RR_CFAOff; + ctx->reg[ctx->reg_sp][reg].arg = off * ctx->data_a_f; if (di->ddump_frames) VG_(printf)(" DW_CFA_offset: r%d at cfa%s%d\n", - (Int)reg, ctx->reg[reg].arg < 0 ? "" : "+", - (Int)ctx->reg[reg].arg ); + (Int)reg, + ctx->reg[ctx->reg_sp][reg].arg < 0 ? "" : "+", + (Int)ctx->reg[ctx->reg_sp][reg].arg ); return i; } @@ -2695,7 +2723,7 @@ static Int run_CF_instruction ( /*MOD*/U return 0; /* fail */ if (restore_ctx == NULL) return 0; /* fail */ - ctx->reg[reg] = restore_ctx->reg[reg]; + ctx->reg[ctx->reg_sp][reg] = restore_ctx->reg[ctx->reg_sp][reg]; if (di->ddump_frames) VG_(printf)(" DW_CFA_restore: r%d\n", (Int)reg); return i; @@ -2781,8 +2809,8 @@ static Int run_CF_instruction ( /*MOD*/U return 0; /* fail */ if (reg2 < 0 || reg2 >= N_CFI_REGS) return 0; /* fail */ - ctx->reg[reg].tag = RR_Reg; - ctx->reg[reg].arg = reg2; + ctx->reg[ctx->reg_sp][reg].tag = RR_Reg; + ctx->reg[ctx->reg_sp][reg].arg = reg2; if (di->ddump_frames) VG_(printf)(" DW_CFA_register: r%d in r%d\n", (Int)reg, (Int)reg2); @@ -2795,8 +2823,8 @@ static Int run_CF_instruction ( /*MOD*/U i += nleb; if (reg < 0 || reg >= N_CFI_REGS) return 0; /* fail */ - ctx->reg[reg].tag = RR_CFAOff; - ctx->reg[reg].arg = off * ctx->data_a_f; + ctx->reg[ctx->reg_sp][reg].tag = RR_CFAOff; + ctx->reg[ctx->reg_sp][reg].arg = off * ctx->data_a_f; if (di->ddump_frames) VG_(printf)(" rci:DW_CFA_offset_extended\n"); break; @@ -2808,12 +2836,13 @@ static Int run_CF_instruction ( /*MOD*/U i += nleb; if (reg < 0 || reg >= N_CFI_REGS) return 0; /* fail */ - ctx->reg[reg].tag = RR_CFAOff; - ctx->reg[reg].arg = off * ctx->data_a_f; + ctx->reg[ctx->reg_sp][reg].tag = RR_CFAOff; + ctx->reg[ctx->reg_sp][reg].arg = off * ctx->data_a_f; if (di->ddump_frames) VG_(printf)(" DW_CFA_offset_extended_sf: r%d at cfa%s%d\n", - reg, ctx->reg[reg].arg < 0 ? "" : "+", - (Int)ctx->reg[reg].arg); + reg, + ctx->reg[ctx->reg_sp][reg].arg < 0 ? "" : "+", + (Int)ctx->reg[ctx->reg_sp][reg].arg); break; case DW_CFA_GNU_negative_offset_extended: @@ -2823,8 +2852,8 @@ static Int run_CF_instruction ( /*MOD*/U i += nleb; if (reg < 0 || reg >= N_CFI_REGS) return 0; /* fail */ - ctx->reg[reg].tag = RR_CFAOff; - ctx->reg[reg].arg = (-off) * ctx->data_a_f; + ctx->reg[ctx->reg_sp][reg].tag = RR_CFAOff; + ctx->reg[ctx->reg_sp][reg].arg = (-off) * ctx->data_a_f; if (di->ddump_frames) VG_(printf)(" rci:DW_CFA_GNU_negative_offset_extended\n"); break; @@ -2836,7 +2865,7 @@ static Int run_CF_instruction ( /*MOD*/U return 0; /* fail */ if (restore_ctx == NULL) return 0; /* fail */ - ctx->reg[reg] = restore_ctx->reg[reg]; + ctx->reg[ctx->reg_sp][reg] = restore_ctx->reg[ctx->reg_sp][reg]; if (di->ddump_frames) VG_(printf)(" rci:DW_CFA_restore_extended\n"); break; @@ -2848,8 +2877,8 @@ static Int run_CF_instruction ( /*MOD*/U i += nleb; if (reg < 0 || reg >= N_CFI_REGS) return 0; /* fail */ - ctx->reg[reg].tag = RR_CFAValOff; - ctx->reg[reg].arg = off * ctx->data_a_f; + ctx->reg[ctx->reg_sp][reg].tag = RR_CFAValOff; + ctx->reg[ctx->reg_sp][reg].arg = off * ctx->data_a_f; if (di->ddump_frames) VG_(printf)(" rci:DW_CFA_val_offset\n"); break; @@ -2861,8 +2890,8 @@ static Int run_CF_instruction ( /*MOD*/U i += nleb; if (reg < 0 || reg >= N_CFI_REGS) return 0; /* fail */ - ctx->reg[reg].tag = RR_CFAValOff; - ctx->reg[reg].arg = off * ctx->data_a_f; + ctx->reg[ctx->reg_sp][reg].tag = RR_CFAValOff; + ctx->reg[ctx->reg_sp][reg].arg = off * ctx->data_a_f; if (di->ddump_frames) VG_(printf)(" rci:DW_CFA_val_offset_sf\n"); break; @@ -2907,8 +2936,8 @@ static Int run_CF_instruction ( /*MOD*/U i += nleb; if (reg < 0 || reg >= N_CFI_REGS) return 0; /* fail */ - ctx->reg[reg].tag = RR_Undef; - ctx->reg[reg].arg = 0; + ctx->reg[ctx->reg_sp][reg].tag = RR_Undef; + ctx->reg[ctx->reg_sp][reg].arg = 0; if (di->ddump_frames) VG_(printf)(" rci:DW_CFA_undefined\n"); break; @@ -2952,8 +2981,8 @@ static Int run_CF_instruction ( /*MOD*/U return 0; /* fail */ /* Add an extra dereference */ j = ML_(CfiExpr_Deref)( ctx->exprs, j ); - ctx->reg[reg].tag = RR_ValExpr; - ctx->reg[reg].arg = j; + ctx->reg[ctx->reg_sp][reg].tag = RR_ValExpr; + ctx->reg[ctx->reg_sp][reg].arg = j; break; case DW_CFA_val_expression: @@ -2981,8 +3010,8 @@ static Int run_CF_instruction ( /*MOD*/U } if (j == -1) return 0; /* fail */ - ctx->reg[reg].tag = RR_ValExpr; - ctx->reg[reg].arg = j; + ctx->reg[ctx->reg_sp][reg].tag = RR_ValExpr; + ctx->reg[ctx->reg_sp][reg].arg = j; break; case DW_CFA_def_cfa_expression: @@ -3008,7 +3037,39 @@ static Int run_CF_instruction ( /*MOD*/U /* Ignored. This appears to be sparc-specific; quite why it turns up in SuSE-supplied x86 .so's beats me. */ if (di->ddump_frames) - VG_(printf)("DW_CFA_GNU_window_save\n"); + VG_(printf)(" DW_CFA_GNU_window_save\n"); + break; + + case DW_CFA_remember_state: + if (di->ddump_frames) + VG_(printf)(" DW_CFA_remember_state\n"); + /* we just checked this at entry, so: */ + vg_assert(ctx->reg_sp >= 0 && ctx->reg_sp < N_RR_STACK); + ctx->reg_sp++; + if (ctx->reg_sp == N_RR_STACK) { + /* stack overflow. We're hosed. */ + VG_(message)(Vg_DebugMsg, "DWARF2 CFI reader: N_RR_STACK is " + "too low; increase and recompile."); + i = 0; /* indicate failure */ + } else { + VG_(memcpy)(/*dst*/&ctx->reg[ctx->reg_sp], + /*src*/&ctx->reg[ctx->reg_sp - 1], + sizeof(ctx->reg[ctx->reg_sp]) ); + } + break; + + case DW_CFA_restore_state: + if (di->ddump_frames) + VG_(printf)(" DW_CFA_restore_state\n"); + /* we just checked this at entry, so: */ + vg_assert(ctx->reg_sp >= 0 && ctx->reg_sp < N_RR_STACK); + if (ctx->reg_sp == 0) { + /* stack overflow. Give up. */ + i = 0; /* indicate failure */ + } else { + /* simply fall back to previous entry */ + ctx->reg_sp--; + } break; default: