aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad')
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp586
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h235
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp169
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h104
4 files changed, 1094 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp
new file mode 100644
index 000000000000..d40f87b1a7b4
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp
@@ -0,0 +1,586 @@
+//===-- BreakpadRecords.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "lldb/lldb-defines.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <optional>
+
+using namespace lldb_private;
+using namespace lldb_private::breakpad;
+
+namespace {
+enum class Token {
+ Unknown,
+ Module,
+ Info,
+ CodeID,
+ File,
+ Func,
+ Inline,
+ InlineOrigin,
+ Public,
+ Stack,
+ CFI,
+ Init,
+ Win,
+};
+}
+
+template<typename T>
+static T stringTo(llvm::StringRef Str);
+
+template <> Token stringTo<Token>(llvm::StringRef Str) {
+ return llvm::StringSwitch<Token>(Str)
+ .Case("MODULE", Token::Module)
+ .Case("INFO", Token::Info)
+ .Case("CODE_ID", Token::CodeID)
+ .Case("FILE", Token::File)
+ .Case("FUNC", Token::Func)
+ .Case("INLINE", Token::Inline)
+ .Case("INLINE_ORIGIN", Token::InlineOrigin)
+ .Case("PUBLIC", Token::Public)
+ .Case("STACK", Token::Stack)
+ .Case("CFI", Token::CFI)
+ .Case("INIT", Token::Init)
+ .Case("WIN", Token::Win)
+ .Default(Token::Unknown);
+}
+
+template <>
+llvm::Triple::OSType stringTo<llvm::Triple::OSType>(llvm::StringRef Str) {
+ using llvm::Triple;
+ return llvm::StringSwitch<Triple::OSType>(Str)
+ .Case("Linux", Triple::Linux)
+ .Case("mac", Triple::MacOSX)
+ .Case("windows", Triple::Win32)
+ .Default(Triple::UnknownOS);
+}
+
+template <>
+llvm::Triple::ArchType stringTo<llvm::Triple::ArchType>(llvm::StringRef Str) {
+ using llvm::Triple;
+ return llvm::StringSwitch<Triple::ArchType>(Str)
+ .Case("arm", Triple::arm)
+ .Cases("arm64", "arm64e", Triple::aarch64)
+ .Case("mips", Triple::mips)
+ .Case("msp430", Triple::msp430)
+ .Case("ppc", Triple::ppc)
+ .Case("ppc64", Triple::ppc64)
+ .Case("s390", Triple::systemz)
+ .Case("sparc", Triple::sparc)
+ .Case("sparcv9", Triple::sparcv9)
+ .Case("x86", Triple::x86)
+ .Cases("x86_64", "x86_64h", Triple::x86_64)
+ .Default(Triple::UnknownArch);
+}
+
+template<typename T>
+static T consume(llvm::StringRef &Str) {
+ llvm::StringRef Token;
+ std::tie(Token, Str) = getToken(Str);
+ return stringTo<T>(Token);
+}
+
+/// Return the number of hex digits needed to encode an (POD) object of a given
+/// type.
+template <typename T> static constexpr size_t hex_digits() {
+ return 2 * sizeof(T);
+}
+
+static UUID parseModuleId(llvm::Triple::OSType os, llvm::StringRef str) {
+ struct data_t {
+ using uuid_t = uint8_t[16];
+ uuid_t uuid;
+ llvm::support::ubig32_t age;
+ } data;
+ static_assert(sizeof(data) == 20);
+ // The textual module id encoding should be between 33 and 40 bytes long,
+ // depending on the size of the age field, which is of variable length.
+ // The first three chunks of the id are encoded in big endian, so we need to
+ // byte-swap those.
+ if (str.size() <= hex_digits<data_t::uuid_t>() ||
+ str.size() > hex_digits<data_t>())
+ return UUID();
+ if (!all_of(str, llvm::isHexDigit))
+ return UUID();
+
+ llvm::StringRef uuid_str = str.take_front(hex_digits<data_t::uuid_t>());
+ llvm::StringRef age_str = str.drop_front(hex_digits<data_t::uuid_t>());
+
+ llvm::copy(fromHex(uuid_str), data.uuid);
+ uint32_t age;
+ bool success = to_integer(age_str, age, 16);
+ assert(success);
+ UNUSED_IF_ASSERT_DISABLED(success);
+ data.age = age;
+
+ // On non-windows, the age field should always be zero, so we don't include to
+ // match the native uuid format of these platforms.
+ return UUID(&data, os == llvm::Triple::Win32 ? sizeof(data)
+ : sizeof(data.uuid));
+}
+
+std::optional<Record::Kind> Record::classify(llvm::StringRef Line) {
+ Token Tok = consume<Token>(Line);
+ switch (Tok) {
+ case Token::Module:
+ return Record::Module;
+ case Token::Info:
+ return Record::Info;
+ case Token::File:
+ return Record::File;
+ case Token::Func:
+ return Record::Func;
+ case Token::Public:
+ return Record::Public;
+ case Token::Stack:
+ Tok = consume<Token>(Line);
+ switch (Tok) {
+ case Token::CFI:
+ return Record::StackCFI;
+ case Token::Win:
+ return Record::StackWin;
+ default:
+ return std::nullopt;
+ }
+ case Token::Inline:
+ return Record::Inline;
+ case Token::InlineOrigin:
+ return Record::InlineOrigin;
+ case Token::Unknown:
+ // Optimistically assume that any unrecognised token means this is a line
+ // record, those don't have a special keyword and start directly with a
+ // hex number.
+ return Record::Line;
+
+ case Token::CodeID:
+ case Token::CFI:
+ case Token::Init:
+ case Token::Win:
+ // These should never appear at the start of a valid record.
+ return std::nullopt;
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+std::optional<ModuleRecord> ModuleRecord::parse(llvm::StringRef Line) {
+ // MODULE Linux x86_64 E5894855C35DCCCCCCCCCCCCCCCCCCCC0 a.out
+ if (consume<Token>(Line) != Token::Module)
+ return std::nullopt;
+
+ llvm::Triple::OSType OS = consume<llvm::Triple::OSType>(Line);
+ if (OS == llvm::Triple::UnknownOS)
+ return std::nullopt;
+
+ llvm::Triple::ArchType Arch = consume<llvm::Triple::ArchType>(Line);
+ if (Arch == llvm::Triple::UnknownArch)
+ return std::nullopt;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ UUID ID = parseModuleId(OS, Str);
+ if (!ID)
+ return std::nullopt;
+
+ return ModuleRecord(OS, Arch, std::move(ID));
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const ModuleRecord &R) {
+ return OS << "MODULE " << llvm::Triple::getOSTypeName(R.OS) << " "
+ << llvm::Triple::getArchTypeName(R.Arch) << " "
+ << R.ID.GetAsString();
+}
+
+std::optional<InfoRecord> InfoRecord::parse(llvm::StringRef Line) {
+ // INFO CODE_ID 554889E55DC3CCCCCCCCCCCCCCCCCCCC [a.exe]
+ if (consume<Token>(Line) != Token::Info)
+ return std::nullopt;
+
+ if (consume<Token>(Line) != Token::CodeID)
+ return std::nullopt;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ // If we don't have any text following the code ID (e.g. on linux), we should
+ // use this as the UUID. Otherwise, we should revert back to the module ID.
+ UUID ID;
+ if (Line.trim().empty()) {
+ if (Str.empty() || !ID.SetFromStringRef(Str))
+ return std::nullopt;
+ }
+ return InfoRecord(std::move(ID));
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const InfoRecord &R) {
+ return OS << "INFO CODE_ID " << R.ID.GetAsString();
+}
+
+template <typename T>
+static std::optional<T> parseNumberName(llvm::StringRef Line, Token TokenType) {
+ // TOKEN number name
+ if (consume<Token>(Line) != TokenType)
+ return std::nullopt;
+
+ llvm::StringRef Str;
+ size_t Number;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Number))
+ return std::nullopt;
+
+ llvm::StringRef Name = Line.trim();
+ if (Name.empty())
+ return std::nullopt;
+
+ return T(Number, Name);
+}
+
+std::optional<FileRecord> FileRecord::parse(llvm::StringRef Line) {
+ // FILE number name
+ return parseNumberName<FileRecord>(Line, Token::File);
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const FileRecord &R) {
+ return OS << "FILE " << R.Number << " " << R.Name;
+}
+
+std::optional<InlineOriginRecord>
+InlineOriginRecord::parse(llvm::StringRef Line) {
+ // INLINE_ORIGIN number name
+ return parseNumberName<InlineOriginRecord>(Line, Token::InlineOrigin);
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const InlineOriginRecord &R) {
+ return OS << "INLINE_ORIGIN " << R.Number << " " << R.Name;
+}
+
+static bool parsePublicOrFunc(llvm::StringRef Line, bool &Multiple,
+ lldb::addr_t &Address, lldb::addr_t *Size,
+ lldb::addr_t &ParamSize, llvm::StringRef &Name) {
+ // PUBLIC [m] address param_size name
+ // or
+ // FUNC [m] address size param_size name
+
+ Token Tok = Size ? Token::Func : Token::Public;
+
+ if (consume<Token>(Line) != Tok)
+ return false;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ Multiple = Str == "m";
+
+ if (Multiple)
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Address, 16))
+ return false;
+
+ if (Tok == Token::Func) {
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, *Size, 16))
+ return false;
+ }
+
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, ParamSize, 16))
+ return false;
+
+ Name = Line.trim();
+ if (Name.empty())
+ return false;
+
+ return true;
+}
+
+std::optional<FuncRecord> FuncRecord::parse(llvm::StringRef Line) {
+ bool Multiple;
+ lldb::addr_t Address, Size, ParamSize;
+ llvm::StringRef Name;
+
+ if (parsePublicOrFunc(Line, Multiple, Address, &Size, ParamSize, Name))
+ return FuncRecord(Multiple, Address, Size, ParamSize, Name);
+
+ return std::nullopt;
+}
+
+bool breakpad::operator==(const FuncRecord &L, const FuncRecord &R) {
+ return L.Multiple == R.Multiple && L.Address == R.Address &&
+ L.Size == R.Size && L.ParamSize == R.ParamSize && L.Name == R.Name;
+}
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const FuncRecord &R) {
+ return OS << llvm::formatv("FUNC {0}{1:x-} {2:x-} {3:x-} {4}",
+ R.Multiple ? "m " : "", R.Address, R.Size,
+ R.ParamSize, R.Name);
+}
+
+std::optional<InlineRecord> InlineRecord::parse(llvm::StringRef Line) {
+ // INLINE inline_nest_level call_site_line call_site_file_num origin_num
+ // [address size]+
+ if (consume<Token>(Line) != Token::Inline)
+ return std::nullopt;
+
+ llvm::SmallVector<llvm::StringRef> Tokens;
+ SplitString(Line, Tokens, " ");
+ if (Tokens.size() < 6 || Tokens.size() % 2 == 1)
+ return std::nullopt;
+
+ size_t InlineNestLevel;
+ uint32_t CallSiteLineNum;
+ size_t CallSiteFileNum;
+ size_t OriginNum;
+ if (!(to_integer(Tokens[0], InlineNestLevel) &&
+ to_integer(Tokens[1], CallSiteLineNum) &&
+ to_integer(Tokens[2], CallSiteFileNum) &&
+ to_integer(Tokens[3], OriginNum)))
+ return std::nullopt;
+
+ InlineRecord Record = InlineRecord(InlineNestLevel, CallSiteLineNum,
+ CallSiteFileNum, OriginNum);
+ for (size_t i = 4; i < Tokens.size(); i += 2) {
+ lldb::addr_t Address;
+ if (!to_integer(Tokens[i], Address, 16))
+ return std::nullopt;
+ lldb::addr_t Size;
+ if (!to_integer(Tokens[i + 1].trim(), Size, 16))
+ return std::nullopt;
+ Record.Ranges.emplace_back(Address, Size);
+ }
+ return Record;
+}
+
+bool breakpad::operator==(const InlineRecord &L, const InlineRecord &R) {
+ return L.InlineNestLevel == R.InlineNestLevel &&
+ L.CallSiteLineNum == R.CallSiteLineNum &&
+ L.CallSiteFileNum == R.CallSiteFileNum && L.OriginNum == R.OriginNum &&
+ L.Ranges == R.Ranges;
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const InlineRecord &R) {
+ OS << llvm::formatv("INLINE {0} {1} {2} {3}", R.InlineNestLevel,
+ R.CallSiteLineNum, R.CallSiteFileNum, R.OriginNum);
+ for (const auto &range : R.Ranges) {
+ OS << llvm::formatv(" {0:x-} {1:x-}", range.first, range.second);
+ }
+ return OS;
+}
+
+std::optional<LineRecord> LineRecord::parse(llvm::StringRef Line) {
+ lldb::addr_t Address;
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Address, 16))
+ return std::nullopt;
+
+ lldb::addr_t Size;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, Size, 16))
+ return std::nullopt;
+
+ uint32_t LineNum;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, LineNum))
+ return std::nullopt;
+
+ size_t FileNum;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, FileNum))
+ return std::nullopt;
+
+ return LineRecord(Address, Size, LineNum, FileNum);
+}
+
+bool breakpad::operator==(const LineRecord &L, const LineRecord &R) {
+ return L.Address == R.Address && L.Size == R.Size && L.LineNum == R.LineNum &&
+ L.FileNum == R.FileNum;
+}
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const LineRecord &R) {
+ return OS << llvm::formatv("{0:x-} {1:x-} {2} {3}", R.Address, R.Size,
+ R.LineNum, R.FileNum);
+}
+
+std::optional<PublicRecord> PublicRecord::parse(llvm::StringRef Line) {
+ bool Multiple;
+ lldb::addr_t Address, ParamSize;
+ llvm::StringRef Name;
+
+ if (parsePublicOrFunc(Line, Multiple, Address, nullptr, ParamSize, Name))
+ return PublicRecord(Multiple, Address, ParamSize, Name);
+
+ return std::nullopt;
+}
+
+bool breakpad::operator==(const PublicRecord &L, const PublicRecord &R) {
+ return L.Multiple == R.Multiple && L.Address == R.Address &&
+ L.ParamSize == R.ParamSize && L.Name == R.Name;
+}
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const PublicRecord &R) {
+ return OS << llvm::formatv("PUBLIC {0}{1:x-} {2:x-} {3}",
+ R.Multiple ? "m " : "", R.Address, R.ParamSize,
+ R.Name);
+}
+
+std::optional<StackCFIRecord> 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<Token>(Line) != Token::Stack)
+ return std::nullopt;
+ if (consume<Token>(Line) != Token::CFI)
+ return std::nullopt;
+
+ llvm::StringRef Str;
+ std::tie(Str, Line) = getToken(Line);
+
+ bool IsInitRecord = stringTo<Token>(Str) == Token::Init;
+ if (IsInitRecord)
+ std::tie(Str, Line) = getToken(Line);
+
+ lldb::addr_t Address;
+ if (!to_integer(Str, Address, 16))
+ return std::nullopt;
+
+ std::optional<lldb::addr_t> Size;
+ if (IsInitRecord) {
+ Size.emplace();
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, *Size, 16))
+ return std::nullopt;
+ }
+
+ return StackCFIRecord(Address, Size, Line.trim());
+}
+
+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);
+ return OS << " " << R.UnwindRules;
+}
+
+std::optional<StackWinRecord> StackWinRecord::parse(llvm::StringRef Line) {
+ // STACK WIN type rva code_size prologue_size epilogue_size parameter_size
+ // saved_register_size local_size max_stack_size has_program_string
+ // program_string_OR_allocates_base_pointer
+
+ if (consume<Token>(Line) != Token::Stack)
+ return std::nullopt;
+ if (consume<Token>(Line) != Token::Win)
+ return std::nullopt;
+
+ llvm::StringRef Str;
+ uint8_t Type;
+ std::tie(Str, Line) = getToken(Line);
+ // Right now we only support the "FrameData" frame type.
+ if (!to_integer(Str, Type) || FrameType(Type) != FrameType::FrameData)
+ return std::nullopt;
+
+ lldb::addr_t RVA;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, RVA, 16))
+ return std::nullopt;
+
+ lldb::addr_t CodeSize;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, CodeSize, 16))
+ return std::nullopt;
+
+ // Skip fields which we aren't using right now.
+ std::tie(Str, Line) = getToken(Line); // prologue_size
+ std::tie(Str, Line) = getToken(Line); // epilogue_size
+
+ lldb::addr_t ParameterSize;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, ParameterSize, 16))
+ return std::nullopt;
+
+ lldb::addr_t SavedRegisterSize;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, SavedRegisterSize, 16))
+ return std::nullopt;
+
+ lldb::addr_t LocalSize;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, LocalSize, 16))
+ return std::nullopt;
+
+ std::tie(Str, Line) = getToken(Line); // max_stack_size
+
+ uint8_t HasProgramString;
+ std::tie(Str, Line) = getToken(Line);
+ if (!to_integer(Str, HasProgramString))
+ return std::nullopt;
+ // FrameData records should always have a program string.
+ if (!HasProgramString)
+ return std::nullopt;
+
+ return StackWinRecord(RVA, CodeSize, ParameterSize, SavedRegisterSize,
+ LocalSize, Line.trim());
+}
+
+bool breakpad::operator==(const StackWinRecord &L, const StackWinRecord &R) {
+ return L.RVA == R.RVA && L.CodeSize == R.CodeSize &&
+ L.ParameterSize == R.ParameterSize &&
+ L.SavedRegisterSize == R.SavedRegisterSize &&
+ L.LocalSize == R.LocalSize && L.ProgramString == R.ProgramString;
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+ const StackWinRecord &R) {
+ return OS << llvm::formatv(
+ "STACK WIN 4 {0:x-} {1:x-} ? ? {2} {3} {4} ? 1 {5}", R.RVA,
+ R.CodeSize, R.ParameterSize, R.SavedRegisterSize, R.LocalSize,
+ R.ProgramString);
+}
+
+llvm::StringRef breakpad::toString(Record::Kind K) {
+ switch (K) {
+ case Record::Module:
+ return "MODULE";
+ case Record::Info:
+ return "INFO";
+ case Record::File:
+ return "FILE";
+ case Record::Func:
+ return "FUNC";
+ case Record::Inline:
+ return "INLINE";
+ case Record::InlineOrigin:
+ return "INLINE_ORIGIN";
+ case Record::Line:
+ return "LINE";
+ case Record::Public:
+ return "PUBLIC";
+ case Record::StackCFI:
+ return "STACK CFI";
+ case Record::StackWin:
+ return "STACK WIN";
+ }
+ llvm_unreachable("Unknown record kind!");
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h
new file mode 100644
index 000000000000..f10c8c41b793
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h
@@ -0,0 +1,235 @@
+//===-- BreakpadRecords.h ------------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
+#define LLDB_SOURCE_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
+
+#include "lldb/Utility/UUID.h"
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FormatProviders.h"
+#include "llvm/TargetParser/Triple.h"
+#include <optional>
+
+namespace lldb_private {
+namespace breakpad {
+
+class Record {
+public:
+ enum Kind {
+ Module,
+ Info,
+ File,
+ Func,
+ Inline,
+ InlineOrigin,
+ Line,
+ Public,
+ StackCFI,
+ StackWin
+ };
+
+ /// 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
+ /// records, but the full parse can still fail in case of corrupted input.
+ static std::optional<Kind> classify(llvm::StringRef Line);
+
+protected:
+ Record(Kind K) : TheKind(K) {}
+
+ ~Record() = default;
+
+public:
+ Kind getKind() { return TheKind; }
+
+private:
+ Kind TheKind;
+};
+
+llvm::StringRef toString(Record::Kind K);
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Record::Kind K) {
+ OS << toString(K);
+ return OS;
+}
+
+class ModuleRecord : public Record {
+public:
+ static std::optional<ModuleRecord> parse(llvm::StringRef Line);
+ ModuleRecord(llvm::Triple::OSType OS, llvm::Triple::ArchType Arch, UUID ID)
+ : Record(Module), OS(OS), Arch(Arch), ID(std::move(ID)) {}
+
+ llvm::Triple::OSType OS;
+ llvm::Triple::ArchType Arch;
+ UUID ID;
+};
+
+inline bool operator==(const ModuleRecord &L, const ModuleRecord &R) {
+ return L.OS == R.OS && L.Arch == R.Arch && L.ID == R.ID;
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ModuleRecord &R);
+
+class InfoRecord : public Record {
+public:
+ static std::optional<InfoRecord> parse(llvm::StringRef Line);
+ InfoRecord(UUID ID) : Record(Info), ID(std::move(ID)) {}
+
+ UUID ID;
+};
+
+inline bool operator==(const InfoRecord &L, const InfoRecord &R) {
+ return L.ID == R.ID;
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const InfoRecord &R);
+
+class FileRecord : public Record {
+public:
+ static std::optional<FileRecord> parse(llvm::StringRef Line);
+ FileRecord(size_t Number, llvm::StringRef Name)
+ : Record(File), Number(Number), Name(Name) {}
+
+ size_t Number;
+ llvm::StringRef Name;
+};
+
+inline bool operator==(const FileRecord &L, const FileRecord &R) {
+ return L.Number == R.Number && L.Name == R.Name;
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const FileRecord &R);
+
+class InlineOriginRecord : public Record {
+public:
+ static std::optional<InlineOriginRecord> parse(llvm::StringRef Line);
+ InlineOriginRecord(size_t Number, llvm::StringRef Name)
+ : Record(InlineOrigin), Number(Number), Name(Name) {}
+
+ size_t Number;
+ llvm::StringRef Name;
+};
+
+inline bool operator==(const InlineOriginRecord &L,
+ const InlineOriginRecord &R) {
+ return L.Number == R.Number && L.Name == R.Name;
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ const InlineOriginRecord &R);
+
+class FuncRecord : public Record {
+public:
+ static std::optional<FuncRecord> parse(llvm::StringRef Line);
+ FuncRecord(bool Multiple, lldb::addr_t Address, lldb::addr_t Size,
+ lldb::addr_t ParamSize, llvm::StringRef Name)
+ : Record(Module), Multiple(Multiple), Address(Address), Size(Size),
+ ParamSize(ParamSize), Name(Name) {}
+
+ bool Multiple;
+ lldb::addr_t Address;
+ lldb::addr_t Size;
+ lldb::addr_t ParamSize;
+ llvm::StringRef Name;
+};
+
+bool operator==(const FuncRecord &L, const FuncRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const FuncRecord &R);
+
+class InlineRecord : public Record {
+public:
+ static std::optional<InlineRecord> parse(llvm::StringRef Line);
+ InlineRecord(size_t InlineNestLevel, uint32_t CallSiteLineNum,
+ size_t CallSiteFileNum, size_t OriginNum)
+ : Record(Inline), InlineNestLevel(InlineNestLevel),
+ CallSiteLineNum(CallSiteLineNum), CallSiteFileNum(CallSiteFileNum),
+ OriginNum(OriginNum) {}
+
+ size_t InlineNestLevel;
+ uint32_t CallSiteLineNum;
+ size_t CallSiteFileNum;
+ size_t OriginNum;
+ // A vector of address range covered by this inline
+ std::vector<std::pair<lldb::addr_t, lldb::addr_t>> Ranges;
+};
+
+bool operator==(const InlineRecord &L, const InlineRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const InlineRecord &R);
+
+class LineRecord : public Record {
+public:
+ static std::optional<LineRecord> parse(llvm::StringRef Line);
+ LineRecord(lldb::addr_t Address, lldb::addr_t Size, uint32_t LineNum,
+ size_t FileNum)
+ : Record(Line), Address(Address), Size(Size), LineNum(LineNum),
+ FileNum(FileNum) {}
+
+ lldb::addr_t Address;
+ lldb::addr_t Size;
+ uint32_t LineNum;
+ size_t FileNum;
+};
+
+bool operator==(const LineRecord &L, const LineRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const LineRecord &R);
+
+class PublicRecord : public Record {
+public:
+ static std::optional<PublicRecord> parse(llvm::StringRef Line);
+ PublicRecord(bool Multiple, lldb::addr_t Address, lldb::addr_t ParamSize,
+ llvm::StringRef Name)
+ : Record(Module), Multiple(Multiple), Address(Address),
+ ParamSize(ParamSize), Name(Name) {}
+
+ bool Multiple;
+ lldb::addr_t Address;
+ lldb::addr_t ParamSize;
+ llvm::StringRef Name;
+};
+
+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 std::optional<StackCFIRecord> parse(llvm::StringRef Line);
+ StackCFIRecord(lldb::addr_t Address, std::optional<lldb::addr_t> Size,
+ llvm::StringRef UnwindRules)
+ : Record(StackCFI), Address(Address), Size(Size),
+ UnwindRules(UnwindRules) {}
+
+ lldb::addr_t Address;
+ std::optional<lldb::addr_t> Size;
+ llvm::StringRef UnwindRules;
+};
+
+bool operator==(const StackCFIRecord &L, const StackCFIRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const StackCFIRecord &R);
+
+class StackWinRecord : public Record {
+public:
+ static std::optional<StackWinRecord> parse(llvm::StringRef Line);
+
+ StackWinRecord(lldb::addr_t RVA, lldb::addr_t CodeSize,
+ lldb::addr_t ParameterSize, lldb::addr_t SavedRegisterSize,
+ lldb::addr_t LocalSize, llvm::StringRef ProgramString)
+ : Record(StackWin), RVA(RVA), CodeSize(CodeSize),
+ ParameterSize(ParameterSize), SavedRegisterSize(SavedRegisterSize),
+ LocalSize(LocalSize), ProgramString(ProgramString) {}
+
+ enum class FrameType : uint8_t { FPO = 0, FrameData = 4 };
+ lldb::addr_t RVA;
+ lldb::addr_t CodeSize;
+ lldb::addr_t ParameterSize;
+ lldb::addr_t SavedRegisterSize;
+ lldb::addr_t LocalSize;
+ llvm::StringRef ProgramString;
+};
+
+bool operator==(const StackWinRecord &L, const StackWinRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const StackWinRecord &R);
+
+} // namespace breakpad
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp
new file mode 100644
index 000000000000..33673f139b49
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp
@@ -0,0 +1,169 @@
+//===-- ObjectFileBreakpad.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::breakpad;
+
+LLDB_PLUGIN_DEFINE(ObjectFileBreakpad)
+
+namespace {
+struct Header {
+ ArchSpec arch;
+ UUID uuid;
+ static std::optional<Header> parse(llvm::StringRef text);
+};
+} // namespace
+
+std::optional<Header> Header::parse(llvm::StringRef text) {
+ llvm::StringRef line;
+ std::tie(line, text) = text.split('\n');
+ auto Module = ModuleRecord::parse(line);
+ if (!Module)
+ return std::nullopt;
+
+ llvm::Triple triple;
+ triple.setArch(Module->Arch);
+ triple.setOS(Module->OS);
+
+ std::tie(line, text) = text.split('\n');
+
+ auto Info = InfoRecord::parse(line);
+ UUID uuid = Info && Info->ID ? Info->ID : Module->ID;
+ return Header{ArchSpec(triple), std::move(uuid)};
+}
+
+char ObjectFileBreakpad::ID;
+
+void ObjectFileBreakpad::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance,
+ CreateMemoryInstance, GetModuleSpecifications);
+}
+
+void ObjectFileBreakpad::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ObjectFile *ObjectFileBreakpad::CreateInstance(
+ const ModuleSP &module_sp, DataBufferSP data_sp, offset_t data_offset,
+ const FileSpec *file, offset_t file_offset, offset_t length) {
+ if (!data_sp) {
+ data_sp = MapFileData(*file, length, file_offset);
+ if (!data_sp)
+ return nullptr;
+ data_offset = 0;
+ }
+ auto text = toStringRef(data_sp->GetData());
+ std::optional<Header> header = Header::parse(text);
+ if (!header)
+ return nullptr;
+
+ // Update the data to contain the entire file if it doesn't already
+ if (data_sp->GetByteSize() < length) {
+ data_sp = MapFileData(*file, length, file_offset);
+ if (!data_sp)
+ return nullptr;
+ data_offset = 0;
+ }
+
+ return new ObjectFileBreakpad(module_sp, data_sp, data_offset, file,
+ file_offset, length, std::move(header->arch),
+ std::move(header->uuid));
+}
+
+ObjectFile *ObjectFileBreakpad::CreateMemoryInstance(
+ const ModuleSP &module_sp, WritableDataBufferSP data_sp,
+ const ProcessSP &process_sp, addr_t header_addr) {
+ return nullptr;
+}
+
+size_t ObjectFileBreakpad::GetModuleSpecifications(
+ const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,
+ offset_t file_offset, offset_t length, ModuleSpecList &specs) {
+ auto text = toStringRef(data_sp->GetData());
+ std::optional<Header> header = Header::parse(text);
+ if (!header)
+ return 0;
+ ModuleSpec spec(file, std::move(header->arch));
+ spec.GetUUID() = std::move(header->uuid);
+ specs.Append(spec);
+ return 1;
+}
+
+ObjectFileBreakpad::ObjectFileBreakpad(const ModuleSP &module_sp,
+ DataBufferSP &data_sp,
+ offset_t data_offset,
+ const FileSpec *file, offset_t offset,
+ offset_t length, ArchSpec arch,
+ UUID uuid)
+ : ObjectFile(module_sp, file, offset, length, data_sp, data_offset),
+ m_arch(std::move(arch)), m_uuid(std::move(uuid)) {}
+
+bool ObjectFileBreakpad::ParseHeader() {
+ // We already parsed the header during initialization.
+ return true;
+}
+
+void ObjectFileBreakpad::ParseSymtab(Symtab &symtab) {
+ // Nothing to do for breakpad files, all information is parsed as debug info
+ // which means "lldb_private::Function" objects are used, or symbols are added
+ // by the SymbolFileBreakpad::AddSymbols(...) function in the symbol file.
+}
+
+void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) {
+ if (m_sections_up)
+ return;
+ m_sections_up = std::make_unique<SectionList>();
+
+ std::optional<Record::Kind> current_section;
+ offset_t section_start;
+ llvm::StringRef text = toStringRef(m_data.GetData());
+ uint32_t next_section_id = 1;
+ auto maybe_add_section = [&](const uint8_t *end_ptr) {
+ if (!current_section)
+ return; // We have been called before parsing the first line.
+
+ offset_t end_offset = end_ptr - m_data.GetDataStart();
+ auto section_sp = std::make_shared<Section>(
+ GetModule(), this, next_section_id++,
+ ConstString(toString(*current_section)), eSectionTypeOther,
+ /*file_vm_addr*/ 0, /*vm_size*/ 0, section_start,
+ end_offset - section_start, /*log2align*/ 0, /*flags*/ 0);
+ m_sections_up->AddSection(section_sp);
+ unified_section_list.AddSection(section_sp);
+ };
+ while (!text.empty()) {
+ llvm::StringRef line;
+ std::tie(line, text) = text.split('\n');
+
+ std::optional<Record::Kind> next_section = Record::classify(line);
+ if (next_section == Record::Line || next_section == Record::Inline) {
+ // Line/Inline records logically belong to the preceding Func record, so
+ // we put them in the same section.
+ next_section = Record::Func;
+ }
+ if (next_section == current_section)
+ continue;
+
+ // Changing sections, finish off the previous one, if there was any.
+ maybe_add_section(line.bytes_begin());
+ // And start a new one.
+ current_section = next_section;
+ section_start = line.bytes_begin() - m_data.GetDataStart();
+ }
+ // Finally, add the last section.
+ maybe_add_section(m_data.GetDataEnd());
+}
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h
new file mode 100644
index 000000000000..074d667c1ca5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h
@@ -0,0 +1,104 @@
+//===-- ObjectFileBreakpad.h ---------------------------------- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H
+#define LLDB_SOURCE_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H
+
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Utility/ArchSpec.h"
+
+namespace lldb_private {
+namespace breakpad {
+
+class ObjectFileBreakpad : public ObjectFile {
+public:
+ // Static Functions
+ static void Initialize();
+ static void Terminate();
+
+ static llvm::StringRef GetPluginNameStatic() { return "breakpad"; }
+ static const char *GetPluginDescriptionStatic() {
+ return "Breakpad object file reader.";
+ }
+
+ static ObjectFile *
+ CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp,
+ lldb::offset_t data_offset, const FileSpec *file,
+ lldb::offset_t file_offset, lldb::offset_t length);
+
+ static ObjectFile *CreateMemoryInstance(const lldb::ModuleSP &module_sp,
+ lldb::WritableDataBufferSP data_sp,
+ const lldb::ProcessSP &process_sp,
+ lldb::addr_t header_addr);
+
+ static size_t GetModuleSpecifications(const FileSpec &file,
+ lldb::DataBufferSP &data_sp,
+ lldb::offset_t data_offset,
+ lldb::offset_t file_offset,
+ lldb::offset_t length,
+ ModuleSpecList &specs);
+
+ // PluginInterface protocol
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+ // LLVM RTTI support
+ static char ID;
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || ObjectFile::isA(ClassID);
+ }
+ static bool classof(const ObjectFile *obj) { return obj->isA(&ID); }
+
+ // ObjectFile Protocol.
+
+ bool ParseHeader() override;
+
+ lldb::ByteOrder GetByteOrder() const override {
+ return m_arch.GetByteOrder();
+ }
+
+ bool IsExecutable() const override { return false; }
+
+ uint32_t GetAddressByteSize() const override {
+ return m_arch.GetAddressByteSize();
+ }
+
+ AddressClass GetAddressClass(lldb::addr_t file_addr) override {
+ return AddressClass::eInvalid;
+ }
+
+ void ParseSymtab(lldb_private::Symtab &symtab) override;
+
+ bool IsStripped() override { return false; }
+
+ void CreateSections(SectionList &unified_section_list) override;
+
+ void Dump(Stream *s) override {}
+
+ ArchSpec GetArchitecture() override { return m_arch; }
+
+ UUID GetUUID() override { return m_uuid; }
+
+ uint32_t GetDependentModules(FileSpecList &files) override { return 0; }
+
+ Type CalculateType() override { return eTypeDebugInfo; }
+
+ Strata CalculateStrata() override { return eStrataUser; }
+
+private:
+ ArchSpec m_arch;
+ UUID m_uuid;
+
+ ObjectFileBreakpad(const lldb::ModuleSP &module_sp,
+ lldb::DataBufferSP &data_sp, lldb::offset_t data_offset,
+ const FileSpec *file, lldb::offset_t offset,
+ lldb::offset_t length, ArchSpec arch, UUID uuid);
+};
+
+} // namespace breakpad
+} // namespace lldb_private
+#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H