1 //===-- SymbolFileBreakpad.cpp ----------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h" 10 #include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h" 11 #include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h" 12 #include "lldb/Core/Module.h" 13 #include "lldb/Core/PluginManager.h" 14 #include "lldb/Core/Section.h" 15 #include "lldb/Host/FileSystem.h" 16 #include "lldb/Symbol/CompileUnit.h" 17 #include "lldb/Symbol/ObjectFile.h" 18 #include "lldb/Symbol/SymbolVendor.h" 19 #include "lldb/Symbol/TypeMap.h" 20 #include "lldb/Utility/Log.h" 21 #include "llvm/ADT/StringExtras.h" 22 23 using namespace lldb; 24 using namespace lldb_private; 25 using namespace lldb_private::breakpad; 26 27 class SymbolFileBreakpad::LineIterator { 28 public: 29 // begin iterator for sections of given type 30 LineIterator(ObjectFile &obj, Record::Kind section_type) 31 : m_obj(&obj), m_section_type(toString(section_type)), 32 m_next_section_idx(0), m_next_line(llvm::StringRef::npos) { 33 ++*this; 34 } 35 36 // An iterator starting at the position given by the bookmark. 37 LineIterator(ObjectFile &obj, Record::Kind section_type, Bookmark bookmark); 38 39 // end iterator 40 explicit LineIterator(ObjectFile &obj) 41 : m_obj(&obj), 42 m_next_section_idx(m_obj->GetSectionList()->GetNumSections(0)), 43 m_current_line(llvm::StringRef::npos), 44 m_next_line(llvm::StringRef::npos) {} 45 46 friend bool operator!=(const LineIterator &lhs, const LineIterator &rhs) { 47 assert(lhs.m_obj == rhs.m_obj); 48 if (lhs.m_next_section_idx != rhs.m_next_section_idx) 49 return true; 50 if (lhs.m_current_line != rhs.m_current_line) 51 return true; 52 assert(lhs.m_next_line == rhs.m_next_line); 53 return false; 54 } 55 56 const LineIterator &operator++(); 57 llvm::StringRef operator*() const { 58 return m_section_text.slice(m_current_line, m_next_line); 59 } 60 61 Bookmark GetBookmark() const { 62 return Bookmark{m_next_section_idx, m_current_line}; 63 } 64 65 private: 66 ObjectFile *m_obj; 67 ConstString m_section_type; 68 uint32_t m_next_section_idx; 69 llvm::StringRef m_section_text; 70 size_t m_current_line; 71 size_t m_next_line; 72 73 void FindNextLine() { 74 m_next_line = m_section_text.find('\n', m_current_line); 75 if (m_next_line != llvm::StringRef::npos) { 76 ++m_next_line; 77 if (m_next_line >= m_section_text.size()) 78 m_next_line = llvm::StringRef::npos; 79 } 80 } 81 }; 82 83 SymbolFileBreakpad::LineIterator::LineIterator(ObjectFile &obj, 84 Record::Kind section_type, 85 Bookmark bookmark) 86 : m_obj(&obj), m_section_type(toString(section_type)), 87 m_next_section_idx(bookmark.section), m_current_line(bookmark.offset) { 88 Section § = 89 *obj.GetSectionList()->GetSectionAtIndex(m_next_section_idx - 1); 90 assert(sect.GetName() == m_section_type); 91 92 DataExtractor data; 93 obj.ReadSectionData(§, data); 94 m_section_text = toStringRef(data.GetData()); 95 96 assert(m_current_line < m_section_text.size()); 97 FindNextLine(); 98 } 99 100 const SymbolFileBreakpad::LineIterator & 101 SymbolFileBreakpad::LineIterator::operator++() { 102 const SectionList &list = *m_obj->GetSectionList(); 103 size_t num_sections = list.GetNumSections(0); 104 while (m_next_line != llvm::StringRef::npos || 105 m_next_section_idx < num_sections) { 106 if (m_next_line != llvm::StringRef::npos) { 107 m_current_line = m_next_line; 108 FindNextLine(); 109 return *this; 110 } 111 112 Section § = *list.GetSectionAtIndex(m_next_section_idx++); 113 if (sect.GetName() != m_section_type) 114 continue; 115 DataExtractor data; 116 m_obj->ReadSectionData(§, data); 117 m_section_text = toStringRef(data.GetData()); 118 m_next_line = 0; 119 } 120 // We've reached the end. 121 m_current_line = m_next_line; 122 return *this; 123 } 124 125 llvm::iterator_range<SymbolFileBreakpad::LineIterator> 126 SymbolFileBreakpad::lines(Record::Kind section_type) { 127 return llvm::make_range(LineIterator(*m_obj_file, section_type), 128 LineIterator(*m_obj_file)); 129 } 130 131 namespace { 132 // A helper class for constructing the list of support files for a given compile 133 // unit. 134 class SupportFileMap { 135 public: 136 // Given a breakpad file ID, return a file ID to be used in the support files 137 // for this compile unit. 138 size_t operator[](size_t file) { 139 return m_map.try_emplace(file, m_map.size() + 1).first->second; 140 } 141 142 // Construct a FileSpecList containing only the support files relevant for 143 // this compile unit (in the correct order). 144 FileSpecList translate(const FileSpec &cu_spec, 145 llvm::ArrayRef<FileSpec> all_files); 146 147 private: 148 llvm::DenseMap<size_t, size_t> m_map; 149 }; 150 } // namespace 151 152 FileSpecList SupportFileMap::translate(const FileSpec &cu_spec, 153 llvm::ArrayRef<FileSpec> all_files) { 154 std::vector<FileSpec> result; 155 result.resize(m_map.size() + 1); 156 result[0] = cu_spec; 157 for (const auto &KV : m_map) { 158 if (KV.first < all_files.size()) 159 result[KV.second] = all_files[KV.first]; 160 } 161 return FileSpecList(std::move(result)); 162 } 163 164 void SymbolFileBreakpad::Initialize() { 165 PluginManager::RegisterPlugin(GetPluginNameStatic(), 166 GetPluginDescriptionStatic(), CreateInstance, 167 DebuggerInitialize); 168 } 169 170 void SymbolFileBreakpad::Terminate() { 171 PluginManager::UnregisterPlugin(CreateInstance); 172 } 173 174 ConstString SymbolFileBreakpad::GetPluginNameStatic() { 175 static ConstString g_name("breakpad"); 176 return g_name; 177 } 178 179 uint32_t SymbolFileBreakpad::CalculateAbilities() { 180 if (!m_obj_file) 181 return 0; 182 if (m_obj_file->GetPluginName() != ObjectFileBreakpad::GetPluginNameStatic()) 183 return 0; 184 185 return CompileUnits | Functions | LineTables; 186 } 187 188 uint32_t SymbolFileBreakpad::GetNumCompileUnits() { 189 ParseCUData(); 190 return m_cu_data->GetSize(); 191 } 192 193 CompUnitSP SymbolFileBreakpad::ParseCompileUnitAtIndex(uint32_t index) { 194 if (index >= m_cu_data->GetSize()) 195 return nullptr; 196 197 CompUnitData &data = m_cu_data->GetEntryRef(index).data; 198 199 ParseFileRecords(); 200 201 FileSpec spec; 202 203 // The FileSpec of the compile unit will be the file corresponding to the 204 // first LINE record. 205 LineIterator It(*m_obj_file, Record::Func, data.bookmark), End(*m_obj_file); 206 assert(Record::classify(*It) == Record::Func); 207 ++It; // Skip FUNC record. 208 if (It != End) { 209 auto record = LineRecord::parse(*It); 210 if (record && record->FileNum < m_files->size()) 211 spec = (*m_files)[record->FileNum]; 212 } 213 214 auto cu_sp = std::make_shared<CompileUnit>(m_obj_file->GetModule(), 215 /*user_data*/ nullptr, spec, index, 216 eLanguageTypeUnknown, 217 /*is_optimized*/ eLazyBoolNo); 218 219 GetSymbolVendor().SetCompileUnitAtIndex(index, cu_sp); 220 return cu_sp; 221 } 222 223 size_t SymbolFileBreakpad::ParseFunctions(CompileUnit &comp_unit) { 224 // TODO 225 return 0; 226 } 227 228 bool SymbolFileBreakpad::ParseLineTable(CompileUnit &comp_unit) { 229 CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data; 230 231 if (!data.line_table_up) 232 ParseLineTableAndSupportFiles(comp_unit, data); 233 234 comp_unit.SetLineTable(data.line_table_up.release()); 235 return true; 236 } 237 238 bool SymbolFileBreakpad::ParseSupportFiles(CompileUnit &comp_unit, 239 FileSpecList &support_files) { 240 CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data; 241 if (!data.support_files) 242 ParseLineTableAndSupportFiles(comp_unit, data); 243 244 support_files = std::move(*data.support_files); 245 return true; 246 } 247 248 uint32_t 249 SymbolFileBreakpad::ResolveSymbolContext(const Address &so_addr, 250 SymbolContextItem resolve_scope, 251 SymbolContext &sc) { 252 if (!(resolve_scope & (eSymbolContextCompUnit | eSymbolContextLineEntry))) 253 return 0; 254 255 ParseCUData(); 256 uint32_t idx = 257 m_cu_data->FindEntryIndexThatContains(so_addr.GetFileAddress()); 258 if (idx == UINT32_MAX) 259 return 0; 260 261 sc.comp_unit = GetSymbolVendor().GetCompileUnitAtIndex(idx).get(); 262 SymbolContextItem result = eSymbolContextCompUnit; 263 if (resolve_scope & eSymbolContextLineEntry) { 264 if (sc.comp_unit->GetLineTable()->FindLineEntryByAddress(so_addr, 265 sc.line_entry)) { 266 result |= eSymbolContextLineEntry; 267 } 268 } 269 270 return result; 271 } 272 273 uint32_t SymbolFileBreakpad::ResolveSymbolContext( 274 const FileSpec &file_spec, uint32_t line, bool check_inlines, 275 lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) { 276 if (!(resolve_scope & eSymbolContextCompUnit)) 277 return 0; 278 279 uint32_t old_size = sc_list.GetSize(); 280 for (size_t i = 0, size = GetNumCompileUnits(); i < size; ++i) { 281 CompileUnit &cu = *GetSymbolVendor().GetCompileUnitAtIndex(i); 282 cu.ResolveSymbolContext(file_spec, line, check_inlines, 283 /*exact*/ false, resolve_scope, sc_list); 284 } 285 return sc_list.GetSize() - old_size; 286 } 287 288 uint32_t SymbolFileBreakpad::FindFunctions( 289 ConstString name, const CompilerDeclContext *parent_decl_ctx, 290 FunctionNameType name_type_mask, bool include_inlines, bool append, 291 SymbolContextList &sc_list) { 292 // TODO 293 if (!append) 294 sc_list.Clear(); 295 return sc_list.GetSize(); 296 } 297 298 uint32_t SymbolFileBreakpad::FindFunctions(const RegularExpression ®ex, 299 bool include_inlines, bool append, 300 SymbolContextList &sc_list) { 301 // TODO 302 if (!append) 303 sc_list.Clear(); 304 return sc_list.GetSize(); 305 } 306 307 uint32_t SymbolFileBreakpad::FindTypes( 308 ConstString name, const CompilerDeclContext *parent_decl_ctx, 309 bool append, uint32_t max_matches, 310 llvm::DenseSet<SymbolFile *> &searched_symbol_files, TypeMap &types) { 311 if (!append) 312 types.Clear(); 313 return types.GetSize(); 314 } 315 316 size_t 317 SymbolFileBreakpad::FindTypes(const std::vector<CompilerContext> &context, 318 bool append, TypeMap &types) { 319 if (!append) 320 types.Clear(); 321 return types.GetSize(); 322 } 323 324 void SymbolFileBreakpad::AddSymbols(Symtab &symtab) { 325 Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); 326 Module &module = *m_obj_file->GetModule(); 327 addr_t base = GetBaseFileAddress(); 328 if (base == LLDB_INVALID_ADDRESS) { 329 LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping " 330 "symtab population."); 331 return; 332 } 333 334 const SectionList &list = *module.GetSectionList(); 335 llvm::DenseMap<addr_t, Symbol> symbols; 336 auto add_symbol = [&](addr_t address, llvm::Optional<addr_t> size, 337 llvm::StringRef name) { 338 address += base; 339 SectionSP section_sp = list.FindSectionContainingFileAddress(address); 340 if (!section_sp) { 341 LLDB_LOG(log, 342 "Ignoring symbol {0}, whose address ({1}) is outside of the " 343 "object file. Mismatched symbol file?", 344 name, address); 345 return; 346 } 347 symbols.try_emplace( 348 address, /*symID*/ 0, Mangled(name, /*is_mangled*/ false), 349 eSymbolTypeCode, /*is_global*/ true, /*is_debug*/ false, 350 /*is_trampoline*/ false, /*is_artificial*/ false, 351 AddressRange(section_sp, address - section_sp->GetFileAddress(), 352 size.getValueOr(0)), 353 size.hasValue(), /*contains_linker_annotations*/ false, /*flags*/ 0); 354 }; 355 356 for (llvm::StringRef line : lines(Record::Func)) { 357 if (auto record = FuncRecord::parse(line)) 358 add_symbol(record->Address, record->Size, record->Name); 359 } 360 361 for (llvm::StringRef line : lines(Record::Public)) { 362 if (auto record = PublicRecord::parse(line)) 363 add_symbol(record->Address, llvm::None, record->Name); 364 else 365 LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line); 366 } 367 368 for (auto &KV : symbols) 369 symtab.AddSymbol(std::move(KV.second)); 370 symtab.CalculateSymbolSizes(); 371 } 372 373 SymbolVendor &SymbolFileBreakpad::GetSymbolVendor() { 374 return *m_obj_file->GetModule()->GetSymbolVendor(); 375 } 376 377 addr_t SymbolFileBreakpad::GetBaseFileAddress() { 378 return m_obj_file->GetModule() 379 ->GetObjectFile() 380 ->GetBaseAddress() 381 .GetFileAddress(); 382 } 383 384 // Parse out all the FILE records from the breakpad file. These will be needed 385 // when constructing the support file lists for individual compile units. 386 void SymbolFileBreakpad::ParseFileRecords() { 387 if (m_files) 388 return; 389 m_files.emplace(); 390 391 Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); 392 for (llvm::StringRef line : lines(Record::File)) { 393 auto record = FileRecord::parse(line); 394 if (!record) { 395 LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line); 396 continue; 397 } 398 399 if (record->Number >= m_files->size()) 400 m_files->resize(record->Number + 1); 401 FileSpec::Style style = FileSpec::GuessPathStyle(record->Name) 402 .getValueOr(FileSpec::Style::native); 403 (*m_files)[record->Number] = FileSpec(record->Name, style); 404 } 405 } 406 407 void SymbolFileBreakpad::ParseCUData() { 408 if (m_cu_data) 409 return; 410 411 m_cu_data.emplace(); 412 Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); 413 addr_t base = GetBaseFileAddress(); 414 if (base == LLDB_INVALID_ADDRESS) { 415 LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address " 416 "of object file."); 417 } 418 419 // We shall create one compile unit for each FUNC record. So, count the number 420 // of FUNC records, and store them in m_cu_data, together with their ranges. 421 for (LineIterator It(*m_obj_file, Record::Func), End(*m_obj_file); It != End; 422 ++It) { 423 if (auto record = FuncRecord::parse(*It)) { 424 m_cu_data->Append(CompUnitMap::Entry(base + record->Address, record->Size, 425 CompUnitData(It.GetBookmark()))); 426 } else 427 LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It); 428 } 429 m_cu_data->Sort(); 430 } 431 432 // Construct the list of support files and line table entries for the given 433 // compile unit. 434 void SymbolFileBreakpad::ParseLineTableAndSupportFiles(CompileUnit &cu, 435 CompUnitData &data) { 436 addr_t base = GetBaseFileAddress(); 437 assert(base != LLDB_INVALID_ADDRESS && 438 "How did we create compile units without a base address?"); 439 440 SupportFileMap map; 441 data.line_table_up = llvm::make_unique<LineTable>(&cu); 442 std::unique_ptr<LineSequence> line_seq_up( 443 data.line_table_up->CreateLineSequenceContainer()); 444 llvm::Optional<addr_t> next_addr; 445 auto finish_sequence = [&]() { 446 data.line_table_up->AppendLineEntryToSequence( 447 line_seq_up.get(), *next_addr, /*line*/ 0, /*column*/ 0, 448 /*file_idx*/ 0, /*is_start_of_statement*/ false, 449 /*is_start_of_basic_block*/ false, /*is_prologue_end*/ false, 450 /*is_epilogue_begin*/ false, /*is_terminal_entry*/ true); 451 data.line_table_up->InsertSequence(line_seq_up.get()); 452 line_seq_up->Clear(); 453 }; 454 455 LineIterator It(*m_obj_file, Record::Func, data.bookmark), End(*m_obj_file); 456 assert(Record::classify(*It) == Record::Func); 457 for (++It; It != End; ++It) { 458 auto record = LineRecord::parse(*It); 459 if (!record) 460 break; 461 462 record->Address += base; 463 464 if (next_addr && *next_addr != record->Address) { 465 // Discontiguous entries. Finish off the previous sequence and reset. 466 finish_sequence(); 467 } 468 data.line_table_up->AppendLineEntryToSequence( 469 line_seq_up.get(), record->Address, record->LineNum, /*column*/ 0, 470 map[record->FileNum], /*is_start_of_statement*/ true, 471 /*is_start_of_basic_block*/ false, /*is_prologue_end*/ false, 472 /*is_epilogue_begin*/ false, /*is_terminal_entry*/ false); 473 next_addr = record->Address + record->Size; 474 } 475 if (next_addr) 476 finish_sequence(); 477 data.support_files = map.translate(cu, *m_files); 478 } 479