Path: blob/main/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
39648 views
//===-- SymbolFileBreakpad.cpp --------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h"9#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"10#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"11#include "lldb/Core/Module.h"12#include "lldb/Core/PluginManager.h"13#include "lldb/Core/Section.h"14#include "lldb/Host/FileSystem.h"15#include "lldb/Symbol/CompileUnit.h"16#include "lldb/Symbol/ObjectFile.h"17#include "lldb/Symbol/SymbolVendor.h"18#include "lldb/Symbol/TypeMap.h"19#include "lldb/Utility/LLDBLog.h"20#include "lldb/Utility/Log.h"21#include "lldb/Utility/StreamString.h"22#include "llvm/ADT/StringExtras.h"23#include <optional>2425using namespace lldb;26using namespace lldb_private;27using namespace lldb_private::breakpad;2829LLDB_PLUGIN_DEFINE(SymbolFileBreakpad)3031char SymbolFileBreakpad::ID;3233class SymbolFileBreakpad::LineIterator {34public:35// begin iterator for sections of given type36LineIterator(ObjectFile &obj, Record::Kind section_type)37: m_obj(&obj), m_section_type(toString(section_type)),38m_next_section_idx(0), m_next_line(llvm::StringRef::npos) {39++*this;40}4142// An iterator starting at the position given by the bookmark.43LineIterator(ObjectFile &obj, Record::Kind section_type, Bookmark bookmark);4445// end iterator46explicit LineIterator(ObjectFile &obj)47: m_obj(&obj),48m_next_section_idx(m_obj->GetSectionList()->GetNumSections(0)),49m_current_line(llvm::StringRef::npos),50m_next_line(llvm::StringRef::npos) {}5152friend bool operator!=(const LineIterator &lhs, const LineIterator &rhs) {53assert(lhs.m_obj == rhs.m_obj);54if (lhs.m_next_section_idx != rhs.m_next_section_idx)55return true;56if (lhs.m_current_line != rhs.m_current_line)57return true;58assert(lhs.m_next_line == rhs.m_next_line);59return false;60}6162const LineIterator &operator++();63llvm::StringRef operator*() const {64return m_section_text.slice(m_current_line, m_next_line);65}6667Bookmark GetBookmark() const {68return Bookmark{m_next_section_idx, m_current_line};69}7071private:72ObjectFile *m_obj;73ConstString m_section_type;74uint32_t m_next_section_idx;75llvm::StringRef m_section_text;76size_t m_current_line;77size_t m_next_line;7879void FindNextLine() {80m_next_line = m_section_text.find('\n', m_current_line);81if (m_next_line != llvm::StringRef::npos) {82++m_next_line;83if (m_next_line >= m_section_text.size())84m_next_line = llvm::StringRef::npos;85}86}87};8889SymbolFileBreakpad::LineIterator::LineIterator(ObjectFile &obj,90Record::Kind section_type,91Bookmark bookmark)92: m_obj(&obj), m_section_type(toString(section_type)),93m_next_section_idx(bookmark.section), m_current_line(bookmark.offset) {94Section § =95*obj.GetSectionList()->GetSectionAtIndex(m_next_section_idx - 1);96assert(sect.GetName() == m_section_type);9798DataExtractor data;99obj.ReadSectionData(§, data);100m_section_text = toStringRef(data.GetData());101102assert(m_current_line < m_section_text.size());103FindNextLine();104}105106const SymbolFileBreakpad::LineIterator &107SymbolFileBreakpad::LineIterator::operator++() {108const SectionList &list = *m_obj->GetSectionList();109size_t num_sections = list.GetNumSections(0);110while (m_next_line != llvm::StringRef::npos ||111m_next_section_idx < num_sections) {112if (m_next_line != llvm::StringRef::npos) {113m_current_line = m_next_line;114FindNextLine();115return *this;116}117118Section § = *list.GetSectionAtIndex(m_next_section_idx++);119if (sect.GetName() != m_section_type)120continue;121DataExtractor data;122m_obj->ReadSectionData(§, data);123m_section_text = toStringRef(data.GetData());124m_next_line = 0;125}126// We've reached the end.127m_current_line = m_next_line;128return *this;129}130131llvm::iterator_range<SymbolFileBreakpad::LineIterator>132SymbolFileBreakpad::lines(Record::Kind section_type) {133return llvm::make_range(LineIterator(*m_objfile_sp, section_type),134LineIterator(*m_objfile_sp));135}136137namespace {138// A helper class for constructing the list of support files for a given compile139// unit.140class SupportFileMap {141public:142// Given a breakpad file ID, return a file ID to be used in the support files143// for this compile unit.144size_t operator[](size_t file) {145return m_map.try_emplace(file, m_map.size() + 1).first->second;146}147148// Construct a FileSpecList containing only the support files relevant for149// this compile unit (in the correct order).150FileSpecList translate(const FileSpec &cu_spec,151llvm::ArrayRef<FileSpec> all_files);152153private:154llvm::DenseMap<size_t, size_t> m_map;155};156} // namespace157158FileSpecList SupportFileMap::translate(const FileSpec &cu_spec,159llvm::ArrayRef<FileSpec> all_files) {160std::vector<FileSpec> result;161result.resize(m_map.size() + 1);162result[0] = cu_spec;163for (const auto &KV : m_map) {164if (KV.first < all_files.size())165result[KV.second] = all_files[KV.first];166}167return FileSpecList(std::move(result));168}169170void SymbolFileBreakpad::Initialize() {171PluginManager::RegisterPlugin(GetPluginNameStatic(),172GetPluginDescriptionStatic(), CreateInstance,173DebuggerInitialize);174}175176void SymbolFileBreakpad::Terminate() {177PluginManager::UnregisterPlugin(CreateInstance);178}179180uint32_t SymbolFileBreakpad::CalculateAbilities() {181if (!m_objfile_sp || !llvm::isa<ObjectFileBreakpad>(*m_objfile_sp))182return 0;183184return CompileUnits | Functions | LineTables;185}186187uint32_t SymbolFileBreakpad::CalculateNumCompileUnits() {188ParseCUData();189return m_cu_data->GetSize();190}191192CompUnitSP SymbolFileBreakpad::ParseCompileUnitAtIndex(uint32_t index) {193if (index >= m_cu_data->GetSize())194return nullptr;195196CompUnitData &data = m_cu_data->GetEntryRef(index).data;197198ParseFileRecords();199200FileSpec spec;201202// The FileSpec of the compile unit will be the file corresponding to the203// first LINE record.204LineIterator It(*m_objfile_sp, Record::Func, data.bookmark),205End(*m_objfile_sp);206assert(Record::classify(*It) == Record::Func);207++It; // Skip FUNC record.208// Skip INLINE records.209while (It != End && Record::classify(*It) == Record::Inline)210++It;211212if (It != End) {213auto record = LineRecord::parse(*It);214if (record && record->FileNum < m_files->size())215spec = (*m_files)[record->FileNum];216}217218auto cu_sp = std::make_shared<CompileUnit>(219m_objfile_sp->GetModule(),220/*user_data*/ nullptr, std::make_shared<SupportFile>(spec), index,221eLanguageTypeUnknown,222/*is_optimized*/ eLazyBoolNo);223224SetCompileUnitAtIndex(index, cu_sp);225return cu_sp;226}227228FunctionSP SymbolFileBreakpad::GetOrCreateFunction(CompileUnit &comp_unit) {229user_id_t id = comp_unit.GetID();230if (FunctionSP func_sp = comp_unit.FindFunctionByUID(id))231return func_sp;232233Log *log = GetLog(LLDBLog::Symbols);234FunctionSP func_sp;235addr_t base = GetBaseFileAddress();236if (base == LLDB_INVALID_ADDRESS) {237LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping "238"symtab population.");239return func_sp;240}241242const SectionList *list = comp_unit.GetModule()->GetSectionList();243CompUnitData &data = m_cu_data->GetEntryRef(id).data;244LineIterator It(*m_objfile_sp, Record::Func, data.bookmark);245assert(Record::classify(*It) == Record::Func);246247if (auto record = FuncRecord::parse(*It)) {248Mangled func_name;249func_name.SetValue(ConstString(record->Name));250addr_t address = record->Address + base;251SectionSP section_sp = list->FindSectionContainingFileAddress(address);252if (section_sp) {253AddressRange func_range(254section_sp, address - section_sp->GetFileAddress(), record->Size);255// Use the CU's id because every CU has only one function inside.256func_sp = std::make_shared<Function>(&comp_unit, id, 0, func_name,257nullptr, func_range);258comp_unit.AddFunction(func_sp);259}260}261return func_sp;262}263264size_t SymbolFileBreakpad::ParseFunctions(CompileUnit &comp_unit) {265std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());266return GetOrCreateFunction(comp_unit) ? 1 : 0;267}268269bool SymbolFileBreakpad::ParseLineTable(CompileUnit &comp_unit) {270std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());271CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data;272273if (!data.line_table_up)274ParseLineTableAndSupportFiles(comp_unit, data);275276comp_unit.SetLineTable(data.line_table_up.release());277return true;278}279280bool SymbolFileBreakpad::ParseSupportFiles(CompileUnit &comp_unit,281SupportFileList &support_files) {282std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());283CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data;284if (!data.support_files)285ParseLineTableAndSupportFiles(comp_unit, data);286287for (auto &fs : *data.support_files)288support_files.Append(fs);289return true;290}291292size_t SymbolFileBreakpad::ParseBlocksRecursive(Function &func) {293std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());294CompileUnit *comp_unit = func.GetCompileUnit();295lldbassert(comp_unit);296ParseInlineOriginRecords();297// A vector of current each level's parent block. For example, when parsing298// "INLINE 0 ...", the current level is 0 and its parent block is the299// function block at index 0.300std::vector<Block *> blocks;301Block &block = func.GetBlock(false);302block.AddRange(Block::Range(0, func.GetAddressRange().GetByteSize()));303blocks.push_back(&block);304305size_t blocks_added = 0;306addr_t func_base = func.GetAddressRange().GetBaseAddress().GetOffset();307CompUnitData &data = m_cu_data->GetEntryRef(comp_unit->GetID()).data;308LineIterator It(*m_objfile_sp, Record::Func, data.bookmark),309End(*m_objfile_sp);310++It; // Skip the FUNC record.311size_t last_added_nest_level = 0;312while (It != End && Record::classify(*It) == Record::Inline) {313if (auto record = InlineRecord::parse(*It)) {314if (record->InlineNestLevel == 0 ||315record->InlineNestLevel <= last_added_nest_level + 1) {316last_added_nest_level = record->InlineNestLevel;317BlockSP block_sp = std::make_shared<Block>(It.GetBookmark().offset);318FileSpec callsite_file;319if (record->CallSiteFileNum < m_files->size())320callsite_file = (*m_files)[record->CallSiteFileNum];321llvm::StringRef name;322if (record->OriginNum < m_inline_origins->size())323name = (*m_inline_origins)[record->OriginNum];324325Declaration callsite(callsite_file, record->CallSiteLineNum);326block_sp->SetInlinedFunctionInfo(name.str().c_str(),327/*mangled=*/nullptr,328/*decl_ptr=*/nullptr, &callsite);329for (const auto &range : record->Ranges) {330block_sp->AddRange(331Block::Range(range.first - func_base, range.second));332}333block_sp->FinalizeRanges();334335blocks[record->InlineNestLevel]->AddChild(block_sp);336if (record->InlineNestLevel + 1 >= blocks.size()) {337blocks.resize(blocks.size() + 1);338}339blocks[record->InlineNestLevel + 1] = block_sp.get();340++blocks_added;341}342}343++It;344}345return blocks_added;346}347348void SymbolFileBreakpad::ParseInlineOriginRecords() {349if (m_inline_origins)350return;351m_inline_origins.emplace();352353Log *log = GetLog(LLDBLog::Symbols);354for (llvm::StringRef line : lines(Record::InlineOrigin)) {355auto record = InlineOriginRecord::parse(line);356if (!record) {357LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line);358continue;359}360361if (record->Number >= m_inline_origins->size())362m_inline_origins->resize(record->Number + 1);363(*m_inline_origins)[record->Number] = record->Name;364}365}366367uint32_t368SymbolFileBreakpad::ResolveSymbolContext(const Address &so_addr,369SymbolContextItem resolve_scope,370SymbolContext &sc) {371std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());372if (!(resolve_scope & (eSymbolContextCompUnit | eSymbolContextLineEntry |373eSymbolContextFunction | eSymbolContextBlock)))374return 0;375376ParseCUData();377uint32_t idx =378m_cu_data->FindEntryIndexThatContains(so_addr.GetFileAddress());379if (idx == UINT32_MAX)380return 0;381382sc.comp_unit = GetCompileUnitAtIndex(idx).get();383SymbolContextItem result = eSymbolContextCompUnit;384if (resolve_scope & eSymbolContextLineEntry) {385if (sc.comp_unit->GetLineTable()->FindLineEntryByAddress(so_addr,386sc.line_entry)) {387result |= eSymbolContextLineEntry;388}389}390391if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) {392FunctionSP func_sp = GetOrCreateFunction(*sc.comp_unit);393if (func_sp) {394sc.function = func_sp.get();395result |= eSymbolContextFunction;396if (resolve_scope & eSymbolContextBlock) {397Block &block = func_sp->GetBlock(true);398sc.block = block.FindInnermostBlockByOffset(399so_addr.GetFileAddress() -400sc.function->GetAddressRange().GetBaseAddress().GetFileAddress());401if (sc.block)402result |= eSymbolContextBlock;403}404}405}406407return result;408}409410uint32_t SymbolFileBreakpad::ResolveSymbolContext(411const SourceLocationSpec &src_location_spec,412lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) {413std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());414if (!(resolve_scope & eSymbolContextCompUnit))415return 0;416417uint32_t old_size = sc_list.GetSize();418for (size_t i = 0, size = GetNumCompileUnits(); i < size; ++i) {419CompileUnit &cu = *GetCompileUnitAtIndex(i);420cu.ResolveSymbolContext(src_location_spec, resolve_scope, sc_list);421}422return sc_list.GetSize() - old_size;423}424425void SymbolFileBreakpad::FindFunctions(426const Module::LookupInfo &lookup_info,427const CompilerDeclContext &parent_decl_ctx, bool include_inlines,428SymbolContextList &sc_list) {429std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());430// TODO: Implement this with supported FunctionNameType.431432ConstString name = lookup_info.GetLookupName();433for (uint32_t i = 0; i < GetNumCompileUnits(); ++i) {434CompUnitSP cu_sp = GetCompileUnitAtIndex(i);435FunctionSP func_sp = GetOrCreateFunction(*cu_sp);436if (func_sp && name == func_sp->GetNameNoArguments()) {437SymbolContext sc;438sc.comp_unit = cu_sp.get();439sc.function = func_sp.get();440sc.module_sp = func_sp->CalculateSymbolContextModule();441sc_list.Append(sc);442}443}444}445446void SymbolFileBreakpad::FindFunctions(const RegularExpression ®ex,447bool include_inlines,448SymbolContextList &sc_list) {449// TODO450}451452void SymbolFileBreakpad::AddSymbols(Symtab &symtab) {453Log *log = GetLog(LLDBLog::Symbols);454Module &module = *m_objfile_sp->GetModule();455addr_t base = GetBaseFileAddress();456if (base == LLDB_INVALID_ADDRESS) {457LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping "458"symtab population.");459return;460}461462const SectionList &list = *module.GetSectionList();463llvm::DenseSet<addr_t> found_symbol_addresses;464std::vector<Symbol> symbols;465auto add_symbol = [&](addr_t address, std::optional<addr_t> size,466llvm::StringRef name) {467address += base;468SectionSP section_sp = list.FindSectionContainingFileAddress(address);469if (!section_sp) {470LLDB_LOG(log,471"Ignoring symbol {0}, whose address ({1}) is outside of the "472"object file. Mismatched symbol file?",473name, address);474return;475}476// Keep track of what addresses were already added so far and only add477// the symbol with the first address.478if (!found_symbol_addresses.insert(address).second)479return;480symbols.emplace_back(481/*symID*/ 0, Mangled(name), eSymbolTypeCode,482/*is_global*/ true, /*is_debug*/ false,483/*is_trampoline*/ false, /*is_artificial*/ false,484AddressRange(section_sp, address - section_sp->GetFileAddress(),485size.value_or(0)),486size.has_value(), /*contains_linker_annotations*/ false, /*flags*/ 0);487};488489for (llvm::StringRef line : lines(Record::Public)) {490if (auto record = PublicRecord::parse(line))491add_symbol(record->Address, std::nullopt, record->Name);492else493LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line);494}495496for (Symbol &symbol : symbols)497symtab.AddSymbol(std::move(symbol));498symtab.Finalize();499}500501llvm::Expected<lldb::addr_t>502SymbolFileBreakpad::GetParameterStackSize(Symbol &symbol) {503ParseUnwindData();504if (auto *entry = m_unwind_data->win.FindEntryThatContains(505symbol.GetAddress().GetFileAddress())) {506auto record = StackWinRecord::parse(507*LineIterator(*m_objfile_sp, Record::StackWin, entry->data));508assert(record);509return record->ParameterSize;510}511return llvm::createStringError(llvm::inconvertibleErrorCode(),512"Parameter size unknown.");513}514515static std::optional<std::pair<llvm::StringRef, llvm::StringRef>>516GetRule(llvm::StringRef &unwind_rules) {517// Unwind rules are of the form518// register1: expression1 register2: expression2 ...519// We assume none of the tokens in expression<n> end with a colon.520521llvm::StringRef lhs, rest;522std::tie(lhs, rest) = getToken(unwind_rules);523if (!lhs.consume_back(":"))524return std::nullopt;525526// Seek forward to the next register: expression pair527llvm::StringRef::size_type pos = rest.find(": ");528if (pos == llvm::StringRef::npos) {529// No pair found, this means the rest of the string is a single expression.530unwind_rules = llvm::StringRef();531return std::make_pair(lhs, rest);532}533534// Go back one token to find the end of the current rule.535pos = rest.rfind(' ', pos);536if (pos == llvm::StringRef::npos)537return std::nullopt;538539llvm::StringRef rhs = rest.take_front(pos);540unwind_rules = rest.drop_front(pos);541return std::make_pair(lhs, rhs);542}543544static const RegisterInfo *545ResolveRegister(const llvm::Triple &triple,546const SymbolFile::RegisterInfoResolver &resolver,547llvm::StringRef name) {548if (triple.isX86() || triple.isMIPS()) {549// X86 and MIPS registers have '$' in front of their register names. Arm and550// AArch64 don't.551if (!name.consume_front("$"))552return nullptr;553}554return resolver.ResolveName(name);555}556557static const RegisterInfo *558ResolveRegisterOrRA(const llvm::Triple &triple,559const SymbolFile::RegisterInfoResolver &resolver,560llvm::StringRef name) {561if (name == ".ra")562return resolver.ResolveNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);563return ResolveRegister(triple, resolver, name);564}565566llvm::ArrayRef<uint8_t> SymbolFileBreakpad::SaveAsDWARF(postfix::Node &node) {567ArchSpec arch = m_objfile_sp->GetArchitecture();568StreamString dwarf(Stream::eBinary, arch.GetAddressByteSize(),569arch.GetByteOrder());570ToDWARF(node, dwarf);571uint8_t *saved = m_allocator.Allocate<uint8_t>(dwarf.GetSize());572std::memcpy(saved, dwarf.GetData(), dwarf.GetSize());573return {saved, dwarf.GetSize()};574}575576bool SymbolFileBreakpad::ParseCFIUnwindRow(llvm::StringRef unwind_rules,577const RegisterInfoResolver &resolver,578UnwindPlan::Row &row) {579Log *log = GetLog(LLDBLog::Symbols);580581llvm::BumpPtrAllocator node_alloc;582llvm::Triple triple = m_objfile_sp->GetArchitecture().GetTriple();583while (auto rule = GetRule(unwind_rules)) {584node_alloc.Reset();585llvm::StringRef lhs = rule->first;586postfix::Node *rhs = postfix::ParseOneExpression(rule->second, node_alloc);587if (!rhs) {588LLDB_LOG(log, "Could not parse `{0}` as unwind rhs.", rule->second);589return false;590}591592bool success = postfix::ResolveSymbols(593rhs, [&](postfix::SymbolNode &symbol) -> postfix::Node * {594llvm::StringRef name = symbol.GetName();595if (name == ".cfa" && lhs != ".cfa")596return postfix::MakeNode<postfix::InitialValueNode>(node_alloc);597598if (const RegisterInfo *info =599ResolveRegister(triple, resolver, name)) {600return postfix::MakeNode<postfix::RegisterNode>(601node_alloc, info->kinds[eRegisterKindLLDB]);602}603return nullptr;604});605606if (!success) {607LLDB_LOG(log, "Resolving symbols in `{0}` failed.", rule->second);608return false;609}610611llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(*rhs);612if (lhs == ".cfa") {613row.GetCFAValue().SetIsDWARFExpression(saved.data(), saved.size());614} else if (const RegisterInfo *info =615ResolveRegisterOrRA(triple, resolver, lhs)) {616UnwindPlan::Row::RegisterLocation loc;617loc.SetIsDWARFExpression(saved.data(), saved.size());618row.SetRegisterInfo(info->kinds[eRegisterKindLLDB], loc);619} else620LLDB_LOG(log, "Invalid register `{0}` in unwind rule.", lhs);621}622if (unwind_rules.empty())623return true;624625LLDB_LOG(log, "Could not parse `{0}` as an unwind rule.", unwind_rules);626return false;627}628629UnwindPlanSP630SymbolFileBreakpad::GetUnwindPlan(const Address &address,631const RegisterInfoResolver &resolver) {632ParseUnwindData();633if (auto *entry =634m_unwind_data->cfi.FindEntryThatContains(address.GetFileAddress()))635return ParseCFIUnwindPlan(entry->data, resolver);636if (auto *entry =637m_unwind_data->win.FindEntryThatContains(address.GetFileAddress()))638return ParseWinUnwindPlan(entry->data, resolver);639return nullptr;640}641642UnwindPlanSP643SymbolFileBreakpad::ParseCFIUnwindPlan(const Bookmark &bookmark,644const RegisterInfoResolver &resolver) {645addr_t base = GetBaseFileAddress();646if (base == LLDB_INVALID_ADDRESS)647return nullptr;648649LineIterator It(*m_objfile_sp, Record::StackCFI, bookmark),650End(*m_objfile_sp);651std::optional<StackCFIRecord> init_record = StackCFIRecord::parse(*It);652assert(init_record && init_record->Size &&653"Record already parsed successfully in ParseUnwindData!");654655auto plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindLLDB);656plan_sp->SetSourceName("breakpad STACK CFI");657plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);658plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo);659plan_sp->SetSourcedFromCompiler(eLazyBoolYes);660plan_sp->SetPlanValidAddressRange(661AddressRange(base + init_record->Address, *init_record->Size,662m_objfile_sp->GetModule()->GetSectionList()));663664auto row_sp = std::make_shared<UnwindPlan::Row>();665row_sp->SetOffset(0);666if (!ParseCFIUnwindRow(init_record->UnwindRules, resolver, *row_sp))667return nullptr;668plan_sp->AppendRow(row_sp);669for (++It; It != End; ++It) {670std::optional<StackCFIRecord> record = StackCFIRecord::parse(*It);671if (!record)672return nullptr;673if (record->Size)674break;675676row_sp = std::make_shared<UnwindPlan::Row>(*row_sp);677row_sp->SetOffset(record->Address - init_record->Address);678if (!ParseCFIUnwindRow(record->UnwindRules, resolver, *row_sp))679return nullptr;680plan_sp->AppendRow(row_sp);681}682return plan_sp;683}684685UnwindPlanSP686SymbolFileBreakpad::ParseWinUnwindPlan(const Bookmark &bookmark,687const RegisterInfoResolver &resolver) {688Log *log = GetLog(LLDBLog::Symbols);689addr_t base = GetBaseFileAddress();690if (base == LLDB_INVALID_ADDRESS)691return nullptr;692693LineIterator It(*m_objfile_sp, Record::StackWin, bookmark);694std::optional<StackWinRecord> record = StackWinRecord::parse(*It);695assert(record && "Record already parsed successfully in ParseUnwindData!");696697auto plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindLLDB);698plan_sp->SetSourceName("breakpad STACK WIN");699plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);700plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo);701plan_sp->SetSourcedFromCompiler(eLazyBoolYes);702plan_sp->SetPlanValidAddressRange(703AddressRange(base + record->RVA, record->CodeSize,704m_objfile_sp->GetModule()->GetSectionList()));705706auto row_sp = std::make_shared<UnwindPlan::Row>();707row_sp->SetOffset(0);708709llvm::BumpPtrAllocator node_alloc;710std::vector<std::pair<llvm::StringRef, postfix::Node *>> program =711postfix::ParseFPOProgram(record->ProgramString, node_alloc);712713if (program.empty()) {714LLDB_LOG(log, "Invalid unwind rule: {0}.", record->ProgramString);715return nullptr;716}717auto it = program.begin();718llvm::Triple triple = m_objfile_sp->GetArchitecture().GetTriple();719const auto &symbol_resolver =720[&](postfix::SymbolNode &symbol) -> postfix::Node * {721llvm::StringRef name = symbol.GetName();722for (const auto &rule : llvm::make_range(program.begin(), it)) {723if (rule.first == name)724return rule.second;725}726if (const RegisterInfo *info = ResolveRegister(triple, resolver, name))727return postfix::MakeNode<postfix::RegisterNode>(728node_alloc, info->kinds[eRegisterKindLLDB]);729return nullptr;730};731732// We assume the first value will be the CFA. It is usually called T0, but733// clang will use T1, if it needs to realign the stack.734auto *symbol = llvm::dyn_cast<postfix::SymbolNode>(it->second);735if (symbol && symbol->GetName() == ".raSearch") {736row_sp->GetCFAValue().SetRaSearch(record->LocalSize +737record->SavedRegisterSize);738} else {739if (!postfix::ResolveSymbols(it->second, symbol_resolver)) {740LLDB_LOG(log, "Resolving symbols in `{0}` failed.",741record->ProgramString);742return nullptr;743}744llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(*it->second);745row_sp->GetCFAValue().SetIsDWARFExpression(saved.data(), saved.size());746}747748// Replace the node value with InitialValueNode, so that subsequent749// expressions refer to the CFA value instead of recomputing the whole750// expression.751it->second = postfix::MakeNode<postfix::InitialValueNode>(node_alloc);752753754// Now process the rest of the assignments.755for (++it; it != program.end(); ++it) {756const RegisterInfo *info = ResolveRegister(triple, resolver, it->first);757// It is not an error if the resolution fails because the program may758// contain temporary variables.759if (!info)760continue;761if (!postfix::ResolveSymbols(it->second, symbol_resolver)) {762LLDB_LOG(log, "Resolving symbols in `{0}` failed.",763record->ProgramString);764return nullptr;765}766767llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(*it->second);768UnwindPlan::Row::RegisterLocation loc;769loc.SetIsDWARFExpression(saved.data(), saved.size());770row_sp->SetRegisterInfo(info->kinds[eRegisterKindLLDB], loc);771}772773plan_sp->AppendRow(row_sp);774return plan_sp;775}776777addr_t SymbolFileBreakpad::GetBaseFileAddress() {778return m_objfile_sp->GetModule()779->GetObjectFile()780->GetBaseAddress()781.GetFileAddress();782}783784// Parse out all the FILE records from the breakpad file. These will be needed785// when constructing the support file lists for individual compile units.786void SymbolFileBreakpad::ParseFileRecords() {787if (m_files)788return;789m_files.emplace();790791Log *log = GetLog(LLDBLog::Symbols);792for (llvm::StringRef line : lines(Record::File)) {793auto record = FileRecord::parse(line);794if (!record) {795LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line);796continue;797}798799if (record->Number >= m_files->size())800m_files->resize(record->Number + 1);801FileSpec::Style style = FileSpec::GuessPathStyle(record->Name)802.value_or(FileSpec::Style::native);803(*m_files)[record->Number] = FileSpec(record->Name, style);804}805}806807void SymbolFileBreakpad::ParseCUData() {808if (m_cu_data)809return;810811m_cu_data.emplace();812Log *log = GetLog(LLDBLog::Symbols);813addr_t base = GetBaseFileAddress();814if (base == LLDB_INVALID_ADDRESS) {815LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address "816"of object file.");817}818819// We shall create one compile unit for each FUNC record. So, count the number820// of FUNC records, and store them in m_cu_data, together with their ranges.821for (LineIterator It(*m_objfile_sp, Record::Func), End(*m_objfile_sp);822It != End; ++It) {823if (auto record = FuncRecord::parse(*It)) {824m_cu_data->Append(CompUnitMap::Entry(base + record->Address, record->Size,825CompUnitData(It.GetBookmark())));826} else827LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It);828}829m_cu_data->Sort();830}831832// Construct the list of support files and line table entries for the given833// compile unit.834void SymbolFileBreakpad::ParseLineTableAndSupportFiles(CompileUnit &cu,835CompUnitData &data) {836addr_t base = GetBaseFileAddress();837assert(base != LLDB_INVALID_ADDRESS &&838"How did we create compile units without a base address?");839840SupportFileMap map;841std::vector<std::unique_ptr<LineSequence>> sequences;842std::unique_ptr<LineSequence> line_seq_up =843LineTable::CreateLineSequenceContainer();844std::optional<addr_t> next_addr;845auto finish_sequence = [&]() {846LineTable::AppendLineEntryToSequence(847line_seq_up.get(), *next_addr, /*line=*/0, /*column=*/0,848/*file_idx=*/0, /*is_start_of_statement=*/false,849/*is_start_of_basic_block=*/false, /*is_prologue_end=*/false,850/*is_epilogue_begin=*/false, /*is_terminal_entry=*/true);851sequences.push_back(std::move(line_seq_up));852line_seq_up = LineTable::CreateLineSequenceContainer();853};854855LineIterator It(*m_objfile_sp, Record::Func, data.bookmark),856End(*m_objfile_sp);857assert(Record::classify(*It) == Record::Func);858for (++It; It != End; ++It) {859// Skip INLINE records860if (Record::classify(*It) == Record::Inline)861continue;862863auto record = LineRecord::parse(*It);864if (!record)865break;866867record->Address += base;868869if (next_addr && *next_addr != record->Address) {870// Discontiguous entries. Finish off the previous sequence and reset.871finish_sequence();872}873LineTable::AppendLineEntryToSequence(874line_seq_up.get(), record->Address, record->LineNum, /*column=*/0,875map[record->FileNum], /*is_start_of_statement=*/true,876/*is_start_of_basic_block=*/false, /*is_prologue_end=*/false,877/*is_epilogue_begin=*/false, /*is_terminal_entry=*/false);878next_addr = record->Address + record->Size;879}880if (next_addr)881finish_sequence();882data.line_table_up = std::make_unique<LineTable>(&cu, std::move(sequences));883data.support_files = map.translate(cu.GetPrimaryFile(), *m_files);884}885886void SymbolFileBreakpad::ParseUnwindData() {887if (m_unwind_data)888return;889m_unwind_data.emplace();890891Log *log = GetLog(LLDBLog::Symbols);892addr_t base = GetBaseFileAddress();893if (base == LLDB_INVALID_ADDRESS) {894LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address "895"of object file.");896}897898for (LineIterator It(*m_objfile_sp, Record::StackCFI), End(*m_objfile_sp);899It != End; ++It) {900if (auto record = StackCFIRecord::parse(*It)) {901if (record->Size)902m_unwind_data->cfi.Append(UnwindMap::Entry(903base + record->Address, *record->Size, It.GetBookmark()));904} else905LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It);906}907m_unwind_data->cfi.Sort();908909for (LineIterator It(*m_objfile_sp, Record::StackWin), End(*m_objfile_sp);910It != End; ++It) {911if (auto record = StackWinRecord::parse(*It)) {912m_unwind_data->win.Append(UnwindMap::Entry(913base + record->RVA, record->CodeSize, It.GetBookmark()));914} else915LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It);916}917m_unwind_data->win.Sort();918}919920uint64_t SymbolFileBreakpad::GetDebugInfoSize(bool load_all_debug_info) {921// Breakpad files are all debug info.922return m_objfile_sp->GetByteSize();923}924925926