diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 |
commit | cfca06d7963fa0909f90483b42a6d7d194d01e08 (patch) | |
tree | 209fb2a2d68f8f277793fc8df46c753d31bc853b /libunwind/src/DwarfParser.hpp | |
parent | 706b4fc47bbc608932d3b491ae19a3b9cde9497b (diff) |
Notes
Diffstat (limited to 'libunwind/src/DwarfParser.hpp')
-rw-r--r-- | libunwind/src/DwarfParser.hpp | 129 |
1 files changed, 87 insertions, 42 deletions
diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp index df69c2a4bd23..d05ac468367f 100644 --- a/libunwind/src/DwarfParser.hpp +++ b/libunwind/src/DwarfParser.hpp @@ -77,6 +77,7 @@ public: }; struct RegisterLocation { RegisterSavedWhere location; + bool initialStateSaved; int64_t value; }; /// Information about a frame layout and registers saved determined @@ -90,6 +91,40 @@ public: bool registersInOtherRegisters; bool sameValueUsed; RegisterLocation savedRegisters[kMaxRegisterNumber + 1]; + enum class InitializeTime { kLazy, kNormal }; + + // When saving registers, this data structure is lazily initialized. + PrologInfo(InitializeTime IT = InitializeTime::kNormal) { + if (IT == InitializeTime::kNormal) + memset(this, 0, sizeof(*this)); + } + 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 { @@ -355,18 +390,30 @@ bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo, const CIE_Info &cieInfo, pint_t upToPC, int arch, PrologInfo *results) { - // clear results - memset(results, '\0', sizeof(PrologInfo)); PrologInfoStackEntry *rememberStack = NULL; // parse CIE then FDE instructions - return parseInstructions(addressSpace, cieInfo.cieInstructions, - cieInfo.cieStart + cieInfo.cieLength, cieInfo, - (pint_t)(-1), rememberStack, arch, results) && - parseInstructions(addressSpace, fdeInfo.fdeInstructions, - fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo, - upToPC - fdeInfo.pcStart, rememberStack, arch, - results); + bool returnValue = + parseInstructions(addressSpace, cieInfo.cieInstructions, + cieInfo.cieStart + cieInfo.cieLength, cieInfo, + (pint_t)(-1), rememberStack, arch, results) && + parseInstructions(addressSpace, fdeInfo.fdeInstructions, + fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo, + upToPC - fdeInfo.pcStart, rememberStack, arch, results); + +#if !defined(_LIBUNWIND_NO_HEAP) + // Clean up rememberStack. Even in the case where every DW_CFA_remember_state + // is paired with a DW_CFA_restore_state, parseInstructions can skip restore + // opcodes if it reaches the target PC and stops interpreting, so we have to + // make sure we don't leak memory. + while (rememberStack) { + PrologInfoStackEntry *next = rememberStack->next; + free(rememberStack); + rememberStack = next; + } +#endif + + return returnValue; } /// "run" the DWARF instructions @@ -378,7 +425,9 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, int arch, PrologInfo *results) { pint_t p = instructions; pint_t codeOffset = 0; - PrologInfo initialState = *results; + // initialState initialized as registers in results are modified. Use + // PrologInfo accessor functions to avoid reading uninitialized data. + PrologInfo initialState(PrologInfo::InitializeTime::kLazy); _LIBUNWIND_TRACE_DWARF("parseInstructions(instructions=0x%0" PRIx64 ")\n", static_cast<uint64_t>(instructionsEnd)); @@ -431,8 +480,7 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, "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); @@ -444,7 +492,7 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, "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: @@ -454,7 +502,7 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, "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: @@ -468,7 +516,7 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, // "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); @@ -486,8 +534,8 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, "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( @@ -564,8 +612,8 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, "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<pint_t>(~0) && "pointer overflow"); p += static_cast<pint_t>(length); @@ -583,8 +631,7 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, } 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); @@ -622,8 +669,7 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, } 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); @@ -637,8 +683,7 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, } 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); @@ -650,8 +695,8 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, "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<pint_t>(~0) && "pointer overflow"); p += static_cast<pint_t>(length); @@ -673,8 +718,7 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, } 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; @@ -687,25 +731,27 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, 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 @@ -728,8 +774,7 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, } 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; @@ -746,7 +791,7 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, reg); return false; } - results->savedRegisters[reg] = initialState.savedRegisters[reg]; + results->restoreRegisterToInitialState(reg, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n", static_cast<uint64_t>(operand)); break; |