diff --git a/lldb/include/lldb/Symbol/UnwindPlan.h b/lldb/include/lldb/Symbol/UnwindPlan.h --- a/lldb/include/lldb/Symbol/UnwindPlan.h +++ b/lldb/include/lldb/Symbol/UnwindPlan.h @@ -215,9 +215,13 @@ bool IsUnspecified() const { return m_type == unspecified; } - void SetRaSearch(int32_t offset) { + bool IsRaSearch() const { return m_type == isRaSearch; } + + void SetRaSearch(int32_t search_offset, int32_t cfa_offset) { + m_value.ra_search.search_offset = search_offset; + m_value.ra_search.cfa_offset = cfa_offset; + m_value.ra_search.is_first_search = m_type == unspecified; m_type = isRaSearch; - m_value.ra_search_offset = offset; } bool IsRegisterPlusOffset() const { @@ -255,12 +259,18 @@ ValueType GetValueType() const { return m_type; } + int32_t GetRaSearchOffset() const { + return m_type == isRaSearch ? m_value.ra_search.search_offset : 0; + } + + bool IsFirstRaSearch() const { return m_value.ra_search.is_first_search; } + int32_t GetOffset() const { switch (m_type) { case isRegisterPlusOffset: return m_value.reg.offset; case isRaSearch: - return m_value.ra_search_offset; + return m_value.ra_search.cfa_offset; default: return 0; } @@ -316,7 +326,11 @@ uint16_t length; } expr; // For m_type == isRaSearch - int32_t ra_search_offset; + struct { + bool is_first_search; + int32_t search_offset; + int32_t cfa_offset; + } ra_search; } m_value; }; // class FAValue diff --git a/lldb/include/lldb/Target/ABI.h b/lldb/include/lldb/Target/ABI.h --- a/lldb/include/lldb/Target/ABI.h +++ b/lldb/include/lldb/Target/ABI.h @@ -96,6 +96,10 @@ lldb::ProcessSP GetProcessSP() const { return m_process_wp.lock(); } public: + virtual bool CreateStackWalkingUnwindPlan(UnwindPlan &unwind_plan) { + return false; + } + virtual bool CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) = 0; virtual bool CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) = 0; diff --git a/lldb/include/lldb/Target/RegisterContextUnwind.h b/lldb/include/lldb/Target/RegisterContextUnwind.h --- a/lldb/include/lldb/Target/RegisterContextUnwind.h +++ b/lldb/include/lldb/Target/RegisterContextUnwind.h @@ -206,7 +206,7 @@ bool IsUnwindPlanValidForCurrentPC(lldb::UnwindPlanSP unwind_plan_sp, int &valid_pc_offset); - lldb::addr_t GetReturnAddressHint(int32_t plan_offset); + lldb::addr_t GetParameterStackSize(); lldb_private::Thread &m_thread; diff --git a/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.h b/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.h --- a/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.h +++ b/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.h @@ -33,6 +33,9 @@ GetReturnValueObjectImpl(lldb_private::Thread &thread, lldb_private::CompilerType &type) const override; + bool + CreateStackWalkingUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override; + bool CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override; diff --git a/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp b/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp --- a/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp +++ b/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp @@ -731,6 +731,27 @@ return return_valobj_sp; } +bool ABIWindows_x86_64::CreateStackWalkingUnwindPlan(UnwindPlan &unwind_plan) { + unwind_plan.Clear(); + unwind_plan.SetRegisterKind(eRegisterKindLLDB); + + uint32_t sp_reg_num = dwarf_rsp; + uint32_t pc_reg_num = dwarf_rip; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + row->GetCFAValue().SetRaSearch(0, 8); + row->SetUnspecifiedRegistersAreUndefined(true); + + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -8, true); + row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); + unwind_plan.AppendRow(row); + unwind_plan.SetSourceName("x86_64 stack-walking unwind plan"); + unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); + unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); + return true; +} + // This defines the CFA as rsp+8 // the saved pc is at CFA-8 (i.e. rsp+0) // The saved rsp is CFA+0 diff --git a/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp b/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp --- a/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp +++ b/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp @@ -740,8 +740,8 @@ // clang will use T1, if it needs to realign the stack. auto *symbol = llvm::dyn_cast(it->second); if (symbol && symbol->GetName() == ".raSearch") { - row_sp->GetCFAValue().SetRaSearch(record->LocalSize + - record->SavedRegisterSize); + row_sp->GetCFAValue().SetRaSearch( + record->LocalSize + record->SavedRegisterSize, 0); } else { if (!postfix::ResolveSymbols(it->second, symbol_resolver)) { LLDB_LOG(log, "Resolving symbols in `{0}` failed.", diff --git a/lldb/source/Symbol/UnwindPlan.cpp b/lldb/source/Symbol/UnwindPlan.cpp --- a/lldb/source/Symbol/UnwindPlan.cpp +++ b/lldb/source/Symbol/UnwindPlan.cpp @@ -169,8 +169,11 @@ if (m_type == rhs.m_type) { switch (m_type) { case unspecified: + return true; case isRaSearch: - return m_value.ra_search_offset == rhs.m_value.ra_search_offset; + return m_value.ra_search.search_offset == + rhs.m_value.ra_search.search_offset && + m_value.ra_search.cfa_offset == rhs.m_value.ra_search.cfa_offset; case isRegisterPlusOffset: return m_value.reg.offset == rhs.m_value.reg.offset; @@ -209,7 +212,8 @@ s.PutCString("unspecified"); break; case isRaSearch: - s.Printf("RaSearch@SP%+d", m_value.ra_search_offset); + s.Printf("RaSearch@SP%+d, offset=%+d", m_value.ra_search.search_offset, + GetOffset()); break; } } diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -285,7 +285,18 @@ cfa_status = true; } if (!cfa_status) { - UnwindLogMsg("could not read CFA value for first frame."); + UnwindLogMsg("could not read CFA value for first frame. Trying stack " + "walking unwind plan."); + if (abi) { + UnwindPlanSP stack_walk_unwind_plan = + std::make_shared(lldb::eRegisterKindGeneric); + if (abi->CreateStackWalkingUnwindPlan(*stack_walk_unwind_plan)) { + m_fallback_unwind_plan_sp = stack_walk_unwind_plan; + cfa_status = TryFallbackUnwindPlan(); + } + } + } + if (!cfa_status) { m_frame_type = eNotAValidFrame; return; } @@ -384,7 +395,7 @@ // symbol/function information - just stick in some reasonable defaults and // hope we can unwind past this frame. If we're above a trap handler, // we may be at a bogus address because we jumped through a bogus function - // pointer and trapped, so don't force the arch default unwind plan in that + // pointer and trapped, so don't force the arch default unwind plan in that // case. ModuleSP pc_module_sp(m_current_pc.GetModule()); if ((!m_current_pc.IsValid() || !pc_module_sp) && @@ -660,9 +671,20 @@ } if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), m_cfa)) { - UnwindLogMsg("failed to get cfa"); - m_frame_type = eNotAValidFrame; - return; + UnwindLogMsg("failed to get cfa. Trying stack walking unwind plan."); + bool cfa_status = false; + if (abi) { + UnwindPlanSP stack_walk_unwind_plan = + std::make_shared(lldb::eRegisterKindGeneric); + if (abi->CreateStackWalkingUnwindPlan(*stack_walk_unwind_plan)) { + m_fallback_unwind_plan_sp = stack_walk_unwind_plan; + cfa_status = TryFallbackUnwindPlan(); + } + } + if (!cfa_status) { + m_frame_type = eNotAValidFrame; + return; + } } ReadFrameAddress(row_register_kind, active_row->GetAFAValue(), m_afa); @@ -1287,7 +1309,7 @@ // arch default unwind plan is used as the Fast Unwind Plan, we // need to recognize this & switch over to the Full Unwind Plan // to see what unwind rule that (more knoweldgeable, probably) - // UnwindPlan has. If the full UnwindPlan says the register + // UnwindPlan has. If the full UnwindPlan says the register // location is Undefined, then it really is. if (active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc) && @@ -1335,14 +1357,14 @@ m_full_unwind_plan_sp->GetReturnAddressRegister() != LLDB_INVALID_REGNUM) { // If this is a trap handler frame, we should have access to - // the complete register context when the interrupt/async + // the complete register context when the interrupt/async // signal was received, we should fetch the actual saved $pc // value instead of the Return Address register. // If $pc is not available, fall back to the RA reg. UnwindPlan::Row::RegisterLocation scratch; if (m_frame_type == eTrapHandlerFrame && - active_row->GetRegisterInfo - (pc_regnum.GetAsKind (unwindplan_registerkind), scratch)) { + active_row->GetRegisterInfo( + pc_regnum.GetAsKind(unwindplan_registerkind), scratch)) { UnwindLogMsg("Providing pc register instead of rewriting to " "RA reg because this is a trap handler and there is " "a location for the saved pc register value."); @@ -1710,12 +1732,6 @@ if (m_full_unwind_plan_sp.get() == nullptr) return false; - if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() || - m_full_unwind_plan_sp->GetSourceName() == - m_fallback_unwind_plan_sp->GetSourceName()) { - return false; - } - // If a compiler generated unwind plan failed, trying the arch default // unwindplan isn't going to do any better. if (m_full_unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) @@ -1771,71 +1787,86 @@ UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); - + UnwindPlan::Row::FAValue &fa = active_row->GetCFAValue(); if (active_row && - active_row->GetCFAValue().GetValueType() != - UnwindPlan::Row::FAValue::unspecified) { + fa.GetValueType() != UnwindPlan::Row::FAValue::unspecified) { addr_t new_cfa; - if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(), - active_row->GetCFAValue(), new_cfa) || - new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) { - UnwindLogMsg("failed to get cfa with fallback unwindplan"); - m_fallback_unwind_plan_sp.reset(); - m_full_unwind_plan_sp = original_full_unwind_plan_sp; - return false; + // If itn's not first attempt and ra search, update ra search offset before + // searching for cfa. Previous attempts may fail due to bad pc. + if (fa.IsRaSearch() && old_cfa != LLDB_INVALID_ADDRESS) { + addr_t sp; + if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp)) { + UnwindLogMsg("failed to get cfa with fallback unwindplan"); + m_fallback_unwind_plan_sp.reset(); + m_full_unwind_plan_sp = original_full_unwind_plan_sp; + return false; + } + Process &process = *m_thread.GetProcess(); + // It's the next stack offset location so that sp + next_search_offset == + // old_cfa + process.GetAddressByteSize(). + addr_t next_search_offset = + old_cfa - sp - fa.GetOffset() + process.GetAddressByteSize(); + fa.SetRaSearch(next_search_offset, fa.GetOffset()); } - m_cfa = new_cfa; - - ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(), - active_row->GetAFAValue(), m_afa); + if (ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(), fa, + new_cfa) || + new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) { - if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB), - regloc) == - UnwindLLDB::RegisterSearchResult::eRegisterFound) { - const RegisterInfo *reg_info = - GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB)); - if (reg_info) { - RegisterValue reg_value; - if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, - reg_value)) { - new_caller_pc_value = reg_value.GetAsUInt64(); - if (ProcessSP process_sp = m_thread.GetProcess()) { - if (ABISP abi = process_sp->GetABI()) - new_caller_pc_value = abi->FixCodeAddress(new_caller_pc_value); + m_cfa = new_cfa; + + ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(), + active_row->GetAFAValue(), m_afa); + + if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB), + regloc) == + UnwindLLDB::RegisterSearchResult::eRegisterFound) { + const RegisterInfo *reg_info = + GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB)); + if (reg_info) { + RegisterValue reg_value; + if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, + reg_value)) { + new_caller_pc_value = reg_value.GetAsUInt64(); + if (ProcessSP process_sp = m_thread.GetProcess()) { + if (ABISP abi = process_sp->GetABI()) + new_caller_pc_value = abi->FixCodeAddress(new_caller_pc_value); + } } } } - } - if (new_caller_pc_value == LLDB_INVALID_ADDRESS) { - UnwindLogMsg("failed to get a pc value for the caller frame with the " - "fallback unwind plan"); - m_fallback_unwind_plan_sp.reset(); - m_full_unwind_plan_sp = original_full_unwind_plan_sp; - m_cfa = old_cfa; - m_afa = old_afa; - return false; - } + if (new_caller_pc_value == LLDB_INVALID_ADDRESS) { + UnwindLogMsg("failed to get a pc value for the caller frame with the " + "fallback unwind plan"); + m_fallback_unwind_plan_sp.reset(); + m_full_unwind_plan_sp = original_full_unwind_plan_sp; + m_cfa = old_cfa; + m_afa = old_afa; + return false; + } + + if (old_caller_pc_value == new_caller_pc_value && m_cfa == old_cfa && + m_afa == old_afa) { + UnwindLogMsg("fallback unwind plan got the same values for this frame " + "CFA and caller frame pc, not using"); + m_fallback_unwind_plan_sp.reset(); + m_full_unwind_plan_sp = original_full_unwind_plan_sp; + return false; + } + + UnwindLogMsg( + "trying to unwind from this function with the UnwindPlan '%s' " + "because UnwindPlan '%s' failed.", + m_fallback_unwind_plan_sp->GetSourceName().GetCString(), + original_full_unwind_plan_sp->GetSourceName().GetCString()); - if (old_caller_pc_value == new_caller_pc_value && - m_cfa == old_cfa && - m_afa == old_afa) { - UnwindLogMsg("fallback unwind plan got the same values for this frame " - "CFA and caller frame pc, not using"); + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); + } else { + UnwindLogMsg("failed to get cfa with fallback unwindplan"); m_fallback_unwind_plan_sp.reset(); m_full_unwind_plan_sp = original_full_unwind_plan_sp; return false; } - - UnwindLogMsg("trying to unwind from this function with the UnwindPlan '%s' " - "because UnwindPlan '%s' failed.", - m_fallback_unwind_plan_sp->GetSourceName().GetCString(), - original_full_unwind_plan_sp->GetSourceName().GetCString()); - - // We've copied the fallback unwind plan into the full - now clear the - // fallback. - m_fallback_unwind_plan_sp.reset(); - PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); } return true; @@ -2025,16 +2056,21 @@ } case UnwindPlan::Row::FAValue::isRaSearch: { Process &process = *m_thread.GetProcess(); - lldb::addr_t return_address_hint = GetReturnAddressHint(fa.GetOffset()); - if (return_address_hint == LLDB_INVALID_ADDRESS) + addr_t sp; + if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp)) return false; + addr_t return_address_hint = sp + fa.GetRaSearchOffset(); + if (fa.IsFirstRaSearch()) + return_address_hint += GetParameterStackSize(); const unsigned max_iterations = 256; for (unsigned i = 0; i < max_iterations; ++i) { Status st; - lldb::addr_t candidate_addr = + addr_t candidate_addr = return_address_hint + i * process.GetAddressByteSize(); - lldb::addr_t candidate = - process.ReadPointerFromMemory(candidate_addr, st); + if (!process.GetABI()->CallFrameAddressIsValid(candidate_addr + + fa.GetOffset())) + continue; + addr_t candidate = process.ReadPointerFromMemory(candidate_addr, st); if (st.Fail()) { UnwindLogMsg("Cannot read memory at 0x%" PRIx64 ": %s", candidate_addr, st.AsCString()); @@ -2044,7 +2080,7 @@ uint32_t permissions; if (process.GetLoadAddressPermissions(candidate, permissions) && permissions & lldb::ePermissionsExecutable) { - address = candidate_addr; + address = candidate_addr + fa.GetOffset(); UnwindLogMsg("Heuristically found CFA: 0x%" PRIx64, address); return true; } @@ -2058,29 +2094,21 @@ return false; } -lldb::addr_t RegisterContextUnwind::GetReturnAddressHint(int32_t plan_offset) { - addr_t hint; - if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, hint)) - return LLDB_INVALID_ADDRESS; - if (!m_sym_ctx.module_sp || !m_sym_ctx.symbol) - return LLDB_INVALID_ADDRESS; - - hint += plan_offset; - +addr_t RegisterContextUnwind::GetParameterStackSize() { if (auto next = GetNextFrame()) { if (!next->m_sym_ctx.module_sp || !next->m_sym_ctx.symbol) - return LLDB_INVALID_ADDRESS; + return 0; if (auto expected_size = next->m_sym_ctx.module_sp->GetSymbolFile()->GetParameterStackSize( *next->m_sym_ctx.symbol)) - hint += *expected_size; + return *expected_size; else { UnwindLogMsgVerbose("Could not retrieve parameter size: %s", llvm::toString(expected_size.takeError()).c_str()); - return LLDB_INVALID_ADDRESS; + return 0; } } - return hint; + return 0; } // Retrieve a general purpose register value for THIS frame, as saved by the diff --git a/lldb/test/Shell/SymbolFile/Breakpad/unwind-via-raSearch.test b/lldb/test/Shell/SymbolFile/Breakpad/unwind-via-raSearch.test --- a/lldb/test/Shell/SymbolFile/Breakpad/unwind-via-raSearch.test +++ b/lldb/test/Shell/SymbolFile/Breakpad/unwind-via-raSearch.test @@ -16,13 +16,13 @@ # CHECK: This UnwindPlan is valid at all instruction locations: no. # CHECK: This UnwindPlan is for a trap handler function: no. # CHECK: Address range of this UnwindPlan: [unwind-via-stack-win.exe..module_image + 4112-0x0000107d) -# CHECK: row[0]: 0: CFA=RaSearch@SP+0 => esp=DW_OP_pick 0x0, DW_OP_consts +4, DW_OP_plus eip=DW_OP_pick 0x0, DW_OP_deref +# CHECK: row[0]: 0: CFA=RaSearch@SP+0, offset=+0 => esp=DW_OP_pick 0x0, DW_OP_consts +4, DW_OP_plus eip=DW_OP_pick 0x0, DW_OP_deref image show-unwind -n nonzero_frame_size # CHECK-LABEL: image show-unwind -n nonzero_frame_size # CHECK: UNWIND PLANS for unwind-via-stack-win.exe`nonzero_frame_size # CHECK: Symbol file UnwindPlan: -# CHECK: row[0]: 0: CFA=RaSearch@SP+12 => esp=DW_OP_pick 0x0, DW_OP_consts +4, DW_OP_plus eip=DW_OP_pick 0x0, DW_OP_deref +# CHECK: row[0]: 0: CFA=RaSearch@SP+12, offset=+0 => esp=DW_OP_pick 0x0, DW_OP_consts +4, DW_OP_plus eip=DW_OP_pick 0x0, DW_OP_deref # Then, some invalid rules. image show-unwind -n complex_rasearch