Skip to content

Commit

Permalink
Breakpad: Basic support for STACK WIN unwinding
Browse files Browse the repository at this point in the history
Summary:
This patch makes it possible to unwind via breakpad STACK WIN records.
It is "basic" because two important features are missing:
- support for the .raSearch keyword
- support for multiple STACK WIN records within a single function
Right now, we just reject the .raSearch records, and always pick the
first record for the whole function
SymbolFileBreakpad, and so I think it can serve as a good example of
what is needed of the symbol file and unwinding machinery to make this
work.

However, it is already useful for unwinding in some situations, and it
sets up the general framework for the parsing of these kinds of records,
which reduces the size of the followup patches implementing the two
other components.

Reviewers: amccarth, rnk, markmentovai

Subscribers: lldb-commits

Differential Revision: https://reviews.llvm.org/D67067

llvm-svn: 371017
  • Loading branch information
labath committed Sep 5, 2019
1 parent e466396 commit c3bea40
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 27 deletions.
17 changes: 17 additions & 0 deletions lldb/lit/SymbolFile/Breakpad/Inputs/unwind-via-stack-win.syms
@@ -0,0 +1,17 @@
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 bogus_rule
PUBLIC 110 0 bogus_cfa_rhs
PUBLIC 120 0 bogus_esp_rhs
PUBLIC 130 0 temporary_var
STACK WIN 4 10 6d 0 0 0 0 0 0 1 $T0 $esp 80 + = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 80 8 0 0 0 0 0 0 1 $T0 $esp = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 90 5 0 0 50 0 0 0 1 $T0 $esp = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 100 4 0 0 0 0 0 0 1 bogus
STACK WIN 4 110 4 0 0 0 0 0 0 1 $T0 $bogus = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 120 4 0 0 0 0 0 0 1 $T0 $esp = $eip $T0 ^ = $esp $bogus 4 + =
STACK WIN 4 130 4 0 0 0 0 0 0 1 $T0 $esp = $bogus $T0 = $eip $bogus ^ = $esp $T0 4 + =
35 changes: 35 additions & 0 deletions lldb/lit/SymbolFile/Breakpad/Inputs/unwind-via-stack-win.yaml
@@ -0,0 +1,35 @@
--- !minidump
Streams:
- Type: ThreadList
Threads:
- Thread Id: 0x0000290C
Priority Class: 0x00000020
Environment Block: 0x0000000000A98000
Context: 3F0001000000000000000000000000000000000000000000000000007F02000000000000FFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002B000000530000002B0000002B00000080100B0080100B000050A90080100B0080100B0000000000E4FECF0092100B0023000000440301007CFECF002B0000007F0200000000000000000000000000000000000000000000801F0000FFFF020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000144E3D6C2000000001000000EA1E00F4C000E50700000000FC0100002CE3C014D8E202010000E507880F401D839DC60100000000400000007F00000000000000880F401D0A000000900F401D0000000000000100EA1E00009808E5077F00000000000000000000009008E507990100006002E5072CABC87708346474B42300000100000044E3C014200000000000000020532777A80F401D4F346474D00F401D6F378CCC5C4CD5010000000000000000000000000000000000000000000000003AFCD72F90E3C01418CE3470B4230000B80F401DC00F401DC80F401DD00F401D
Stack:
Start of Memory Range: 0x0000000000CFFE78
Content: 0000000079100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0085100B0094842777
- Type: ModuleList
Modules:
- Base of Image: 0x00000000000B1000
Size of Image: 0x00004000
Module Name: 'unwind-via-stack-win.exe'
CodeView Record: 525344533ED87D89C8A8184197F3A925EE4BF74101000000433A5C70726F6A656374735C746573745F6170705C436F6E736F6C654170706C69636174696F6E315C44656275675C436F6E736F6C654170706C69636174696F6E312E70646200
- Base of Image: 0x0000000077260000
Size of Image: 0x000E0000
Module Name: 'C:\Windows\System32\kernel32.dll'
CodeView Record: 5253445300F90A57CF8DED8A463A90390318CD4401000000776B65726E656C33322EFFFFFFFF
- Type: MemoryList
Memory Ranges:
- Start of Memory Range: 0x0000000000CFFE78
Content: 0000000079100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0000100B0085100B0094842777
- Type: SystemInfo
Processor Arch: X86
Platform ID: Win32NT
CPU:
Vendor ID: AuthenticAMD
Version Info: 0x00800F82
Feature Info: 0x178BFBFF
- Type: MiscInfo
Content: 54050000F703000008290000C883495D0000000000000000AC0D000098080000AC0D00000200000002000000002000000D0000000000000002000000C4FFFFFF430065006E007400720061006C0020004500750072006F007000650020005300740061006E0064006100720064002000540069006D006500000000000000000000000A0000000500030000000000000000000000430065006E007400720061006C0020004500750072006F00700065002000530075006D006D00650072002000540069006D00650000000000000000000000000000000300000005000200000000000000C4FFFFFF310037003100330034002E0031002E007800380036006600720065002E007200730034005F00720065006C0065006100730065002E003100380030003400310030002D00310038003000340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064006200670063006F00720065002E0069003300380036002C00310030002E0030002E00310037003100330034002E0031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C816B92
...
54 changes: 54 additions & 0 deletions lldb/lit/SymbolFile/Breakpad/unwind-via-stack-win.test
@@ -0,0 +1,54 @@
# RUN: yaml2obj %S/Inputs/unwind-via-stack-win.yaml > %t
# RUN: %lldb -c %t \
# RUN: -o "target symbols add %S/Inputs/unwind-via-stack-win.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=DW_OP_breg7 +0, DW_OP_consts +80, DW_OP_plus => 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 bogus_rule
# CHECK-LABEL: image show-unwind -n bogus_rule
# CHECK: UNWIND PLANS for unwind-via-stack-win.exe`bogus_rule
# CHECK-NOT: Symbol file

image show-unwind -n bogus_cfa_rhs
# CHECK-LABEL: image show-unwind -n bogus_cfa_rhs
# CHECK: UNWIND PLANS for unwind-via-stack-win.exe`bogus_cfa_rhs
# CHECK-NOT: Symbol file

image show-unwind -n bogus_esp_rhs
# CHECK-LABEL: image show-unwind -n bogus_esp_rhs
# CHECK: UNWIND PLANS for unwind-via-stack-win.exe`bogus_esp_rhs
# CHECK-NOT: Symbol file

# We don't treat unknown lhs as an error, as it can be just a temporary
# variable used in other rules.
image show-unwind -n temporary_var
# CHECK-LABEL: image show-unwind -n temporary_var
# CHECK: UNWIND PLANS for unwind-via-stack-win.exe`temporary_var
# 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 + 304-0x00000134)
# CHECK: row[0]: 0: CFA=DW_OP_breg7 +0 => esp=DW_OP_pick 0x00, DW_OP_consts +4, DW_OP_plus eip=DW_OP_pick 0x00, DW_OP_deref

# 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
151 changes: 128 additions & 23 deletions lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -421,7 +420,17 @@ ResolveRegisterOrRA(const SymbolFile::RegisterInfoResolver &resolver,
return ResolveRegister(resolver, name);
}

bool SymbolFileBreakpad::ParseUnwindRow(llvm::StringRef unwind_rules,
llvm::ArrayRef<uint8_t> 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<uint8_t>(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);
Expand Down Expand Up @@ -454,18 +463,12 @@ bool SymbolFileBreakpad::ParseUnwindRow(llvm::StringRef unwind_rules,
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<uint8_t>(dwarf.GetSize());
std::memcpy(saved, dwarf.GetData(), dwarf.GetSize());

llvm::ArrayRef<uint8_t> 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);
Expand All @@ -481,20 +484,27 @@ UnwindPlanSP
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_data->cfi.FindEntryThatContains(address.GetFileAddress()))
return ParseCFIUnwindPlan(entry->data, resolver);
if (auto *entry =
m_unwind_data->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<StackCFIRecord> init_record = StackCFIRecord::parse(*It);
assert(init_record.hasValue());
assert(init_record->Size.hasValue());
assert(init_record.hasValue() && init_record->Size.hasValue() &&
"Record already parsed successfully in ParseUnwindData!");

auto plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindLLDB);
plan_sp->SetSourceName("breakpad STACK CFI");
Expand All @@ -507,7 +517,7 @@ SymbolFileBreakpad::GetUnwindPlan(const Address &address,

auto row_sp = std::make_shared<UnwindPlan::Row>();
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) {
Expand All @@ -519,13 +529,98 @@ SymbolFileBreakpad::GetUnwindPlan(const Address &address,

row_sp = std::make_shared<UnwindPlan::Row>(*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<StackWinRecord> record = StackWinRecord::parse(*It);
assert(record.hasValue() &&
"Record already parsed successfully in ParseUnwindData!");

auto plan_sp = std::make_shared<UnwindPlan>(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<UnwindPlan::Row>();
row_sp->SetOffset(0);

llvm::BumpPtrAllocator node_alloc;
std::vector<std::pair<llvm::StringRef, postfix::Node *>> 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<postfix::RegisterNode>(
node_alloc, info->kinds[eRegisterKindLLDB]);
return nullptr;
};

// 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;
}
llvm::ArrayRef<uint8_t> 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
// expression.
it->second = postfix::MakeNode<postfix::InitialValueNode>(node_alloc);


// Now process the rest of the assignments.
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<uint8_t> 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()
Expand Down Expand Up @@ -633,8 +728,8 @@ void SymbolFileBreakpad::ParseLineTableAndSupportFiles(CompileUnit &cu,
void SymbolFileBreakpad::ParseUnwindData() {
if (m_unwind_data)
return;

m_unwind_data.emplace();

Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
addr_t base = GetBaseFileAddress();
if (base == LLDB_INVALID_ADDRESS) {
Expand All @@ -646,10 +741,20 @@ void SymbolFileBreakpad::ParseUnwindData() {
It != End; ++It) {
if (auto record = StackCFIRecord::parse(*It)) {
if (record->Size)
m_unwind_data->Append(UnwindMap::Entry(
m_unwind_data->cfi.Append(UnwindMap::Entry(
base + record->Address, *record->Size, It.GetBookmark()));
} else
LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It);
}
m_unwind_data->Sort();
m_unwind_data->cfi.Sort();

for (LineIterator It(*m_objfile_sp, Record::StackWin), End(*m_objfile_sp);
It != End; ++It) {
if (auto record = StackWinRecord::parse(*It)) {
m_unwind_data->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->win.Sort();
}
18 changes: 14 additions & 4 deletions lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
Expand Up @@ -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"

Expand Down Expand Up @@ -204,17 +205,26 @@ class SymbolFileBreakpad : public SymbolFile {
void ParseCUData();
void ParseLineTableAndSupportFiles(CompileUnit &cu, CompUnitData &data);
void ParseUnwindData();
bool ParseUnwindRow(llvm::StringRef unwind_rules,
const RegisterInfoResolver &resolver,
UnwindPlan::Row &row);
llvm::ArrayRef<uint8_t> 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<lldb::addr_t, lldb::addr_t, CompUnitData>;

llvm::Optional<std::vector<FileSpec>> m_files;
llvm::Optional<CompUnitMap> m_cu_data;

using UnwindMap = RangeDataVector<lldb::addr_t, lldb::addr_t, Bookmark>;
llvm::Optional<UnwindMap> m_unwind_data;
struct UnwindData {
UnwindMap cfi;
UnwindMap win;
};
llvm::Optional<UnwindData> m_unwind_data;
llvm::BumpPtrAllocator m_allocator;
};

Expand Down

0 comments on commit c3bea40

Please sign in to comment.