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