diff options
Diffstat (limited to 'source/Plugins/ObjectFile/Breakpad')
4 files changed, 603 insertions, 184 deletions
diff --git a/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp b/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp new file mode 100644 index 000000000000..d489eaf11115 --- /dev/null +++ b/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp @@ -0,0 +1,411 @@ +//===-- BreakpadRecords.cpp ----------------------------------- -*- 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 +// +//===----------------------------------------------------------------------===// + +#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace lldb_private; +using namespace lldb_private::breakpad; + +namespace { +enum class Token { Unknown, Module, Info, CodeID, File, Func, Public, Stack, CFI, Init }; +} + +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("PUBLIC", Token::Public) + .Case("STACK", Token::Stack) + .Case("CFI", Token::CFI) + .Case("INIT", Token::Init) + .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("ppc", Triple::ppc) + .Case("ppc64", Triple::ppc64) + .Case("s390", Triple::systemz) + .Case("sparc", Triple::sparc) + .Case("sparcv9", Triple::sparcv9) + .Case("x86", Triple::x86) + .Case("x86_64", 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); + (void)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::fromData(&data, os == llvm::Triple::Win32 ? sizeof(data) + : sizeof(data.uuid)); +} + +llvm::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; + default: + return llvm::None; + } + + 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. CODE_ID should never be at the start of a line, but if it + // is, it can be treated the same way as a garbled line record. + return Record::Line; + + case Token::CodeID: + case Token::CFI: + case Token::Init: + // These should never appear at the start of a valid record. + return llvm::None; + } + llvm_unreachable("Fully covered switch above!"); +} + +llvm::Optional<ModuleRecord> ModuleRecord::parse(llvm::StringRef Line) { + // MODULE Linux x86_64 E5894855C35DCCCCCCCCCCCCCCCCCCCC0 a.out + if (consume<Token>(Line) != Token::Module) + return llvm::None; + + llvm::Triple::OSType OS = consume<llvm::Triple::OSType>(Line); + if (OS == llvm::Triple::UnknownOS) + return llvm::None; + + llvm::Triple::ArchType Arch = consume<llvm::Triple::ArchType>(Line); + if (Arch == llvm::Triple::UnknownArch) + return llvm::None; + + llvm::StringRef Str; + std::tie(Str, Line) = getToken(Line); + UUID ID = parseModuleId(OS, Str); + if (!ID) + return llvm::None; + + 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(); +} + +llvm::Optional<InfoRecord> InfoRecord::parse(llvm::StringRef Line) { + // INFO CODE_ID 554889E55DC3CCCCCCCCCCCCCCCCCCCC [a.exe] + if (consume<Token>(Line) != Token::Info) + return llvm::None; + + if (consume<Token>(Line) != Token::CodeID) + return llvm::None; + + 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, Str.size() / 2) != Str.size()) + return llvm::None; + } + 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(); +} + +llvm::Optional<FileRecord> FileRecord::parse(llvm::StringRef Line) { + // FILE number name + if (consume<Token>(Line) != Token::File) + return llvm::None; + + llvm::StringRef Str; + size_t Number; + std::tie(Str, Line) = getToken(Line); + if (!to_integer(Str, Number)) + return llvm::None; + + llvm::StringRef Name = Line.trim(); + if (Name.empty()) + return llvm::None; + + return FileRecord(Number, Name); +} + +llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS, + const FileRecord &R) { + return OS << "FILE " << 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; +} + +llvm::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 llvm::None; +} + +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); +} + +llvm::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 llvm::None; + + lldb::addr_t Size; + std::tie(Str, Line) = getToken(Line); + if (!to_integer(Str, Size, 16)) + return llvm::None; + + uint32_t LineNum; + std::tie(Str, Line) = getToken(Line); + if (!to_integer(Str, LineNum)) + return llvm::None; + + size_t FileNum; + std::tie(Str, Line) = getToken(Line); + if (!to_integer(Str, FileNum)) + return llvm::None; + + 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); +} + +llvm::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 llvm::None; +} + +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); +} + +llvm::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 llvm::None; + if (consume<Token>(Line) != Token::CFI) + return llvm::None; + + 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 llvm::None; + + llvm::Optional<lldb::addr_t> Size; + if (IsInitRecord) { + Size.emplace(); + std::tie(Str, Line) = getToken(Line); + if (!to_integer(Str, *Size, 16)) + return llvm::None; + } + + 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; +} + +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::Line: + return "LINE"; + case Record::Public: + return "PUBLIC"; + case Record::StackCFI: + return "STACK CFI"; + } + llvm_unreachable("Unknown record kind!"); +} diff --git a/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h b/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h new file mode 100644 index 000000000000..5d5cdb319c10 --- /dev/null +++ b/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h @@ -0,0 +1,163 @@ +//===-- 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_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H +#define LLDB_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H + +#include "lldb/Utility/UUID.h" +#include "lldb/lldb-types.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/FormatProviders.h" + +namespace lldb_private { +namespace breakpad { + +class Record { +public: + 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 + /// records, but the full parse can still fail in case of corrupted input. + static llvm::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 llvm::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 llvm::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 llvm::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 FuncRecord : public Record { +public: + static llvm::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 LineRecord : public Record { +public: + static llvm::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 llvm::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 llvm::Optional<StackCFIRecord> parse(llvm::StringRef Line); + StackCFIRecord(lldb::addr_t Address, llvm::Optional<lldb::addr_t> Size, + llvm::StringRef UnwindRules) + : Record(StackCFI), Address(Address), Size(Size), + UnwindRules(UnwindRules) {} + + lldb::addr_t Address; + llvm::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); + +} // namespace breakpad +} // namespace lldb_private + +#endif // LLDB_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H diff --git a/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp b/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp index 917025030ada..60dd9f9cecf0 100644 --- a/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp +++ b/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp @@ -1,18 +1,16 @@ //===-- ObjectFileBreakpad.cpp -------------------------------- -*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 "lldb/Utility/DataBuffer.h" -#include "llvm/ADT/StringExtras.h" using namespace lldb; using namespace lldb_private; @@ -24,164 +22,24 @@ struct Header { UUID uuid; static llvm::Optional<Header> parse(llvm::StringRef text); }; - -enum class Token { Unknown, Module, Info, File, Func, Public, Stack }; } // namespace -static Token toToken(llvm::StringRef str) { - return llvm::StringSwitch<Token>(str) - .Case("MODULE", Token::Module) - .Case("INFO", Token::Info) - .Case("FILE", Token::File) - .Case("FUNC", Token::Func) - .Case("PUBLIC", Token::Public) - .Case("STACK", Token::Stack) - .Default(Token::Unknown); -} - -static llvm::StringRef toString(Token t) { - switch (t) { - case Token::Unknown: - return ""; - case Token::Module: - return "MODULE"; - case Token::Info: - return "INFO"; - case Token::File: - return "FILE"; - case Token::Func: - return "FUNC"; - case Token::Public: - return "PUBLIC"; - case Token::Stack: - return "STACK"; - } - llvm_unreachable("Unknown token!"); -} - -static llvm::Triple::OSType toOS(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); -} - -static llvm::Triple::ArchType toArch(llvm::StringRef str) { - using llvm::Triple; - return llvm::StringSwitch<Triple::ArchType>(str) - .Case("arm", Triple::arm) - .Case("arm64", Triple::aarch64) - .Case("mips", Triple::mips) - .Case("ppc", Triple::ppc) - .Case("ppc64", Triple::ppc64) - .Case("s390", Triple::systemz) - .Case("sparc", Triple::sparc) - .Case("sparcv9", Triple::sparcv9) - .Case("x86", Triple::x86) - .Case("x86_64", Triple::x86_64) - .Default(Triple::UnknownArch); -} - -static llvm::StringRef consume_front(llvm::StringRef &str, size_t n) { - llvm::StringRef result = str.take_front(n); - str = str.drop_front(n); - return result; -} - -static UUID parseModuleId(llvm::Triple::OSType os, llvm::StringRef str) { - struct uuid_data { - llvm::support::ulittle32_t uuid1; - llvm::support::ulittle16_t uuid2[2]; - uint8_t uuid3[8]; - llvm::support::ulittle32_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() < 33 || str.size() > 40) - return UUID(); - uint32_t t; - if (to_integer(consume_front(str, 8), t, 16)) - data.uuid1 = t; - else - return UUID(); - for (int i = 0; i < 2; ++i) { - if (to_integer(consume_front(str, 4), t, 16)) - data.uuid2[i] = t; - else - return UUID(); - } - for (int i = 0; i < 8; ++i) { - if (!to_integer(consume_front(str, 2), data.uuid3[i], 16)) - return UUID(); - } - if (to_integer(str, t, 16)) - data.age = t; - else - return UUID(); - - // 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::fromData(&data, os == llvm::Triple::Win32 ? 20 : 16); -} - llvm::Optional<Header> Header::parse(llvm::StringRef text) { - // A valid module should start with something like: - // MODULE Linux x86_64 E5894855C35DCCCCCCCCCCCCCCCCCCCC0 a.out - // optionally followed by - // INFO CODE_ID 554889E55DC3CCCCCCCCCCCCCCCCCCCC [a.exe] - llvm::StringRef token, line; + llvm::StringRef line; std::tie(line, text) = text.split('\n'); - std::tie(token, line) = getToken(line); - if (toToken(token) != Token::Module) + auto Module = ModuleRecord::parse(line); + if (!Module) return llvm::None; - std::tie(token, line) = getToken(line); llvm::Triple triple; - triple.setOS(toOS(token)); - if (triple.getOS() == llvm::Triple::UnknownOS) - return llvm::None; - - std::tie(token, line) = getToken(line); - triple.setArch(toArch(token)); - if (triple.getArch() == llvm::Triple::UnknownArch) - return llvm::None; - - llvm::StringRef module_id; - std::tie(module_id, line) = getToken(line); + triple.setArch(Module->Arch); + triple.setOS(Module->OS); std::tie(line, text) = text.split('\n'); - std::tie(token, line) = getToken(line); - if (token == "INFO") { - std::tie(token, line) = getToken(line); - if (token != "CODE_ID") - return llvm::None; - std::tie(token, line) = getToken(line); - // If we don't have any text following the code id (e.g. on linux), we - // should use the module id as UUID. Otherwise, we revert back to the module - // id. - if (line.trim().empty()) { - UUID uuid; - if (uuid.SetFromStringRef(token, token.size() / 2) != token.size()) - return llvm::None; - - return Header{ArchSpec(triple), uuid}; - } - } - - // We reach here if we don't have a INFO CODE_ID section, or we chose not to - // use it. In either case, we need to properly decode the module id, whose - // fields are encoded in big-endian. - UUID uuid = parseModuleId(triple.getOS(), module_id); - if (!uuid) - return llvm::None; - - return Header{ArchSpec(triple), uuid}; + auto Info = InfoRecord::parse(line); + UUID uuid = Info && Info->ID ? Info->ID : Module->ID; + return Header{ArchSpec(triple), std::move(uuid)}; } void ObjectFileBreakpad::Initialize() { @@ -264,50 +122,45 @@ Symtab *ObjectFileBreakpad::GetSymtab() { return nullptr; } -bool ObjectFileBreakpad::GetUUID(UUID *uuid) { - *uuid = m_uuid; - return true; -} - void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) { - if (m_sections_ap) + if (m_sections_up) return; - m_sections_ap = llvm::make_unique<SectionList>(); + m_sections_up = llvm::make_unique<SectionList>(); - Token current_section = Token::Unknown; + llvm::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 == Token::Unknown) + 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, + ConstString(toString(*current_section)), eSectionTypeOther, /*file_vm_addr*/ 0, /*vm_size*/ 0, section_start, end_offset - section_start, /*log2align*/ 0, /*flags*/ 0); - m_sections_ap->AddSection(section_sp); + 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'); - Token token = toToken(getToken(line).first); - if (token == Token::Unknown) { - // We assume this is a line record, which logically belongs to the Func - // section. Errors will be handled when parsing the Func section. - token = Token::Func; + llvm::Optional<Record::Kind> next_section = Record::classify(line); + if (next_section == Record::Line) { + // Line records logically belong to the preceding Func record, so we put + // them in the same section. + next_section = Record::Func; } - if (token == current_section) + 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 = token; + current_section = next_section; section_start = line.bytes_begin() - m_data.GetDataStart(); } // Finally, add the last section. diff --git a/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h b/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h index ba2a3ad30e5f..e8885e0cc898 100644 --- a/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h +++ b/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h @@ -1,9 +1,8 @@ //===-- ObjectFileBreakpad.h ---------------------------------- -*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -12,16 +11,13 @@ #include "lldb/Symbol/ObjectFile.h" #include "lldb/Utility/ArchSpec.h" -#include "llvm/ADT/Triple.h" namespace lldb_private { namespace breakpad { class ObjectFileBreakpad : public ObjectFile { public: - //------------------------------------------------------------------ // Static Functions - //------------------------------------------------------------------ static void Initialize(); static void Terminate(); @@ -47,16 +43,12 @@ public: lldb::offset_t length, ModuleSpecList &specs); - //------------------------------------------------------------------ // PluginInterface protocol - //------------------------------------------------------------------ ConstString GetPluginName() override { return GetPluginNameStatic(); } uint32_t GetPluginVersion() override { return 1; } - //------------------------------------------------------------------ // ObjectFile Protocol. - //------------------------------------------------------------------ bool ParseHeader() override; @@ -84,7 +76,7 @@ public: ArchSpec GetArchitecture() override { return m_arch; } - bool GetUUID(UUID *uuid) override; + UUID GetUUID() override { return m_uuid; } FileSpecList GetDebugSymbolFilePaths() override { return FileSpecList(); } |