Index: lit/SymbolFile/Breakpad/Inputs/basic-elf.yaml =================================================================== --- lit/SymbolFile/Breakpad/Inputs/basic-elf.yaml +++ lit/SymbolFile/Breakpad/Inputs/basic-elf.yaml @@ -0,0 +1,33 @@ +# A very basic ELF file to serve as a recipient of breakpad info + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 + Entry: 0x00000000004000D0 +Sections: + - Name: .text1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x0000000000400000 + AddressAlign: 0x0000000000001000 + Size: 0xb0 + - Name: .text2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x00000000004000B0 + AddressAlign: 0x0000000000000010 + Size: 0x42 +Symbols: +DynamicSymbols: +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x400000 + Align: 0x1000 + Sections: + - Section: .text1 + - Section: .text2 +... Index: lit/SymbolFile/Breakpad/Inputs/symtab.syms =================================================================== --- lit/SymbolFile/Breakpad/Inputs/symtab.syms +++ lit/SymbolFile/Breakpad/Inputs/symtab.syms @@ -0,0 +1,7 @@ +MODULE Linux x86_64 761550E08086333960A9074A9CE2895C0 a.out +INFO CODE_ID E05015768680393360A9074A9CE2895C +FILE 0 /tmp/a.c +PUBLIC b0 0 f1 +PUBLIC m c0 0 f2 +PUBLIC d0 0 _start +PUBLIC ff 0 _out_of_range_ignored Index: lit/SymbolFile/Breakpad/symtab.test =================================================================== --- lit/SymbolFile/Breakpad/symtab.test +++ lit/SymbolFile/Breakpad/symtab.test @@ -0,0 +1,23 @@ +# RUN: yaml2obj %S/Inputs/basic-elf.yaml > %T/symtab.out +# RUN: %lldb %T/symtab.out -o "target symbols add -s symtab.out %S/Inputs/symtab.syms" \ +# RUN: -s %s | FileCheck %s + +# CHECK-LABEL: (lldb) image dump symtab symtab.out +# CHECK: Symtab, file = {{.*}}symtab.out, num_symbols = 3: +# CHECK: Index UserID DSX Type File Address/Value Load Address Size Flags Name +# CHECK: [ 0] 0 X Code 0x00000000004000b0 0x0000000000000010 0x00000000 f1 +# CHECK: [ 1] 0 X Code 0x00000000004000c0 0x0000000000000010 0x00000000 f2 +# CHECK: [ 2] 0 X Code 0x00000000004000d0 0x0000000000000022 0x00000000 _start + +# CHECK-LABEL: (lldb) image lookup -a 0x4000b0 -v +# CHECK: Address: symtab.out[0x00000000004000b0] (symtab.out.PT_LOAD[0]..text2 + 0) +# CHECK: Symbol: id = {0x00000000}, range = [0x00000000004000b0-0x00000000004000c0), name="f1" + +# CHECK-LABEL: (lldb) image lookup -n f2 -v +# CHECK: Address: symtab.out[0x00000000004000c0] (symtab.out.PT_LOAD[0]..text2 + 16) +# CHECK: Symbol: id = {0x00000000}, range = [0x00000000004000c0-0x00000000004000d0), name="f2" + +image dump symtab symtab.out +image lookup -a 0x4000b0 -v +image lookup -n f2 -v +exit Index: source/API/SystemInitializerFull.cpp =================================================================== --- source/API/SystemInitializerFull.cpp +++ source/API/SystemInitializerFull.cpp @@ -83,6 +83,7 @@ #include "Plugins/Process/mach-core/ProcessMachCore.h" #include "Plugins/Process/minidump/ProcessMinidump.h" #include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h" +#include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h" #include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" @@ -343,6 +344,7 @@ MainThreadCheckerRuntime::Initialize(); SymbolVendorELF::Initialize(); + breakpad::SymbolFileBreakpad::Initialize(); SymbolFileDWARF::Initialize(); SymbolFilePDB::Initialize(); SymbolFileSymtab::Initialize(); @@ -471,6 +473,7 @@ UndefinedBehaviorSanitizerRuntime::Terminate(); MainThreadCheckerRuntime::Terminate(); SymbolVendorELF::Terminate(); + breakpad::SymbolFileBreakpad::Terminate(); SymbolFileDWARF::Terminate(); SymbolFilePDB::Terminate(); SymbolFileSymtab::Terminate(); Index: source/Plugins/SymbolFile/Breakpad/CMakeLists.txt =================================================================== --- source/Plugins/SymbolFile/Breakpad/CMakeLists.txt +++ source/Plugins/SymbolFile/Breakpad/CMakeLists.txt @@ -0,0 +1,12 @@ +add_lldb_library(lldbPluginSymbolFileBreakpad PLUGIN + SymbolFileBreakpad.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbUtility + lldbPluginObjectFileBreakpad + LINK_COMPONENTS + Support + ) Index: source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h =================================================================== --- source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h +++ source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h @@ -0,0 +1,149 @@ +//===-- SymbolFileBreakpad.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILE_BREAKPAD_SYMBOLFILEBREAKPAD_H +#define LLDB_PLUGINS_SYMBOLFILE_BREAKPAD_SYMBOLFILEBREAKPAD_H + +#include "lldb/Symbol/SymbolFile.h" + +namespace lldb_private { + +namespace breakpad { + +class SymbolFileBreakpad : public SymbolFile { +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void Initialize(); + static void Terminate(); + static void DebuggerInitialize(Debugger &debugger) {} + static ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic() { + return "Breakpad debug symbol file reader."; + } + + static SymbolFile *CreateInstance(ObjectFile *obj_file) { + return new SymbolFileBreakpad(obj_file); + } + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileBreakpad(ObjectFile *object_file) : SymbolFile(object_file) {} + + ~SymbolFileBreakpad() override {} + + uint32_t CalculateAbilities() override; + + void InitializeObject() override {} + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + + uint32_t GetNumCompileUnits() override; + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + lldb::LanguageType + ParseCompileUnitLanguage(const SymbolContext &sc) override { + return lldb::eLanguageTypeUnknown; + } + + size_t ParseCompileUnitFunctions(const SymbolContext &sc) override; + + bool ParseCompileUnitLineTable(const SymbolContext &sc) override; + + bool ParseCompileUnitDebugMacros(const SymbolContext &sc) override { + return false; + } + + bool ParseCompileUnitSupportFiles(const SymbolContext &sc, + FileSpecList &support_files) override { + return false; + } + + bool + ParseImportedModules(const SymbolContext &sc, + std::vector &imported_modules) override { + return false; + } + + size_t ParseFunctionBlocks(const SymbolContext &sc) override { return 0; } + + uint32_t FindGlobalVariables(const ConstString &name, + const CompilerDeclContext *parent_decl_ctx, + uint32_t max_matches, + VariableList &variables) override { + return 0; + } + + size_t ParseTypesForCompileUnit(CompileUnit &cu) override { return 0; } + size_t ParseVariablesForContext(const SymbolContext &sc) override { + return 0; + } + Type *ResolveTypeUID(lldb::user_id_t type_uid) override { return nullptr; } + llvm::Optional GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, + const lldb_private::ExecutionContext *exe_ctx) override { + return llvm::None; + } + + bool CompleteType(CompilerType &compiler_type) override { return false; } + uint32_t ResolveSymbolContext(const Address &so_addr, + lldb::SymbolContextItem resolve_scope, + SymbolContext &sc) override; + + size_t GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask, + TypeList &type_list) override { + return 0; + } + + uint32_t FindFunctions(const ConstString &name, + const CompilerDeclContext *parent_decl_ctx, + lldb::FunctionNameType name_type_mask, + bool include_inlines, bool append, + SymbolContextList &sc_list) override; + + uint32_t FindFunctions(const RegularExpression ®ex, bool include_inlines, + bool append, SymbolContextList &sc_list) override; + + uint32_t FindTypes(const SymbolContext &sc, const ConstString &name, + const CompilerDeclContext *parent_decl_ctx, bool append, + uint32_t max_matches, + llvm::DenseSet &searched_symbol_files, + TypeMap &types) override; + + size_t FindTypes(const std::vector &context, bool append, + TypeMap &types) override; + + TypeSystem *GetTypeSystemForLanguage(lldb::LanguageType language) override { + return nullptr; + } + + CompilerDeclContext + FindNamespace(const SymbolContext &sc, const ConstString &name, + const CompilerDeclContext *parent_decl_ctx) override { + return CompilerDeclContext(); + } + + void AddSymbols(Symtab &symtab) override; + + ConstString GetPluginName() override { return GetPluginNameStatic(); } + uint32_t GetPluginVersion() override { return 1; } + +private: +}; + +} // namespace breakpad +} // namespace lldb_private + +#endif Index: source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp =================================================================== --- source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp +++ source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp @@ -0,0 +1,224 @@ +//===-- SymbolFileBreakpad.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h" +#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/TypeMap.h" +#include "lldb/Utility/Log.h" +#include "llvm/ADT/StringExtras.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::breakpad; + +namespace { +class LineIterator { +public: + // begin iterator for sections of given type + LineIterator(ObjectFile &obj, ConstString section_type) + : m_obj(&obj), m_section_type(section_type), m_next_section_idx(0) { + ++*this; + } + + // end iterator + explicit LineIterator(ObjectFile &obj) + : m_obj(&obj), + m_next_section_idx(m_obj->GetSectionList()->GetNumSections(0)) {} + + friend bool operator!=(const LineIterator &lhs, const LineIterator &rhs) { + assert(lhs.m_obj == rhs.m_obj); + if (lhs.m_next_section_idx != rhs.m_next_section_idx) + return true; + if (lhs.m_next_text.data() != rhs.m_next_text.data()) + return true; + assert(lhs.m_current_text == rhs.m_current_text); + assert(rhs.m_next_text == rhs.m_next_text); + return false; + } + + const LineIterator &operator++(); + llvm::StringRef operator*() const { return m_current_text; } + +private: + ObjectFile *m_obj; + ConstString m_section_type; + uint32_t m_next_section_idx; + llvm::StringRef m_current_text; + llvm::StringRef m_next_text; +}; +} // namespace + +const LineIterator &LineIterator::operator++() { + const SectionList &list = *m_obj->GetSectionList(); + size_t num_sections = list.GetNumSections(0); + while (m_next_text.empty() && m_next_section_idx < num_sections) { + Section § = *list.GetSectionAtIndex(m_next_section_idx++); + if (sect.GetName() != m_section_type) + continue; + DataExtractor data; + m_obj->ReadSectionData(§, data); + m_next_text = + llvm::StringRef(reinterpret_cast(data.GetDataStart()), + data.GetByteSize()); + } + std::tie(m_current_text, m_next_text) = m_next_text.split('\n'); + return *this; +} + +static llvm::iterator_range lines(ObjectFile &obj, + ConstString section_type) { + return llvm::make_range(LineIterator(obj, section_type), LineIterator(obj)); +} + +void SymbolFileBreakpad::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); +} + +void SymbolFileBreakpad::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ConstString SymbolFileBreakpad::GetPluginNameStatic() { + static ConstString g_name("breakpad"); + return g_name; +} + +uint32_t SymbolFileBreakpad::CalculateAbilities() { + if (!m_obj_file) + return 0; + if (m_obj_file->GetPluginName() != ObjectFileBreakpad::GetPluginNameStatic()) + return 0; + + return CompileUnits | Functions; +} + +uint32_t SymbolFileBreakpad::GetNumCompileUnits() { + // TODO + return 0; +} + +CompUnitSP SymbolFileBreakpad::ParseCompileUnitAtIndex(uint32_t index) { + // TODO + return nullptr; +} + +size_t SymbolFileBreakpad::ParseCompileUnitFunctions(const SymbolContext &sc) { + // TODO + return 0; +} + +bool SymbolFileBreakpad::ParseCompileUnitLineTable(const SymbolContext &sc) { + // TODO + return 0; +} + +uint32_t +SymbolFileBreakpad::ResolveSymbolContext(const Address &so_addr, + SymbolContextItem resolve_scope, + SymbolContext &sc) { + // TODO + return 0; +} + +uint32_t SymbolFileBreakpad::FindFunctions( + const ConstString &name, const CompilerDeclContext *parent_decl_ctx, + FunctionNameType name_type_mask, bool include_inlines, bool append, + SymbolContextList &sc_list) { + // TODO + if (!append) + sc_list.Clear(); + return sc_list.GetSize(); +} + +uint32_t SymbolFileBreakpad::FindFunctions(const RegularExpression ®ex, + bool include_inlines, bool append, + SymbolContextList &sc_list) { + // TODO + if (!append) + sc_list.Clear(); + return sc_list.GetSize(); +} + +uint32_t SymbolFileBreakpad::FindTypes( + const SymbolContext &sc, const ConstString &name, + const CompilerDeclContext *parent_decl_ctx, bool append, + uint32_t max_matches, llvm::DenseSet &searched_symbol_files, + TypeMap &types) { + if (!append) + types.Clear(); + return types.GetSize(); +} + +size_t +SymbolFileBreakpad::FindTypes(const std::vector &context, + bool append, TypeMap &types) { + if (!append) + types.Clear(); + return types.GetSize(); +} + +void SymbolFileBreakpad::AddSymbols(Symtab &symtab) { + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); + Module &module = *m_obj_file->GetModule(); + addr_t base = module.GetObjectFile()->GetBaseAddress().GetFileAddress(); + if (base == LLDB_INVALID_ADDRESS) { + LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping " + "symtab population."); + return; + } + + const SectionList &list = *module.GetSectionList(); + for (llvm::StringRef line : lines(*m_obj_file, ConstString("PUBLIC"))) { + // PUBLIC [m] address param_size name + // skip PUBLIC keyword + line = getToken(line).second; + llvm::StringRef token; + std::tie(token, line) = getToken(line); + if (token == "m") + std::tie(token, line) = getToken(line); + + addr_t address; + if (!to_integer(token, address, 16)) + continue; + address += base; + + // skip param_size + line = getToken(line).second; + + llvm::StringRef name = line.trim(); + + SectionSP section_sp = list.FindSectionContainingFileAddress(address); + if (!section_sp) { + LLDB_LOG(log, + "Ignoring symbol {0}, whose address ({1}) is outside of the " + "object file. Mismatched symbol file?", + name, address); + continue; + } + + symtab.AddSymbol(Symbol( + /*symID*/ 0, Mangled(name, /*is_mangled*/ false), eSymbolTypeCode, + /*is_global*/ true, /*is_debug*/ false, /*is_trampoline*/ false, + /*is_artificial*/ false, + AddressRange(section_sp, address - section_sp->GetFileAddress(), 0), + /*size_is_valid*/ 0, /*contains_linker_annotations*/ false, + /*flags*/ 0)); + } + + // TODO: Process FUNC records as well. + + symtab.CalculateSymbolSizes(); +} Index: source/Plugins/SymbolFile/CMakeLists.txt =================================================================== --- source/Plugins/SymbolFile/CMakeLists.txt +++ source/Plugins/SymbolFile/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(Breakpad) add_subdirectory(DWARF) add_subdirectory(Symtab) add_subdirectory(NativePDB) Index: source/Symbol/SymbolVendor.cpp =================================================================== --- source/Symbol/SymbolVendor.cpp +++ source/Symbol/SymbolVendor.cpp @@ -377,6 +377,7 @@ s->Indent(); s->PutCString("SymbolVendor"); if (m_sym_file_ap.get()) { + *s << " " << m_sym_file_ap->GetPluginName(); ObjectFile *objfile = m_sym_file_ap->GetObjectFile(); if (objfile) { const FileSpec &objfile_file_spec = objfile->GetFileSpec(); @@ -401,6 +402,9 @@ (*cu_pos)->Dump(s, show_context); } + if (Symtab *symtab = GetSymtab()) + symtab->Dump(s, nullptr, eSortOrderNone); + s->IndentLess(); } } Index: tools/lldb-test/SystemInitializerTest.cpp =================================================================== --- tools/lldb-test/SystemInitializerTest.cpp +++ tools/lldb-test/SystemInitializerTest.cpp @@ -70,6 +70,7 @@ #include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" #include "Plugins/Process/minidump/ProcessMinidump.h" #include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h" +#include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h" #include "Plugins/SymbolFile/PDB/SymbolFilePDB.h" @@ -176,6 +177,7 @@ MainThreadCheckerRuntime::Initialize(); SymbolVendorELF::Initialize(); + breakpad::SymbolFileBreakpad::Initialize(); SymbolFileDWARF::Initialize(); SymbolFilePDB::Initialize(); SymbolFileSymtab::Initialize(); @@ -274,6 +276,7 @@ UndefinedBehaviorSanitizerRuntime::Terminate(); MainThreadCheckerRuntime::Terminate(); SymbolVendorELF::Terminate(); + breakpad::SymbolFileBreakpad::Terminate(); SymbolFileDWARF::Terminate(); SymbolFilePDB::Terminate(); SymbolFileSymtab::Terminate(); Index: tools/lldb-test/lldb-test.cpp =================================================================== --- tools/lldb-test/lldb-test.cpp +++ tools/lldb-test/lldb-test.cpp @@ -100,10 +100,14 @@ } // namespace object namespace symbols { -static cl::list InputFilenames(cl::Positional, - cl::desc(""), - cl::OneOrMore, - cl::sub(SymbolsSubcommand)); +static cl::opt InputFile(cl::Positional, cl::desc(""), + cl::Required, cl::sub(SymbolsSubcommand)); + +static cl::opt + SymbolPath("symbol-file", + cl::desc("The file from which to fetch symbol information."), + cl::value_desc("file"), cl::sub(SymbolsSubcommand)); + enum class FindType { None, Function, @@ -692,28 +696,24 @@ } auto Action = *ActionOr; - int HadErrors = 0; - for (const auto &File : InputFilenames) { - outs() << "Module: " << File << "\n"; - ModuleSpec Spec{FileSpec(File)}; - Spec.GetSymbolFileSpec().SetFile(File, FileSpec::Style::native); - - auto ModulePtr = std::make_shared(Spec); - SymbolVendor *Vendor = ModulePtr->GetSymbolVendor(); - if (!Vendor) { - WithColor::error() << "Module has no symbol vendor.\n"; - HadErrors = 1; - continue; - } - - if (Error E = Action(*ModulePtr)) { - WithColor::error() << toString(std::move(E)) << "\n"; - HadErrors = 1; - } + outs() << "Module: " << InputFile << "\n"; + ModuleSpec Spec{FileSpec(InputFile)}; + StringRef Symbols = SymbolPath.empty() ? InputFile : SymbolPath; + Spec.GetSymbolFileSpec().SetFile(Symbols, FileSpec::Style::native); + + auto ModulePtr = std::make_shared(Spec); + SymbolVendor *Vendor = ModulePtr->GetSymbolVendor(); + if (!Vendor) { + WithColor::error() << "Module has no symbol vendor.\n"; + return 1; + } - outs().flush(); + if (Error E = Action(*ModulePtr)) { + WithColor::error() << toString(std::move(E)) << "\n"; + return 1; } - return HadErrors; + + return 0; } static void dumpSectionList(LinePrinter &Printer, const SectionList &List, bool is_subsection) {