Index: include/lldb/Symbol/SymbolFile.h =================================================================== --- include/lldb/Symbol/SymbolFile.h +++ include/lldb/Symbol/SymbolFile.h @@ -18,8 +18,8 @@ #include "lldb/Symbol/Type.h" #include "lldb/Symbol/TypeList.h" #include "lldb/lldb-private.h" - #include "llvm/ADT/DenseSet.h" +#include "llvm/Support/Errc.h" #include @@ -237,6 +237,16 @@ return nullptr; } + virtual llvm::Expected GetParameterSize(Symbol &symbol) { + return llvm::createStringError(make_error_code(llvm::errc::not_supported), + "Operation not supported."); + } + + 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: 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" @@ -1854,12 +1855,72 @@ 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()->GetParameterSize( + *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 @@ -12,6 +12,7 @@ #include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h" #include "lldb/Core/FileSpecList.h" #include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/PostfixExpression.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/UnwindPlan.h" @@ -134,6 +135,9 @@ void AddSymbols(Symtab &symtab) override; + llvm::Expected GetParameterSize(Symbol &symbol) override; + llvm::Expected GetOwnFrameSize(Symbol &symbol) override; + lldb::UnwindPlanSP GetUnwindPlan(const Address &address, const RegisterInfoResolver &resolver) override; @@ -204,9 +208,14 @@ void ParseCUData(); void ParseLineTableAndSupportFiles(CompileUnit &cu, CompUnitData &data); void ParseUnwindData(); - bool ParseUnwindRow(llvm::StringRef unwind_rules, - const RegisterInfoResolver &resolver, - UnwindPlan::Row &row); + llvm::ArrayRef SaveAsDWARF(postfix::Node &node); + lldb::UnwindPlanSP ParseCFIUnwindPlan(const Bookmark &bookmark, + const RegisterInfoResolver &resolver); + bool ParseCFIUnwindRow(llvm::StringRef unwind_rules, + const RegisterInfoResolver &resolver, + UnwindPlan::Row &row); + lldb::UnwindPlanSP ParseWinUnwindPlan(const Bookmark &bookmark, + const RegisterInfoResolver &resolver); using CompUnitMap = RangeDataVector; @@ -214,7 +223,8 @@ llvm::Optional m_cu_data; using UnwindMap = RangeDataVector; - llvm::Optional m_unwind_data; + llvm::Optional m_unwind_cfi; + llvm::Optional m_unwind_win; llvm::BumpPtrAllocator m_allocator; }; Index: source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp =================================================================== --- source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp +++ source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp @@ -15,7 +15,6 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/ObjectFile.h" -#include "lldb/Symbol/PostfixExpression.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeMap.h" #include "lldb/Utility/Log.h" @@ -375,6 +374,31 @@ symtab.CalculateSymbolSizes(); } +llvm::Expected SymbolFileBreakpad::GetParameterSize(Symbol &symbol) { + if (symbol.GetFlags() != 0) + return symbol.GetFlags(); + if (auto *entry = m_unwind_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) { + if (auto *entry = m_unwind_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 @@ -421,7 +445,17 @@ return ResolveRegister(resolver, name); } -bool SymbolFileBreakpad::ParseUnwindRow(llvm::StringRef unwind_rules, +llvm::ArrayRef SymbolFileBreakpad::SaveAsDWARF(postfix::Node &node) { + ArchSpec arch = m_objfile_sp->GetArchitecture(); + StreamString dwarf(Stream::eBinary, arch.GetAddressByteSize(), + arch.GetByteOrder()); + ToDWARF(node, dwarf); + uint8_t *saved = m_allocator.Allocate(dwarf.GetSize()); + std::memcpy(saved, dwarf.GetData(), dwarf.GetSize()); + return {saved, dwarf.GetSize()}; +} + +bool SymbolFileBreakpad::ParseCFIUnwindRow(llvm::StringRef unwind_rules, const RegisterInfoResolver &resolver, UnwindPlan::Row &row) { Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); @@ -454,18 +488,12 @@ return false; } - ArchSpec arch = m_objfile_sp->GetArchitecture(); - StreamString dwarf(Stream::eBinary, arch.GetAddressByteSize(), - arch.GetByteOrder()); - ToDWARF(*rhs, dwarf); - uint8_t *saved = m_allocator.Allocate(dwarf.GetSize()); - std::memcpy(saved, dwarf.GetData(), dwarf.GetSize()); - + llvm::ArrayRef saved = SaveAsDWARF(*rhs); if (lhs == ".cfa") { - row.GetCFAValue().SetIsDWARFExpression(saved, dwarf.GetSize()); + row.GetCFAValue().SetIsDWARFExpression(saved.data(), saved.size()); } else if (const RegisterInfo *info = ResolveRegisterOrRA(resolver, lhs)) { UnwindPlan::Row::RegisterLocation loc; - loc.SetIsDWARFExpression(saved, dwarf.GetSize()); + loc.SetIsDWARFExpression(saved.data(), saved.size()); row.SetRegisterInfo(info->kinds[eRegisterKindLLDB], loc); } else LLDB_LOG(log, "Invalid register `{0}` in unwind rule.", lhs); @@ -481,16 +509,23 @@ SymbolFileBreakpad::GetUnwindPlan(const Address &address, const RegisterInfoResolver &resolver) { ParseUnwindData(); - const UnwindMap::Entry *entry = - m_unwind_data->FindEntryThatContains(address.GetFileAddress()); - if (!entry) - return nullptr; + if (auto *entry = + m_unwind_cfi->FindEntryThatContains(address.GetFileAddress())) + return ParseCFIUnwindPlan(entry->data, resolver); + if (auto *entry = + m_unwind_win->FindEntryThatContains(address.GetFileAddress())) + return ParseWinUnwindPlan(entry->data, resolver); + return nullptr; +} +UnwindPlanSP +SymbolFileBreakpad::ParseCFIUnwindPlan(const Bookmark &bookmark, + const RegisterInfoResolver &resolver) { addr_t base = GetBaseFileAddress(); if (base == LLDB_INVALID_ADDRESS) return nullptr; - LineIterator It(*m_objfile_sp, Record::StackCFI, entry->data), + LineIterator It(*m_objfile_sp, Record::StackCFI, bookmark), End(*m_objfile_sp); llvm::Optional init_record = StackCFIRecord::parse(*It); assert(init_record.hasValue()); @@ -507,7 +542,7 @@ auto row_sp = std::make_shared(); row_sp->SetOffset(0); - if (!ParseUnwindRow(init_record->UnwindRules, resolver, *row_sp)) + if (!ParseCFIUnwindRow(init_record->UnwindRules, resolver, *row_sp)) return nullptr; plan_sp->AppendRow(row_sp); for (++It; It != End; ++It) { @@ -519,13 +554,96 @@ row_sp = std::make_shared(*row_sp); row_sp->SetOffset(record->Address - init_record->Address); - if (!ParseUnwindRow(record->UnwindRules, resolver, *row_sp)) + if (!ParseCFIUnwindRow(record->UnwindRules, resolver, *row_sp)) return nullptr; plan_sp->AppendRow(row_sp); } return plan_sp; } +UnwindPlanSP +SymbolFileBreakpad::ParseWinUnwindPlan(const Bookmark &bookmark, + const RegisterInfoResolver &resolver) { + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); + addr_t base = GetBaseFileAddress(); + if (base == LLDB_INVALID_ADDRESS) + return nullptr; + + LineIterator It(*m_objfile_sp, Record::StackWin, bookmark); + llvm::Optional record = StackWinRecord::parse(*It); + assert(record.hasValue()); + + auto plan_sp = std::make_shared(lldb::eRegisterKindLLDB); + plan_sp->SetSourceName("breakpad STACK WIN"); + plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); + plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo); + plan_sp->SetSourcedFromCompiler(eLazyBoolYes); + plan_sp->SetPlanValidAddressRange( + AddressRange(base + record->RVA, record->CodeSize, + m_objfile_sp->GetModule()->GetSectionList())); + + auto row_sp = std::make_shared(); + row_sp->SetOffset(0); + + llvm::BumpPtrAllocator node_alloc; + std::vector> program = + postfix::ParseFPOProgram(record->ProgramString, node_alloc); + + if (program.empty()) { + LLDB_LOG(log, "Invalid unwind rule: {0}.", record->ProgramString); + return nullptr; + } + auto it = program.begin(); + const auto &symbol_resolver = + [&](postfix::SymbolNode &symbol) -> postfix::Node * { + llvm::StringRef name = symbol.GetName(); + for (const auto &rule : llvm::make_range(program.begin(), it)) { + if (rule.first == name) + return rule.second; + } + if (const RegisterInfo *info = ResolveRegister(resolver, name)) + return postfix::MakeNode( + node_alloc, info->kinds[eRegisterKindLLDB]); + return nullptr; + }; + if (auto *symbol = llvm::dyn_cast(it->second)) { + if (symbol->GetName() != ".raSearch") { + LLDB_LOG(log, "Invalid CFA value in `{0}`.", record->ProgramString); + return nullptr; + } + 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()); + } + it->second = postfix::MakeNode(node_alloc); + for (++it; it != program.end(); ++it) { + const RegisterInfo *info = ResolveRegister(resolver, it->first); + // It is not an error if the resolution fails because the program may + // contain temporary variables. + if (!info) + continue; + 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); + UnwindPlan::Row::RegisterLocation loc; + loc.SetIsDWARFExpression(saved.data(), saved.size()); + row_sp->SetRegisterInfo(info->kinds[eRegisterKindLLDB], loc); + } + + plan_sp->AppendRow(row_sp); + return plan_sp; +} + addr_t SymbolFileBreakpad::GetBaseFileAddress() { return m_objfile_sp->GetModule() ->GetObjectFile() @@ -631,10 +749,11 @@ } void SymbolFileBreakpad::ParseUnwindData() { - if (m_unwind_data) + if (m_unwind_cfi) { + assert(m_unwind_win); return; + } - m_unwind_data.emplace(); Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); addr_t base = GetBaseFileAddress(); if (base == LLDB_INVALID_ADDRESS) { @@ -642,14 +761,26 @@ "of object file."); } + m_unwind_cfi.emplace(); for (LineIterator It(*m_objfile_sp, Record::StackCFI), End(*m_objfile_sp); It != End; ++It) { if (auto record = StackCFIRecord::parse(*It)) { if (record->Size) - m_unwind_data->Append(UnwindMap::Entry( - base + record->Address, *record->Size, It.GetBookmark())); + m_unwind_cfi->Append(UnwindMap::Entry(base + record->Address, + *record->Size, It.GetBookmark())); + } else + LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It); + } + m_unwind_cfi->Sort(); + + m_unwind_win.emplace(); + for (LineIterator It(*m_objfile_sp, Record::StackWin), End(*m_objfile_sp); + It != End; ++It) { + if (auto record = StackWinRecord::parse(*It)) { + m_unwind_win->Append(UnwindMap::Entry( + base + record->RVA, record->CodeSize, It.GetBookmark())); } else LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It); } - m_unwind_data->Sort(); + m_unwind_win->Sort(); } 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: