11f6b2477SPavel Labath //===-- ObjectFileBreakpad.cpp -------------------------------- -*- C++ -*-===//
21f6b2477SPavel Labath //
31f6b2477SPavel Labath //                     The LLVM Compiler Infrastructure
41f6b2477SPavel Labath //
51f6b2477SPavel Labath // This file is distributed under the University of Illinois Open Source
61f6b2477SPavel Labath // License. See LICENSE.TXT for details.
71f6b2477SPavel Labath //
81f6b2477SPavel Labath //===----------------------------------------------------------------------===//
91f6b2477SPavel Labath 
101f6b2477SPavel Labath #include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
111f6b2477SPavel Labath #include "lldb/Core/ModuleSpec.h"
121f6b2477SPavel Labath #include "lldb/Core/PluginManager.h"
13*ed42ea47SPavel Labath #include "lldb/Core/Section.h"
141f6b2477SPavel Labath #include "lldb/Utility/DataBuffer.h"
151f6b2477SPavel Labath #include "llvm/ADT/StringExtras.h"
161f6b2477SPavel Labath 
171f6b2477SPavel Labath using namespace lldb;
181f6b2477SPavel Labath using namespace lldb_private;
191f6b2477SPavel Labath using namespace lldb_private::breakpad;
201f6b2477SPavel Labath 
211f6b2477SPavel Labath namespace {
221f6b2477SPavel Labath struct Header {
231f6b2477SPavel Labath   ArchSpec arch;
241f6b2477SPavel Labath   UUID uuid;
251f6b2477SPavel Labath   static llvm::Optional<Header> parse(llvm::StringRef text);
261f6b2477SPavel Labath };
27*ed42ea47SPavel Labath 
28*ed42ea47SPavel Labath enum class Token { Unknown, Module, Info, File, Func, Public, Stack };
291f6b2477SPavel Labath } // namespace
301f6b2477SPavel Labath 
31*ed42ea47SPavel Labath static Token toToken(llvm::StringRef str) {
32*ed42ea47SPavel Labath   return llvm::StringSwitch<Token>(str)
33*ed42ea47SPavel Labath       .Case("MODULE", Token::Module)
34*ed42ea47SPavel Labath       .Case("INFO", Token::Info)
35*ed42ea47SPavel Labath       .Case("FILE", Token::File)
36*ed42ea47SPavel Labath       .Case("FUNC", Token::Func)
37*ed42ea47SPavel Labath       .Case("PUBLIC", Token::Public)
38*ed42ea47SPavel Labath       .Case("STACK", Token::Stack)
39*ed42ea47SPavel Labath       .Default(Token::Unknown);
40*ed42ea47SPavel Labath }
41*ed42ea47SPavel Labath 
42*ed42ea47SPavel Labath static llvm::StringRef toString(Token t) {
43*ed42ea47SPavel Labath   switch (t) {
44*ed42ea47SPavel Labath   case Token::Unknown:
45*ed42ea47SPavel Labath     return "";
46*ed42ea47SPavel Labath   case Token::Module:
47*ed42ea47SPavel Labath     return "MODULE";
48*ed42ea47SPavel Labath   case Token::Info:
49*ed42ea47SPavel Labath     return "INFO";
50*ed42ea47SPavel Labath   case Token::File:
51*ed42ea47SPavel Labath     return "FILE";
52*ed42ea47SPavel Labath   case Token::Func:
53*ed42ea47SPavel Labath     return "FUNC";
54*ed42ea47SPavel Labath   case Token::Public:
55*ed42ea47SPavel Labath     return "PUBLIC";
56*ed42ea47SPavel Labath   case Token::Stack:
57*ed42ea47SPavel Labath     return "STACK";
58*ed42ea47SPavel Labath   }
59*ed42ea47SPavel Labath   llvm_unreachable("Unknown token!");
60*ed42ea47SPavel Labath }
61*ed42ea47SPavel Labath 
621f6b2477SPavel Labath static llvm::Triple::OSType toOS(llvm::StringRef str) {
631f6b2477SPavel Labath   using llvm::Triple;
641f6b2477SPavel Labath   return llvm::StringSwitch<Triple::OSType>(str)
651f6b2477SPavel Labath       .Case("Linux", Triple::Linux)
661f6b2477SPavel Labath       .Case("mac", Triple::MacOSX)
671f6b2477SPavel Labath       .Case("windows", Triple::Win32)
681f6b2477SPavel Labath       .Default(Triple::UnknownOS);
691f6b2477SPavel Labath }
701f6b2477SPavel Labath 
711f6b2477SPavel Labath static llvm::Triple::ArchType toArch(llvm::StringRef str) {
721f6b2477SPavel Labath   using llvm::Triple;
731f6b2477SPavel Labath   return llvm::StringSwitch<Triple::ArchType>(str)
741f6b2477SPavel Labath       .Case("arm", Triple::arm)
751f6b2477SPavel Labath       .Case("arm64", Triple::aarch64)
761f6b2477SPavel Labath       .Case("mips", Triple::mips)
771f6b2477SPavel Labath       .Case("ppc", Triple::ppc)
781f6b2477SPavel Labath       .Case("ppc64", Triple::ppc64)
791f6b2477SPavel Labath       .Case("s390", Triple::systemz)
801f6b2477SPavel Labath       .Case("sparc", Triple::sparc)
811f6b2477SPavel Labath       .Case("sparcv9", Triple::sparcv9)
821f6b2477SPavel Labath       .Case("x86", Triple::x86)
831f6b2477SPavel Labath       .Case("x86_64", Triple::x86_64)
841f6b2477SPavel Labath       .Default(Triple::UnknownArch);
851f6b2477SPavel Labath }
861f6b2477SPavel Labath 
871f6b2477SPavel Labath static llvm::StringRef consume_front(llvm::StringRef &str, size_t n) {
881f6b2477SPavel Labath   llvm::StringRef result = str.take_front(n);
891f6b2477SPavel Labath   str = str.drop_front(n);
901f6b2477SPavel Labath   return result;
911f6b2477SPavel Labath }
921f6b2477SPavel Labath 
931f6b2477SPavel Labath static UUID parseModuleId(llvm::Triple::OSType os, llvm::StringRef str) {
941f6b2477SPavel Labath   struct uuid_data {
951f6b2477SPavel Labath     llvm::support::ulittle32_t uuid1;
961f6b2477SPavel Labath     llvm::support::ulittle16_t uuid2[2];
971f6b2477SPavel Labath     uint8_t uuid3[8];
981f6b2477SPavel Labath     llvm::support::ulittle32_t age;
991f6b2477SPavel Labath   } data;
1001f6b2477SPavel Labath   static_assert(sizeof(data) == 20, "");
1011f6b2477SPavel Labath   // The textual module id encoding should be between 33 and 40 bytes long,
1021f6b2477SPavel Labath   // depending on the size of the age field, which is of variable length.
1031f6b2477SPavel Labath   // The first three chunks of the id are encoded in big endian, so we need to
1041f6b2477SPavel Labath   // byte-swap those.
1051f6b2477SPavel Labath   if (str.size() < 33 || str.size() > 40)
1061f6b2477SPavel Labath     return UUID();
1071f6b2477SPavel Labath   uint32_t t;
1081f6b2477SPavel Labath   if (to_integer(consume_front(str, 8), t, 16))
1091f6b2477SPavel Labath     data.uuid1 = t;
1101f6b2477SPavel Labath   else
1111f6b2477SPavel Labath     return UUID();
1121f6b2477SPavel Labath   for (int i = 0; i < 2; ++i) {
1131f6b2477SPavel Labath     if (to_integer(consume_front(str, 4), t, 16))
1141f6b2477SPavel Labath       data.uuid2[i] = t;
1151f6b2477SPavel Labath     else
1161f6b2477SPavel Labath       return UUID();
1171f6b2477SPavel Labath   }
1181f6b2477SPavel Labath   for (int i = 0; i < 8; ++i) {
1191f6b2477SPavel Labath     if (!to_integer(consume_front(str, 2), data.uuid3[i], 16))
1201f6b2477SPavel Labath       return UUID();
1211f6b2477SPavel Labath   }
1221f6b2477SPavel Labath   if (to_integer(str, t, 16))
1231f6b2477SPavel Labath     data.age = t;
1241f6b2477SPavel Labath   else
1251f6b2477SPavel Labath     return UUID();
1261f6b2477SPavel Labath 
1271f6b2477SPavel Labath   // On non-windows, the age field should always be zero, so we don't include to
1281f6b2477SPavel Labath   // match the native uuid format of these platforms.
1291f6b2477SPavel Labath   return UUID::fromData(&data, os == llvm::Triple::Win32 ? 20 : 16);
1301f6b2477SPavel Labath }
1311f6b2477SPavel Labath 
1321f6b2477SPavel Labath llvm::Optional<Header> Header::parse(llvm::StringRef text) {
1331f6b2477SPavel Labath   // A valid module should start with something like:
1341f6b2477SPavel Labath   // MODULE Linux x86_64 E5894855C35DCCCCCCCCCCCCCCCCCCCC0 a.out
1351f6b2477SPavel Labath   // optionally followed by
1361f6b2477SPavel Labath   // INFO CODE_ID 554889E55DC3CCCCCCCCCCCCCCCCCCCC [a.exe]
1371f6b2477SPavel Labath   llvm::StringRef token, line;
1381f6b2477SPavel Labath   std::tie(line, text) = text.split('\n');
1391f6b2477SPavel Labath   std::tie(token, line) = getToken(line);
140*ed42ea47SPavel Labath   if (toToken(token) != Token::Module)
1411f6b2477SPavel Labath     return llvm::None;
1421f6b2477SPavel Labath 
1431f6b2477SPavel Labath   std::tie(token, line) = getToken(line);
1441f6b2477SPavel Labath   llvm::Triple triple;
1451f6b2477SPavel Labath   triple.setOS(toOS(token));
1461f6b2477SPavel Labath   if (triple.getOS() == llvm::Triple::UnknownOS)
1471f6b2477SPavel Labath     return llvm::None;
1481f6b2477SPavel Labath 
1491f6b2477SPavel Labath   std::tie(token, line) = getToken(line);
1501f6b2477SPavel Labath   triple.setArch(toArch(token));
1511f6b2477SPavel Labath   if (triple.getArch() == llvm::Triple::UnknownArch)
1521f6b2477SPavel Labath     return llvm::None;
1531f6b2477SPavel Labath 
1541f6b2477SPavel Labath   llvm::StringRef module_id;
1551f6b2477SPavel Labath   std::tie(module_id, line) = getToken(line);
1561f6b2477SPavel Labath 
1571f6b2477SPavel Labath   std::tie(line, text) = text.split('\n');
1581f6b2477SPavel Labath   std::tie(token, line) = getToken(line);
1591f6b2477SPavel Labath   if (token == "INFO") {
1601f6b2477SPavel Labath     std::tie(token, line) = getToken(line);
1611f6b2477SPavel Labath     if (token != "CODE_ID")
1621f6b2477SPavel Labath       return llvm::None;
1631f6b2477SPavel Labath 
1641f6b2477SPavel Labath     std::tie(token, line) = getToken(line);
1651f6b2477SPavel Labath     // If we don't have any text following the code id (e.g. on linux), we
1661f6b2477SPavel Labath     // should use the module id as UUID. Otherwise, we revert back to the module
1671f6b2477SPavel Labath     // id.
1681f6b2477SPavel Labath     if (line.trim().empty()) {
1691f6b2477SPavel Labath       UUID uuid;
1701f6b2477SPavel Labath       if (uuid.SetFromStringRef(token, token.size() / 2) != token.size())
1711f6b2477SPavel Labath         return llvm::None;
1721f6b2477SPavel Labath 
1731f6b2477SPavel Labath       return Header{ArchSpec(triple), uuid};
1741f6b2477SPavel Labath     }
1751f6b2477SPavel Labath   }
1761f6b2477SPavel Labath 
1771f6b2477SPavel Labath   // We reach here if we don't have a INFO CODE_ID section, or we chose not to
1781f6b2477SPavel Labath   // use it. In either case, we need to properly decode the module id, whose
1791f6b2477SPavel Labath   // fields are encoded in big-endian.
1801f6b2477SPavel Labath   UUID uuid = parseModuleId(triple.getOS(), module_id);
1811f6b2477SPavel Labath   if (!uuid)
1821f6b2477SPavel Labath     return llvm::None;
1831f6b2477SPavel Labath 
1841f6b2477SPavel Labath   return Header{ArchSpec(triple), uuid};
1851f6b2477SPavel Labath }
1861f6b2477SPavel Labath 
1871f6b2477SPavel Labath void ObjectFileBreakpad::Initialize() {
1881f6b2477SPavel Labath   PluginManager::RegisterPlugin(GetPluginNameStatic(),
1891f6b2477SPavel Labath                                 GetPluginDescriptionStatic(), CreateInstance,
190871f2b65SPavel Labath                                 CreateMemoryInstance, GetModuleSpecifications);
1911f6b2477SPavel Labath }
1921f6b2477SPavel Labath 
1931f6b2477SPavel Labath void ObjectFileBreakpad::Terminate() {
1941f6b2477SPavel Labath   PluginManager::UnregisterPlugin(CreateInstance);
1951f6b2477SPavel Labath }
1961f6b2477SPavel Labath 
1971f6b2477SPavel Labath ConstString ObjectFileBreakpad::GetPluginNameStatic() {
1981f6b2477SPavel Labath   static ConstString g_name("breakpad");
1991f6b2477SPavel Labath   return g_name;
2001f6b2477SPavel Labath }
2011f6b2477SPavel Labath 
2021f6b2477SPavel Labath ObjectFile *ObjectFileBreakpad::CreateInstance(
2031f6b2477SPavel Labath     const ModuleSP &module_sp, DataBufferSP &data_sp, offset_t data_offset,
2041f6b2477SPavel Labath     const FileSpec *file, offset_t file_offset, offset_t length) {
2051f6b2477SPavel Labath   if (!data_sp) {
2061f6b2477SPavel Labath     data_sp = MapFileData(*file, length, file_offset);
2071f6b2477SPavel Labath     if (!data_sp)
2081f6b2477SPavel Labath       return nullptr;
2091f6b2477SPavel Labath     data_offset = 0;
2101f6b2477SPavel Labath   }
2111f6b2477SPavel Labath   auto text = toStringRef(data_sp->GetData());
2121f6b2477SPavel Labath   llvm::Optional<Header> header = Header::parse(text);
2131f6b2477SPavel Labath   if (!header)
2141f6b2477SPavel Labath     return nullptr;
2151f6b2477SPavel Labath 
2161f6b2477SPavel Labath   // Update the data to contain the entire file if it doesn't already
2171f6b2477SPavel Labath   if (data_sp->GetByteSize() < length) {
2181f6b2477SPavel Labath     data_sp = MapFileData(*file, length, file_offset);
2191f6b2477SPavel Labath     if (!data_sp)
2201f6b2477SPavel Labath       return nullptr;
2211f6b2477SPavel Labath     data_offset = 0;
2221f6b2477SPavel Labath   }
2231f6b2477SPavel Labath 
2241f6b2477SPavel Labath   return new ObjectFileBreakpad(module_sp, data_sp, data_offset, file,
2251f6b2477SPavel Labath                                 file_offset, length, std::move(header->arch),
2261f6b2477SPavel Labath                                 std::move(header->uuid));
2271f6b2477SPavel Labath }
2281f6b2477SPavel Labath 
2291f6b2477SPavel Labath ObjectFile *ObjectFileBreakpad::CreateMemoryInstance(
2301f6b2477SPavel Labath     const ModuleSP &module_sp, DataBufferSP &data_sp,
2311f6b2477SPavel Labath     const ProcessSP &process_sp, addr_t header_addr) {
2321f6b2477SPavel Labath   return nullptr;
2331f6b2477SPavel Labath }
2341f6b2477SPavel Labath 
2351f6b2477SPavel Labath size_t ObjectFileBreakpad::GetModuleSpecifications(
2361f6b2477SPavel Labath     const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,
2371f6b2477SPavel Labath     offset_t file_offset, offset_t length, ModuleSpecList &specs) {
2381f6b2477SPavel Labath   auto text = toStringRef(data_sp->GetData());
2391f6b2477SPavel Labath   llvm::Optional<Header> header = Header::parse(text);
2401f6b2477SPavel Labath   if (!header)
2411f6b2477SPavel Labath     return 0;
2421f6b2477SPavel Labath   ModuleSpec spec(file, std::move(header->arch));
2431f6b2477SPavel Labath   spec.GetUUID() = std::move(header->uuid);
2441f6b2477SPavel Labath   specs.Append(spec);
2451f6b2477SPavel Labath   return 1;
2461f6b2477SPavel Labath }
2471f6b2477SPavel Labath 
2481f6b2477SPavel Labath ObjectFileBreakpad::ObjectFileBreakpad(const ModuleSP &module_sp,
2491f6b2477SPavel Labath                                        DataBufferSP &data_sp,
2501f6b2477SPavel Labath                                        offset_t data_offset,
2511f6b2477SPavel Labath                                        const FileSpec *file, offset_t offset,
2521f6b2477SPavel Labath                                        offset_t length, ArchSpec arch,
2531f6b2477SPavel Labath                                        UUID uuid)
2541f6b2477SPavel Labath     : ObjectFile(module_sp, file, offset, length, data_sp, data_offset),
2551f6b2477SPavel Labath       m_arch(std::move(arch)), m_uuid(std::move(uuid)) {}
2561f6b2477SPavel Labath 
2571f6b2477SPavel Labath bool ObjectFileBreakpad::ParseHeader() {
2581f6b2477SPavel Labath   // We already parsed the header during initialization.
2591f6b2477SPavel Labath   return true;
2601f6b2477SPavel Labath }
2611f6b2477SPavel Labath 
2621f6b2477SPavel Labath Symtab *ObjectFileBreakpad::GetSymtab() {
2631f6b2477SPavel Labath   // TODO
2641f6b2477SPavel Labath   return nullptr;
2651f6b2477SPavel Labath }
2661f6b2477SPavel Labath 
2671f6b2477SPavel Labath bool ObjectFileBreakpad::GetUUID(UUID *uuid) {
2681f6b2477SPavel Labath   *uuid = m_uuid;
2691f6b2477SPavel Labath   return true;
2701f6b2477SPavel Labath }
2711f6b2477SPavel Labath 
2721f6b2477SPavel Labath void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) {
273*ed42ea47SPavel Labath   if (m_sections_ap)
274*ed42ea47SPavel Labath     return;
275*ed42ea47SPavel Labath   m_sections_ap = llvm::make_unique<SectionList>();
276*ed42ea47SPavel Labath 
277*ed42ea47SPavel Labath   Token current_section = Token::Unknown;
278*ed42ea47SPavel Labath   offset_t section_start;
279*ed42ea47SPavel Labath   llvm::StringRef text = toStringRef(m_data.GetData());
280*ed42ea47SPavel Labath   uint32_t next_section_id = 1;
281*ed42ea47SPavel Labath   auto maybe_add_section = [&](const uint8_t *end_ptr) {
282*ed42ea47SPavel Labath     if (current_section == Token::Unknown)
283*ed42ea47SPavel Labath       return; // We have been called before parsing the first line.
284*ed42ea47SPavel Labath 
285*ed42ea47SPavel Labath     offset_t end_offset = end_ptr - m_data.GetDataStart();
286*ed42ea47SPavel Labath     auto section_sp = std::make_shared<Section>(
287*ed42ea47SPavel Labath         GetModule(), this, next_section_id++,
288*ed42ea47SPavel Labath         ConstString(toString(current_section)), eSectionTypeOther,
289*ed42ea47SPavel Labath         /*file_vm_addr*/ 0, /*vm_size*/ 0, section_start,
290*ed42ea47SPavel Labath         end_offset - section_start, /*log2align*/ 0, /*flags*/ 0);
291*ed42ea47SPavel Labath     m_sections_ap->AddSection(section_sp);
292*ed42ea47SPavel Labath     unified_section_list.AddSection(section_sp);
293*ed42ea47SPavel Labath   };
294*ed42ea47SPavel Labath   while (!text.empty()) {
295*ed42ea47SPavel Labath     llvm::StringRef line;
296*ed42ea47SPavel Labath     std::tie(line, text) = text.split('\n');
297*ed42ea47SPavel Labath 
298*ed42ea47SPavel Labath     Token token = toToken(getToken(line).first);
299*ed42ea47SPavel Labath     if (token == Token::Unknown) {
300*ed42ea47SPavel Labath       // We assume this is a line record, which logically belongs to the Func
301*ed42ea47SPavel Labath       // section. Errors will be handled when parsing the Func section.
302*ed42ea47SPavel Labath       token = Token::Func;
303*ed42ea47SPavel Labath     }
304*ed42ea47SPavel Labath     if (token == current_section)
305*ed42ea47SPavel Labath       continue;
306*ed42ea47SPavel Labath 
307*ed42ea47SPavel Labath     // Changing sections, finish off the previous one, if there was any.
308*ed42ea47SPavel Labath     maybe_add_section(line.bytes_begin());
309*ed42ea47SPavel Labath     // And start a new one.
310*ed42ea47SPavel Labath     current_section = token;
311*ed42ea47SPavel Labath     section_start = line.bytes_begin() - m_data.GetDataStart();
312*ed42ea47SPavel Labath   }
313*ed42ea47SPavel Labath   // Finally, add the last section.
314*ed42ea47SPavel Labath   maybe_add_section(m_data.GetDataEnd());
3151f6b2477SPavel Labath }
316