diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp --- a/libunwind/src/DwarfParser.hpp +++ b/libunwind/src/DwarfParser.hpp @@ -77,6 +77,7 @@ }; struct RegisterLocation { RegisterSavedWhere location; + bool initialStateSaved; int64_t value; }; /// Information about a frame layout and registers saved determined @@ -90,6 +91,33 @@ bool registersInOtherRegisters; bool sameValueUsed; RegisterLocation savedRegisters[kMaxRegisterNumber + 1]; + void checkSaveRegister(uint64_t reg, PrologInfo &initialState) { + if (!savedRegisters[reg].initialStateSaved) { + initialState.savedRegisters[reg] = savedRegisters[reg]; + savedRegisters[reg].initialStateSaved = true; + } + } + void setRegister(uint64_t reg, RegisterSavedWhere newLocation, + int64_t newValue, PrologInfo &initialState) { + checkSaveRegister(reg, initialState); + savedRegisters[reg].location = newLocation; + savedRegisters[reg].value = newValue; + } + void setRegisterLocation(uint64_t reg, RegisterSavedWhere newLocation, + PrologInfo &initialState) { + checkSaveRegister(reg, initialState); + savedRegisters[reg].location = newLocation; + } + void setRegisterValue(uint64_t reg, int64_t newValue, + PrologInfo &initialState) { + checkSaveRegister(reg, initialState); + savedRegisters[reg].value = newValue; + } + void restoreRegisterToInitialState(uint64_t reg, PrologInfo &initialState) { + if (savedRegisters[reg].initialStateSaved) + savedRegisters[reg] = initialState.savedRegisters[reg]; + // else the register still holds its initial state + } }; struct PrologInfoStackEntry { @@ -390,7 +418,8 @@ int arch, PrologInfo *results) { pint_t p = instructions; pint_t codeOffset = 0; - PrologInfo initialState = *results; + // These values are lazily initialized as registers in results are modified. + PrologInfo initialState; _LIBUNWIND_TRACE_DWARF("parseInstructions(instructions=0x%0" PRIx64 ")\n", static_cast(instructionsEnd)); @@ -443,8 +472,7 @@ "malformed DW_CFA_offset_extended DWARF unwind, reg too big"); return false; } - results->savedRegisters[reg].location = kRegisterInCFA; - results->savedRegisters[reg].value = offset; + results->setRegister(reg, kRegisterInCFA, offset, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended(reg=%" PRIu64 ", " "offset=%" PRId64 ")\n", reg, offset); @@ -456,7 +484,7 @@ "malformed DW_CFA_restore_extended DWARF unwind, reg too big"); return false; } - results->savedRegisters[reg] = initialState.savedRegisters[reg]; + results->restoreRegisterToInitialState(reg, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_extended(reg=%" PRIu64 ")\n", reg); break; case DW_CFA_undefined: @@ -466,7 +494,7 @@ "malformed DW_CFA_undefined DWARF unwind, reg too big"); return false; } - results->savedRegisters[reg].location = kRegisterUnused; + results->setRegisterLocation(reg, kRegisterUnused, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_undefined(reg=%" PRIu64 ")\n", reg); break; case DW_CFA_same_value: @@ -480,7 +508,7 @@ // "same value" means register was stored in frame, but its current // value has not changed, so no need to restore from frame. // We model this as if the register was never saved. - results->savedRegisters[reg].location = kRegisterUnused; + results->setRegisterLocation(reg, kRegisterUnused, initialState); // set flag to disable conversion to compact unwind results->sameValueUsed = true; _LIBUNWIND_TRACE_DWARF("DW_CFA_same_value(reg=%" PRIu64 ")\n", reg); @@ -498,8 +526,8 @@ "malformed DW_CFA_register DWARF unwind, reg2 too big"); return false; } - results->savedRegisters[reg].location = kRegisterInRegister; - results->savedRegisters[reg].value = (int64_t)reg2; + results->setRegister(reg, kRegisterInRegister, (int64_t)reg2, + initialState); // set flag to disable conversion to compact unwind results->registersInOtherRegisters = true; _LIBUNWIND_TRACE_DWARF( @@ -576,8 +604,8 @@ "malformed DW_CFA_expression DWARF unwind, reg too big"); return false; } - results->savedRegisters[reg].location = kRegisterAtExpression; - results->savedRegisters[reg].value = (int64_t)p; + results->setRegister(reg, kRegisterAtExpression, (int64_t)p, + initialState); length = addressSpace.getULEB128(p, instructionsEnd); assert(length < static_cast(~0) && "pointer overflow"); p += static_cast(length); @@ -595,8 +623,7 @@ } offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; - results->savedRegisters[reg].location = kRegisterInCFA; - results->savedRegisters[reg].value = offset; + results->setRegister(reg, kRegisterInCFA, offset, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended_sf(reg=%" PRIu64 ", " "offset=%" PRId64 ")\n", reg, offset); @@ -634,8 +661,7 @@ } offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; - results->savedRegisters[reg].location = kRegisterOffsetFromCFA; - results->savedRegisters[reg].value = offset; + results->setRegister(reg, kRegisterOffsetFromCFA, offset, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset(reg=%" PRIu64 ", " "offset=%" PRId64 "\n", reg, offset); @@ -649,8 +675,7 @@ } offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; - results->savedRegisters[reg].location = kRegisterOffsetFromCFA; - results->savedRegisters[reg].value = offset; + results->setRegister(reg, kRegisterOffsetFromCFA, offset, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset_sf(reg=%" PRIu64 ", " "offset=%" PRId64 "\n", reg, offset); @@ -662,8 +687,8 @@ "malformed DW_CFA_val_expression DWARF unwind, reg too big"); return false; } - results->savedRegisters[reg].location = kRegisterIsExpression; - results->savedRegisters[reg].value = (int64_t)p; + results->setRegister(reg, kRegisterIsExpression, (int64_t)p, + initialState); length = addressSpace.getULEB128(p, instructionsEnd); assert(length < static_cast(~0) && "pointer overflow"); p += static_cast(length); @@ -685,8 +710,7 @@ } offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; - results->savedRegisters[reg].location = kRegisterInCFA; - results->savedRegisters[reg].value = -offset; + results->setRegister(reg, kRegisterInCFA, -offset, initialState); _LIBUNWIND_TRACE_DWARF( "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset); break; @@ -699,25 +723,27 @@ case DW_CFA_AARCH64_negate_ra_state: switch (arch) { #if defined(_LIBUNWIND_TARGET_AARCH64) - case REGISTERS_ARM64: - results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^= 0x1; - _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n"); - break; + case REGISTERS_ARM64: { + int64_t value = + results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^ 0x1; + results->setRegisterValue(UNW_ARM64_RA_SIGN_STATE, value, initialState); + _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n"); + } break; #endif + #if defined(_LIBUNWIND_TARGET_SPARC) // case DW_CFA_GNU_window_save: case REGISTERS_SPARC: _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save()\n"); for (reg = UNW_SPARC_O0; reg <= UNW_SPARC_O7; reg++) { - results->savedRegisters[reg].location = kRegisterInRegister; - results->savedRegisters[reg].value = - ((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0; + results->setRegister(reg, kRegisterInRegister, + ((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0, + initialState); } for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) { - results->savedRegisters[reg].location = kRegisterInCFA; - results->savedRegisters[reg].value = - ((int64_t)reg - UNW_SPARC_L0) * 4; + results->setRegister(reg, kRegisterInCFA, + ((int64_t)reg - UNW_SPARC_L0) * 4, initialState); } break; #endif @@ -740,8 +766,7 @@ } offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; - results->savedRegisters[reg].location = kRegisterInCFA; - results->savedRegisters[reg].value = offset; + results->setRegister(reg, kRegisterInCFA, offset, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n", operand, offset); break; @@ -758,7 +783,7 @@ reg); return false; } - results->savedRegisters[reg] = initialState.savedRegisters[reg]; + results->restoreRegisterToInitialState(reg, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n", static_cast(operand)); break;