Index: source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h =================================================================== --- source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h +++ source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h @@ -11,6 +11,7 @@ #include "lldb/Utility/UUID.h" #include "lldb/lldb-types.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/FormatProviders.h" @@ -20,7 +21,7 @@ class Record { public: - enum Kind { Module, Info, File, Func, Line, Public, StackCFIInit, StackCFI }; + enum Kind { Module, Info, File, Func, Line, Public, StackCFI }; /// Attempt to guess the kind of the record present in the argument without /// doing a full parse. The returned kind will always be correct for valid @@ -141,6 +142,22 @@ bool operator==(const PublicRecord &L, const PublicRecord &R); llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PublicRecord &R); +class StackCFIRecord : public Record { +public: + static llvm::Optional parse(llvm::StringRef Line); + StackCFIRecord(lldb::addr_t Address, llvm::Optional Size, + llvm::DenseMap UnwindRules) + : Record(StackCFI), Address(Address), Size(Size), + UnwindRules(std::move(UnwindRules)) {} + + lldb::addr_t Address; + llvm::Optional Size; + llvm::DenseMap UnwindRules; +}; + +bool operator==(const StackCFIRecord &L, const StackCFIRecord &R); +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const StackCFIRecord &R); + } // namespace breakpad } // namespace lldb_private Index: source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp =================================================================== --- source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp +++ source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp @@ -143,8 +143,7 @@ Tok = consume(Line); switch (Tok) { case Token::CFI: - Tok = consume(Line); - return Tok == Token::Init ? Record::StackCFIInit : Record::StackCFI; + return Record::StackCFI; default: return llvm::None; } @@ -359,6 +358,91 @@ R.Name); } +llvm::Optional StackCFIRecord::parse(llvm::StringRef Line) { + // STACK CFI INIT address size reg1: expr1 reg2: expr2 ... + // or + // STACK CFI address reg1: expr1 reg2: expr2 ... + // No token in exprN ends with a colon. + + if (consume(Line) != Token::Stack) + return llvm::None; + if (consume(Line) != Token::CFI) + return llvm::None; + + llvm::StringRef Str; + std::tie(Str, Line) = getToken(Line); + + bool IsInitRecord = stringTo(Str) == Token::Init; + if (IsInitRecord) + std::tie(Str, Line) = getToken(Line); + + lldb::addr_t Address; + if (!to_integer(Str, Address, 16)) + return llvm::None; + + llvm::Optional Size; + if (IsInitRecord) { + Size.emplace(); + std::tie(Str, Line) = getToken(Line); + if (!to_integer(Str, *Size, 16)) + return llvm::None; + } + + llvm::DenseMap UnwindRules; + llvm::StringRef LHS, RHS; + while (std::tie(Str, Line) = getToken(Line), !Str.empty()) { + if (Str.back() == ':') { // regN + // Flush the previous expression, if there is one. + if (!LHS.empty()) { + if (RHS.empty()) + return llvm::None; // regN without an exprN + UnwindRules[LHS] = RHS; + } + + LHS = Str.drop_back(); + RHS = llvm::StringRef(); + continue; + } + + if (LHS.empty()) + return llvm::None; // exprN without a preceding regN. + + // Append token to RHS. + if (RHS.empty()) + RHS = Str; + else + RHS = llvm::StringRef(RHS.begin(), Str.end() - RHS.begin()); + } + + // Append last rule, if it is valid. + if (RHS.empty()) + return llvm::None; + + UnwindRules[LHS] = RHS; + return StackCFIRecord(Address, Size, std::move(UnwindRules)); +} + +bool breakpad::operator==(const StackCFIRecord &L, const StackCFIRecord &R) { + return L.Address == R.Address && L.Size == R.Size && + L.UnwindRules == R.UnwindRules; +} + +llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS, + const StackCFIRecord &R) { + OS << "STACK CFI "; + if (R.Size) + OS << "INIT "; + OS << llvm::formatv("{0:x-} ", R.Address); + if (R.Size) + OS << llvm::formatv("{0:x-} ", *R.Size); + llvm::StringRef Sep; + for (const auto &KV : R.UnwindRules) { + OS << Sep << KV.first << ": " << KV.second; + Sep = " "; + } + return OS; +} + llvm::StringRef breakpad::toString(Record::Kind K) { switch (K) { case Record::Module: @@ -373,8 +457,6 @@ return "LINE"; case Record::Public: return "PUBLIC"; - case Record::StackCFIInit: - return "STACK CFI INIT"; case Record::StackCFI: return "STACK CFI"; } Index: source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp =================================================================== --- source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp +++ source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp @@ -153,9 +153,6 @@ // Line records logically belong to the preceding Func record, so we put // them in the same section. next_section = Record::Func; - } else if (next_section == Record::StackCFI) { - // Same goes for StackCFI and StackCFIInit - next_section = Record::StackCFIInit; } if (next_section == current_section) continue; Index: unittests/ObjectFile/Breakpad/BreakpadRecordsTest.cpp =================================================================== --- unittests/ObjectFile/Breakpad/BreakpadRecordsTest.cpp +++ unittests/ObjectFile/Breakpad/BreakpadRecordsTest.cpp @@ -19,7 +19,6 @@ EXPECT_EQ(Record::File, Record::classify("FILE")); EXPECT_EQ(Record::Func, Record::classify("FUNC")); EXPECT_EQ(Record::Public, Record::classify("PUBLIC")); - EXPECT_EQ(Record::StackCFIInit, Record::classify("STACK CFI INIT")); EXPECT_EQ(Record::StackCFI, Record::classify("STACK CFI")); // Any obviously incorrect lines will be classified as such. @@ -97,3 +96,41 @@ EXPECT_EQ(llvm::None, PublicRecord::parse("PUBLIC m")); EXPECT_EQ(llvm::None, PublicRecord::parse("PUBLIC")); } + +TEST(StackCFIRecord, parse) { + EXPECT_EQ( + StackCFIRecord(0x47, 0x8, {{".cfa", "$esp 4 +"}, {"$eip", ".cfa 4 - ^"}}), + StackCFIRecord::parse( + "STACK CFI INIT 47 8 .cfa: $esp 4 + $eip: .cfa 4 - ^")); + EXPECT_EQ(StackCFIRecord(0x47, 0x8, {{".cfa", "$esp 4 +"}}), + StackCFIRecord::parse("STACK CFI INIT 47 8 .cfa: $esp 4 +")); + + EXPECT_EQ( + StackCFIRecord(0x47, llvm::None, + {{".cfa", "$esp 4 +"}, {"$eip", ".cfa 4 - ^"}}), + StackCFIRecord::parse("STACK CFI 47 .cfa: $esp 4 + $eip: .cfa 4 - ^")); + + // The validity of the register value expressions is not checked + EXPECT_EQ(StackCFIRecord(0x47, 0x8, {{".cfa", "^ ^ ^"}}), + StackCFIRecord::parse("STACK CFI INIT 47 8 .cfa: ^ ^ ^")); + EXPECT_EQ(StackCFIRecord(0x47, 0x8, {{".cfa", "STACK CFI"}}), + StackCFIRecord::parse("STACK CFI INIT 47 8 .cfa: STACK CFI")); + + EXPECT_EQ(llvm::None, + StackCFIRecord::parse("STACK CFI INIT 47 .cfa: $esp 4 +")); + EXPECT_EQ(llvm::None, StackCFIRecord::parse("STACK CFI 47 8 .cfa: $esp 4 +")); + EXPECT_EQ(llvm::None, + StackCFIRecord::parse("STACK CFI INIT 47 8 .cfa: $esp 4 + $eip:")); + EXPECT_EQ(llvm::None, + StackCFIRecord::parse("STACK CFI INIT 47 8 .cfa: $eip: .cfa")); + EXPECT_EQ(llvm::None, + StackCFIRecord::parse("STACK CFI INIT 47 8 $esp .cfa: $esp")); + EXPECT_EQ(llvm::None, StackCFIRecord::parse("STACK CFI INIT 47 8 .cfa:")); + EXPECT_EQ(llvm::None, StackCFIRecord::parse("STACK CFI INIT 47 8")); + EXPECT_EQ(llvm::None, StackCFIRecord::parse("STACK CFI INIT 47")); + EXPECT_EQ(llvm::None, StackCFIRecord::parse("STACK CFI INIT")); + EXPECT_EQ(llvm::None, StackCFIRecord::parse("STACK CFI")); + EXPECT_EQ(llvm::None, StackCFIRecord::parse("STACK")); + EXPECT_EQ(llvm::None, StackCFIRecord::parse("FILE 47 foo")); + EXPECT_EQ(llvm::None, StackCFIRecord::parse("42 47")); +}