Index: include/lldb/Symbol/SymbolFile.h =================================================================== --- include/lldb/Symbol/SymbolFile.h +++ include/lldb/Symbol/SymbolFile.h @@ -19,8 +19,8 @@ #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/lldb-private.h" - #include "llvm/ADT/DenseSet.h" +#include "llvm/Support/Errc.h" #include @@ -242,6 +242,21 @@ return nullptr; } + /// Return the number of stack bytes taken up by the parameters to this + /// function. + virtual llvm::Expected GetParameterStackSize(Symbol &symbol) { + return llvm::createStringError(make_error_code(llvm::errc::not_supported), + "Operation not supported."); + } + + /// Return the frame size of this function. This includes things like local + /// variables and spilled registers, but it should not include paramenters, as + /// they are considered to be a part of the callers frame. + virtual llvm::Expected GetOwnFrameSize(Symbol &symbol) { + return llvm::createStringError(make_error_code(llvm::errc::not_supported), + "Operation not supported."); + } + virtual void Dump(Stream &s); protected: Index: include/lldb/Symbol/UnwindPlan.h =================================================================== --- include/lldb/Symbol/UnwindPlan.h +++ include/lldb/Symbol/UnwindPlan.h @@ -201,7 +201,8 @@ unspecified, // not specified isRegisterPlusOffset, // FA = register + offset isRegisterDereferenced, // FA = [reg] - isDWARFExpression // FA = eval(dwarf_expr) + isDWARFExpression, // FA = eval(dwarf_expr) + isFoundHeuristically, // FA = ??? }; FAValue() : m_type(unspecified), m_value() {} @@ -214,6 +215,8 @@ bool IsUnspecified() const { return m_type == unspecified; } + void SetFoundHeuristically() { m_type = isFoundHeuristically; } + bool IsRegisterPlusOffset() const { return m_type == isRegisterPlusOffset; } Index: lit/SymbolFile/Breakpad/Inputs/unwind-via-raSearch.syms =================================================================== --- /dev/null +++ lit/SymbolFile/Breakpad/Inputs/unwind-via-raSearch.syms @@ -0,0 +1,13 @@ +MODULE windows x86 897DD83EA8C8411897F3A925EE4BF7411 unwind-via-stack-win.pdb +INFO CODE_ID 5D499B5C5000 unwind-via-stack-win.exe +PUBLIC 0 0 dummy +PUBLIC 10 0 call_many +PUBLIC 80 0 main +PUBLIC 90 0 many_pointer_args +PUBLIC 100 0 complex_rasearch +PUBLIC 110 0 esp_rasearch +STACK WIN 4 10 6d 0 0 0 0 0 0 1 $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = +STACK WIN 4 80 8 0 0 0 0 0 0 1 $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = +STACK WIN 4 90 5 0 0 50 0 0 0 1 $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = +STACK WIN 4 100 4 0 0 0 0 0 0 1 $T0 .raSearch 80 + = $eip $T0 ^ = $esp $T0 4 + = +STACK WIN 4 110 4 0 0 0 0 0 0 1 $T0 .raSearch = $eip $T0 ^ = $esp .raSearch 4 + = Index: lit/SymbolFile/Breakpad/unwind-via-raSearch.test =================================================================== --- /dev/null +++ lit/SymbolFile/Breakpad/unwind-via-raSearch.test @@ -0,0 +1,37 @@ +# RUN: yaml2obj %S/Inputs/unwind-via-stack-win.yaml > %t +# RUN: %lldb -c %t \ +# RUN: -o "target symbols add %S/Inputs/unwind-via-raSearch.syms" \ +# RUN: -s %s -b | FileCheck %s + +# First check that unwind plan generation works correctly. +# This function has a "typical" unwind rule. +image show-unwind -n call_many +# CHECK-LABEL: image show-unwind -n call_many +# CHECK: UNWIND PLANS for unwind-via-stack-win.exe`call_many +# CHECK: Symbol file UnwindPlan: +# CHECK: This UnwindPlan originally sourced from breakpad STACK WIN +# CHECK: This UnwindPlan is sourced from the compiler: yes. +# CHECK: This UnwindPlan is valid at all instruction locations: no. +# CHECK: Address range of this UnwindPlan: [unwind-via-stack-win.exe..module_image + 16-0x0000007d) +# CHECK: row[0]: 0: CFA=found heuristically => esp=DW_OP_pick 0x00, DW_OP_consts +4, DW_OP_plus eip=DW_OP_pick 0x00, DW_OP_deref + +# Then, some invalid rules. +image show-unwind -n complex_rasearch +# CHECK-LABEL: image show-unwind -n complex_rasearch +# CHECK: UNWIND PLANS for unwind-via-stack-win.exe`complex_rasearch +# CHECK-NOT: Symbol file + +image show-unwind -n esp_rasearch +# CHECK-LABEL: image show-unwind -n esp_rasearch +# CHECK: UNWIND PLANS for unwind-via-stack-win.exe`esp_rasearch +# CHECK-NOT: Symbol file + +# And finally, check that backtracing works as a whole by unwinding a simple +# stack. +thread backtrace +# CHECK-LABEL: thread backtrace +# CHECK: frame #0: 0x000b1092 unwind-via-stack-win.exe`many_pointer_args +# CHECK: frame #1: 0x000b1079 unwind-via-stack-win.exe`call_many + 105 +# CHECK: frame #2: 0x000b1085 unwind-via-stack-win.exe`main + 5 +# CHECK: frame #3: 0x77278494 kernel32.dll +# CHECK-NOT: frame Index: source/Plugins/Process/Utility/RegisterContextLLDB.h =================================================================== --- source/Plugins/Process/Utility/RegisterContextLLDB.h +++ source/Plugins/Process/Utility/RegisterContextLLDB.h @@ -201,6 +201,8 @@ bool IsUnwindPlanValidForCurrentPC(lldb::UnwindPlanSP unwind_plan_sp, int &valid_pc_offset); + lldb::addr_t GetReturnAddressHint(); + lldb_private::Thread &m_thread; /// Index: source/Plugins/Process/Utility/RegisterContextLLDB.cpp =================================================================== --- source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -18,6 +18,7 @@ #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolFile.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/ExecutionContext.h" @@ -1852,12 +1853,73 @@ error.AsCString()); break; } + case UnwindPlan::Row::FAValue::isFoundHeuristically: { + Process &process = *m_thread.GetProcess(); + lldb::addr_t return_address_hint = GetReturnAddressHint(); + if (return_address_hint == LLDB_INVALID_ADDRESS) + return false; + const unsigned max_iterations = 100; + for (unsigned i = 0; i < max_iterations; ++i) { + Status st; + lldb::addr_t candidate_addr = + return_address_hint + i * process.GetAddressByteSize(); + lldb::addr_t candidate = + process.ReadPointerFromMemory(candidate_addr, st); + if (st.Fail()) { + UnwindLogMsg("Cannot read memory at 0x%" PRIx64 ": %s", candidate_addr, + st.AsCString()); + return false; + } + Address addr; + if (addr.SetLoadAddress(candidate, &process.GetTarget()) && + addr.GetSection()->GetPermissions() & lldb::ePermissionsExecutable) { + address = candidate_addr; + UnwindLogMsg("Heuristically found CFA: 0x%" PRIx64, address); + return true; + } + } + UnwindLogMsg("No suitable CFA found"); + break; + } default: return false; } return false; } +lldb::addr_t RegisterContextLLDB::GetReturnAddressHint() { + 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; + + if (auto expected_size = + m_sym_ctx.module_sp->GetSymbolFile()->GetOwnFrameSize( + *m_sym_ctx.symbol)) + hint += *expected_size; + else { + UnwindLogMsgVerbose("Could not retrieve own frame size: %s", + llvm::toString(expected_size.takeError()).c_str()); + return LLDB_INVALID_ADDRESS; + } + + if (auto next = GetNextFrame()) { + if (!next->m_sym_ctx.module_sp || !next->m_sym_ctx.symbol) + return LLDB_INVALID_ADDRESS; + if (auto expected_size = + next->m_sym_ctx.module_sp->GetSymbolFile()->GetParameterStackSize( + *next->m_sym_ctx.symbol)) + hint += *expected_size; + else { + UnwindLogMsgVerbose("Could not retrieve parameter size: %s", + llvm::toString(expected_size.takeError()).c_str()); + return LLDB_INVALID_ADDRESS; + } + } + return hint; +} + // Retrieve a general purpose register value for THIS frame, as saved by the // NEXT frame, i.e. the frame that // this frame called. e.g. Index: source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h =================================================================== --- source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h +++ source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h @@ -135,6 +135,9 @@ void AddSymbols(Symtab &symtab) override; + llvm::Expected GetParameterStackSize(Symbol &symbol) override; + llvm::Expected GetOwnFrameSize(Symbol &symbol) override; + lldb::UnwindPlanSP GetUnwindPlan(const Address &address, const RegisterInfoResolver &resolver) override; Index: source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp =================================================================== --- source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp +++ source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp @@ -374,6 +374,34 @@ symtab.CalculateSymbolSizes(); } +llvm::Expected +SymbolFileBreakpad::GetParameterStackSize(Symbol &symbol) { + ParseUnwindData(); + if (auto *entry = m_unwind_data->win.FindEntryThatContains( + symbol.GetAddress().GetFileAddress())) { + auto record = StackWinRecord::parse( + *LineIterator(*m_objfile_sp, Record::StackWin, entry->data)); + assert(record.hasValue()); + return record->ParameterSize; + } + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Parameter size unknown."); +} + +llvm::Expected SymbolFileBreakpad::GetOwnFrameSize(Symbol &symbol) { + ParseUnwindData(); + if (auto *entry = m_unwind_data->win.FindEntryThatContains( + symbol.GetAddress().GetFileAddress())) { + auto record = StackWinRecord::parse( + *LineIterator(*m_objfile_sp, Record::StackWin, entry->data)); + assert(record.hasValue()); + return record->LocalSize + record->SavedRegisterSize; + } + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Own frame size unknown."); +} + + static llvm::Optional> GetRule(llvm::StringRef &unwind_rules) { // Unwind rules are of the form @@ -585,12 +613,18 @@ // We assume the first value will be the CFA. It is usually called T0, but // clang will use T1, if it needs to realign the stack. - if (!postfix::ResolveSymbols(it->second, symbol_resolver)) { - LLDB_LOG(log, "Resolving symbols in `{0}` failed.", record->ProgramString); - return nullptr; + auto *symbol = llvm::dyn_cast(it->second); + if (symbol && symbol->GetName() == ".raSearch") { + row_sp->GetCFAValue().SetFoundHeuristically(); + } else { + if (!postfix::ResolveSymbols(it->second, symbol_resolver)) { + LLDB_LOG(log, "Resolving symbols in `{0}` failed.", + record->ProgramString); + return nullptr; + } + llvm::ArrayRef saved = SaveAsDWARF(*it->second); + row_sp->GetCFAValue().SetIsDWARFExpression(saved.data(), saved.size()); } - llvm::ArrayRef saved = SaveAsDWARF(*it->second); - row_sp->GetCFAValue().SetIsDWARFExpression(saved.data(), saved.size()); // Replace the node value with InitialValueNode, so that subsequent // expressions refer to the CFA value instead of recomputing the whole Index: source/Symbol/UnwindPlan.cpp =================================================================== --- source/Symbol/UnwindPlan.cpp +++ source/Symbol/UnwindPlan.cpp @@ -170,6 +170,7 @@ if (m_type == rhs.m_type) { switch (m_type) { case unspecified: + case isFoundHeuristically: return true; case isRegisterPlusOffset: @@ -205,9 +206,12 @@ llvm::makeArrayRef(m_value.expr.opcodes, m_value.expr.length), thread); break; - default: + case unspecified: s.PutCString("unspecified"); break; + case isFoundHeuristically: + s.PutCString("found heuristically"); + break; } }