1 | //===-- SymbolFileBreakpad.cpp --------------------------------------------===// |
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/LLDBLog.h" |
21 | #include "lldb/Utility/Log.h" |
22 | #include "lldb/Utility/StreamString.h" |
23 | #include "llvm/ADT/StringExtras.h" |
24 | #include <optional> |
25 | |
26 | using namespace lldb; |
27 | using namespace lldb_private; |
28 | using namespace lldb_private::breakpad; |
29 | |
30 | LLDB_PLUGIN_DEFINE(SymbolFileBreakpad) |
31 | |
32 | char SymbolFileBreakpad::ID; |
33 | |
34 | class SymbolFileBreakpad::LineIterator { |
35 | public: |
36 | // begin iterator for sections of given type |
37 | LineIterator(ObjectFile &obj, Record::Kind section_type) |
38 | : m_obj(&obj), m_section_type(toString(K: section_type)), |
39 | m_next_section_idx(0), m_next_line(llvm::StringRef::npos) { |
40 | ++*this; |
41 | } |
42 | |
43 | // An iterator starting at the position given by the bookmark. |
44 | LineIterator(ObjectFile &obj, Record::Kind section_type, Bookmark bookmark); |
45 | |
46 | // end iterator |
47 | explicit LineIterator(ObjectFile &obj) |
48 | : m_obj(&obj), |
49 | m_next_section_idx(m_obj->GetSectionList()->GetNumSections(depth: 0)), |
50 | m_current_line(llvm::StringRef::npos), |
51 | m_next_line(llvm::StringRef::npos) {} |
52 | |
53 | friend bool operator!=(const LineIterator &lhs, const LineIterator &rhs) { |
54 | assert(lhs.m_obj == rhs.m_obj); |
55 | if (lhs.m_next_section_idx != rhs.m_next_section_idx) |
56 | return true; |
57 | if (lhs.m_current_line != rhs.m_current_line) |
58 | return true; |
59 | assert(lhs.m_next_line == rhs.m_next_line); |
60 | return false; |
61 | } |
62 | |
63 | const LineIterator &operator++(); |
64 | llvm::StringRef operator*() const { |
65 | return m_section_text.slice(Start: m_current_line, End: m_next_line); |
66 | } |
67 | |
68 | Bookmark GetBookmark() const { |
69 | return Bookmark{.section: m_next_section_idx, .offset: m_current_line}; |
70 | } |
71 | |
72 | private: |
73 | ObjectFile *m_obj; |
74 | ConstString m_section_type; |
75 | uint32_t m_next_section_idx; |
76 | llvm::StringRef m_section_text; |
77 | size_t m_current_line; |
78 | size_t m_next_line; |
79 | |
80 | void FindNextLine() { |
81 | m_next_line = m_section_text.find(C: '\n', From: m_current_line); |
82 | if (m_next_line != llvm::StringRef::npos) { |
83 | ++m_next_line; |
84 | if (m_next_line >= m_section_text.size()) |
85 | m_next_line = llvm::StringRef::npos; |
86 | } |
87 | } |
88 | }; |
89 | |
90 | SymbolFileBreakpad::LineIterator::LineIterator(ObjectFile &obj, |
91 | Record::Kind section_type, |
92 | Bookmark bookmark) |
93 | : m_obj(&obj), m_section_type(toString(K: section_type)), |
94 | m_next_section_idx(bookmark.section), m_current_line(bookmark.offset) { |
95 | Section § = |
96 | *obj.GetSectionList()->GetSectionAtIndex(idx: m_next_section_idx - 1); |
97 | assert(sect.GetName() == m_section_type); |
98 | |
99 | DataExtractor data; |
100 | obj.ReadSectionData(section: §, section_data&: data); |
101 | m_section_text = toStringRef(Input: data.GetData()); |
102 | |
103 | assert(m_current_line < m_section_text.size()); |
104 | FindNextLine(); |
105 | } |
106 | |
107 | const SymbolFileBreakpad::LineIterator & |
108 | SymbolFileBreakpad::LineIterator::operator++() { |
109 | const SectionList &list = *m_obj->GetSectionList(); |
110 | size_t num_sections = list.GetNumSections(depth: 0); |
111 | while (m_next_line != llvm::StringRef::npos || |
112 | m_next_section_idx < num_sections) { |
113 | if (m_next_line != llvm::StringRef::npos) { |
114 | m_current_line = m_next_line; |
115 | FindNextLine(); |
116 | return *this; |
117 | } |
118 | |
119 | Section § = *list.GetSectionAtIndex(idx: m_next_section_idx++); |
120 | if (sect.GetName() != m_section_type) |
121 | continue; |
122 | DataExtractor data; |
123 | m_obj->ReadSectionData(section: §, section_data&: data); |
124 | m_section_text = toStringRef(Input: data.GetData()); |
125 | m_next_line = 0; |
126 | } |
127 | // We've reached the end. |
128 | m_current_line = m_next_line; |
129 | return *this; |
130 | } |
131 | |
132 | llvm::iterator_range<SymbolFileBreakpad::LineIterator> |
133 | SymbolFileBreakpad::lines(Record::Kind section_type) { |
134 | return llvm::make_range(x: LineIterator(*m_objfile_sp, section_type), |
135 | y: LineIterator(*m_objfile_sp)); |
136 | } |
137 | |
138 | namespace { |
139 | // A helper class for constructing the list of support files for a given compile |
140 | // unit. |
141 | class SupportFileMap { |
142 | public: |
143 | // Given a breakpad file ID, return a file ID to be used in the support files |
144 | // for this compile unit. |
145 | size_t operator[](size_t file) { |
146 | return m_map.try_emplace(Key: file, Args: m_map.size() + 1).first->second; |
147 | } |
148 | |
149 | // Construct a FileSpecList containing only the support files relevant for |
150 | // this compile unit (in the correct order). |
151 | FileSpecList translate(const FileSpec &cu_spec, |
152 | llvm::ArrayRef<FileSpec> all_files); |
153 | |
154 | private: |
155 | llvm::DenseMap<size_t, size_t> m_map; |
156 | }; |
157 | } // namespace |
158 | |
159 | FileSpecList SupportFileMap::translate(const FileSpec &cu_spec, |
160 | llvm::ArrayRef<FileSpec> all_files) { |
161 | std::vector<FileSpec> result; |
162 | result.resize(new_size: m_map.size() + 1); |
163 | result[0] = cu_spec; |
164 | for (const auto &KV : m_map) { |
165 | if (KV.first < all_files.size()) |
166 | result[KV.second] = all_files[KV.first]; |
167 | } |
168 | return FileSpecList(std::move(result)); |
169 | } |
170 | |
171 | void SymbolFileBreakpad::Initialize() { |
172 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
173 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance, |
174 | debugger_init_callback: DebuggerInitialize); |
175 | } |
176 | |
177 | void SymbolFileBreakpad::Terminate() { |
178 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
179 | } |
180 | |
181 | uint32_t SymbolFileBreakpad::CalculateAbilities() { |
182 | if (!m_objfile_sp || !llvm::isa<ObjectFileBreakpad>(Val: *m_objfile_sp)) |
183 | return 0; |
184 | |
185 | return CompileUnits | Functions | LineTables; |
186 | } |
187 | |
188 | uint32_t SymbolFileBreakpad::CalculateNumCompileUnits() { |
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(i: 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_objfile_sp, Record::Func, data.bookmark), |
206 | End(*m_objfile_sp); |
207 | assert(Record::classify(*It) == Record::Func); |
208 | ++It; // Skip FUNC record. |
209 | // Skip INLINE records. |
210 | while (It != End && Record::classify(Line: *It) == Record::Inline) |
211 | ++It; |
212 | |
213 | if (It != End) { |
214 | auto record = LineRecord::parse(Line: *It); |
215 | if (record && record->FileNum < m_files->size()) |
216 | spec = (*m_files)[record->FileNum]; |
217 | } |
218 | |
219 | auto cu_sp = std::make_shared<CompileUnit>( |
220 | args: m_objfile_sp->GetModule(), |
221 | /*user_data*/ args: nullptr, args: std::make_shared<SupportFile>(args&: spec), args&: index, |
222 | args: eLanguageTypeUnknown, |
223 | /*is_optimized*/ args: eLazyBoolNo); |
224 | |
225 | SetCompileUnitAtIndex(idx: index, cu_sp); |
226 | return cu_sp; |
227 | } |
228 | |
229 | FunctionSP SymbolFileBreakpad::GetOrCreateFunction(CompileUnit &comp_unit) { |
230 | user_id_t id = comp_unit.GetID(); |
231 | if (FunctionSP func_sp = comp_unit.FindFunctionByUID(uid: id)) |
232 | return func_sp; |
233 | |
234 | Log *log = GetLog(mask: LLDBLog::Symbols); |
235 | FunctionSP func_sp; |
236 | addr_t base = GetBaseFileAddress(); |
237 | if (base == LLDB_INVALID_ADDRESS) { |
238 | LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping " |
239 | "symtab population." ); |
240 | return func_sp; |
241 | } |
242 | |
243 | const SectionList *list = comp_unit.GetModule()->GetSectionList(); |
244 | CompUnitData &data = m_cu_data->GetEntryRef(i: id).data; |
245 | LineIterator It(*m_objfile_sp, Record::Func, data.bookmark); |
246 | assert(Record::classify(*It) == Record::Func); |
247 | |
248 | if (auto record = FuncRecord::parse(Line: *It)) { |
249 | Mangled func_name; |
250 | func_name.SetValue(ConstString(record->Name)); |
251 | addr_t address = record->Address + base; |
252 | SectionSP section_sp = list->FindSectionContainingFileAddress(addr: address); |
253 | if (section_sp) { |
254 | AddressRange func_range( |
255 | section_sp, address - section_sp->GetFileAddress(), record->Size); |
256 | // Use the CU's id because every CU has only one function inside. |
257 | func_sp = std::make_shared<Function>(args: &comp_unit, args&: id, args: 0, args&: func_name, |
258 | args: nullptr, args&: func_range); |
259 | comp_unit.AddFunction(function_sp&: func_sp); |
260 | } |
261 | } |
262 | return func_sp; |
263 | } |
264 | |
265 | size_t SymbolFileBreakpad::ParseFunctions(CompileUnit &comp_unit) { |
266 | std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); |
267 | return GetOrCreateFunction(comp_unit) ? 1 : 0; |
268 | } |
269 | |
270 | bool SymbolFileBreakpad::ParseLineTable(CompileUnit &comp_unit) { |
271 | std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); |
272 | CompUnitData &data = m_cu_data->GetEntryRef(i: comp_unit.GetID()).data; |
273 | |
274 | if (!data.line_table_up) |
275 | ParseLineTableAndSupportFiles(cu&: comp_unit, data); |
276 | |
277 | comp_unit.SetLineTable(data.line_table_up.release()); |
278 | return true; |
279 | } |
280 | |
281 | bool SymbolFileBreakpad::ParseSupportFiles(CompileUnit &comp_unit, |
282 | SupportFileList &support_files) { |
283 | std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); |
284 | CompUnitData &data = m_cu_data->GetEntryRef(i: comp_unit.GetID()).data; |
285 | if (!data.support_files) |
286 | ParseLineTableAndSupportFiles(cu&: comp_unit, data); |
287 | |
288 | for (auto &fs : *data.support_files) |
289 | support_files.Append(file: fs); |
290 | return true; |
291 | } |
292 | |
293 | size_t SymbolFileBreakpad::ParseBlocksRecursive(Function &func) { |
294 | std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); |
295 | CompileUnit *comp_unit = func.GetCompileUnit(); |
296 | lldbassert(comp_unit); |
297 | ParseInlineOriginRecords(); |
298 | // A vector of current each level's parent block. For example, when parsing |
299 | // "INLINE 0 ...", the current level is 0 and its parent block is the |
300 | // function block at index 0. |
301 | std::vector<Block *> blocks; |
302 | Block &block = func.GetBlock(can_create: false); |
303 | block.AddRange(range: Block::Range(0, func.GetAddressRange().GetByteSize())); |
304 | blocks.push_back(x: &block); |
305 | |
306 | size_t blocks_added = 0; |
307 | addr_t func_base = func.GetAddressRange().GetBaseAddress().GetOffset(); |
308 | CompUnitData &data = m_cu_data->GetEntryRef(i: comp_unit->GetID()).data; |
309 | LineIterator It(*m_objfile_sp, Record::Func, data.bookmark), |
310 | End(*m_objfile_sp); |
311 | ++It; // Skip the FUNC record. |
312 | size_t last_added_nest_level = 0; |
313 | while (It != End && Record::classify(Line: *It) == Record::Inline) { |
314 | if (auto record = InlineRecord::parse(Line: *It)) { |
315 | if (record->InlineNestLevel == 0 || |
316 | record->InlineNestLevel <= last_added_nest_level + 1) { |
317 | last_added_nest_level = record->InlineNestLevel; |
318 | BlockSP block_sp = std::make_shared<Block>(args: It.GetBookmark().offset); |
319 | FileSpec callsite_file; |
320 | if (record->CallSiteFileNum < m_files->size()) |
321 | callsite_file = (*m_files)[record->CallSiteFileNum]; |
322 | llvm::StringRef name; |
323 | if (record->OriginNum < m_inline_origins->size()) |
324 | name = (*m_inline_origins)[record->OriginNum]; |
325 | |
326 | Declaration callsite(callsite_file, record->CallSiteLineNum); |
327 | block_sp->SetInlinedFunctionInfo(name: name.str().c_str(), |
328 | /*mangled=*/nullptr, |
329 | /*decl_ptr=*/nullptr, call_decl_ptr: &callsite); |
330 | for (const auto &range : record->Ranges) { |
331 | block_sp->AddRange( |
332 | range: Block::Range(range.first - func_base, range.second)); |
333 | } |
334 | block_sp->FinalizeRanges(); |
335 | |
336 | blocks[record->InlineNestLevel]->AddChild(child_block_sp: block_sp); |
337 | if (record->InlineNestLevel + 1 >= blocks.size()) { |
338 | blocks.resize(new_size: blocks.size() + 1); |
339 | } |
340 | blocks[record->InlineNestLevel + 1] = block_sp.get(); |
341 | ++blocks_added; |
342 | } |
343 | } |
344 | ++It; |
345 | } |
346 | return blocks_added; |
347 | } |
348 | |
349 | void SymbolFileBreakpad::ParseInlineOriginRecords() { |
350 | if (m_inline_origins) |
351 | return; |
352 | m_inline_origins.emplace(); |
353 | |
354 | Log *log = GetLog(mask: LLDBLog::Symbols); |
355 | for (llvm::StringRef line : lines(section_type: Record::InlineOrigin)) { |
356 | auto record = InlineOriginRecord::parse(Line: line); |
357 | if (!record) { |
358 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , line); |
359 | continue; |
360 | } |
361 | |
362 | if (record->Number >= m_inline_origins->size()) |
363 | m_inline_origins->resize(new_size: record->Number + 1); |
364 | (*m_inline_origins)[record->Number] = record->Name; |
365 | } |
366 | } |
367 | |
368 | uint32_t |
369 | SymbolFileBreakpad::ResolveSymbolContext(const Address &so_addr, |
370 | SymbolContextItem resolve_scope, |
371 | SymbolContext &sc) { |
372 | std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); |
373 | if (!(resolve_scope & (eSymbolContextCompUnit | eSymbolContextLineEntry | |
374 | eSymbolContextFunction | eSymbolContextBlock))) |
375 | return 0; |
376 | |
377 | ParseCUData(); |
378 | uint32_t idx = |
379 | m_cu_data->FindEntryIndexThatContains(addr: so_addr.GetFileAddress()); |
380 | if (idx == UINT32_MAX) |
381 | return 0; |
382 | |
383 | sc.comp_unit = GetCompileUnitAtIndex(idx).get(); |
384 | SymbolContextItem result = eSymbolContextCompUnit; |
385 | if (resolve_scope & eSymbolContextLineEntry) { |
386 | if (sc.comp_unit->GetLineTable()->FindLineEntryByAddress(so_addr, |
387 | line_entry&: sc.line_entry)) { |
388 | result |= eSymbolContextLineEntry; |
389 | } |
390 | } |
391 | |
392 | if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) { |
393 | FunctionSP func_sp = GetOrCreateFunction(comp_unit&: *sc.comp_unit); |
394 | if (func_sp) { |
395 | sc.function = func_sp.get(); |
396 | result |= eSymbolContextFunction; |
397 | if (resolve_scope & eSymbolContextBlock) { |
398 | Block &block = func_sp->GetBlock(can_create: true); |
399 | sc.block = block.FindInnermostBlockByOffset( |
400 | offset: so_addr.GetFileAddress() - |
401 | sc.function->GetAddressRange().GetBaseAddress().GetFileAddress()); |
402 | if (sc.block) |
403 | result |= eSymbolContextBlock; |
404 | } |
405 | } |
406 | } |
407 | |
408 | return result; |
409 | } |
410 | |
411 | uint32_t SymbolFileBreakpad::ResolveSymbolContext( |
412 | const SourceLocationSpec &src_location_spec, |
413 | lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) { |
414 | std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); |
415 | if (!(resolve_scope & eSymbolContextCompUnit)) |
416 | return 0; |
417 | |
418 | uint32_t old_size = sc_list.GetSize(); |
419 | for (size_t i = 0, size = GetNumCompileUnits(); i < size; ++i) { |
420 | CompileUnit &cu = *GetCompileUnitAtIndex(idx: i); |
421 | cu.ResolveSymbolContext(src_location_spec, resolve_scope, sc_list); |
422 | } |
423 | return sc_list.GetSize() - old_size; |
424 | } |
425 | |
426 | void SymbolFileBreakpad::FindFunctions( |
427 | const Module::LookupInfo &lookup_info, |
428 | const CompilerDeclContext &parent_decl_ctx, bool include_inlines, |
429 | SymbolContextList &sc_list) { |
430 | std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); |
431 | // TODO: Implement this with supported FunctionNameType. |
432 | |
433 | ConstString name = lookup_info.GetLookupName(); |
434 | for (uint32_t i = 0; i < GetNumCompileUnits(); ++i) { |
435 | CompUnitSP cu_sp = GetCompileUnitAtIndex(idx: i); |
436 | FunctionSP func_sp = GetOrCreateFunction(comp_unit&: *cu_sp); |
437 | if (func_sp && name == func_sp->GetNameNoArguments()) { |
438 | SymbolContext sc; |
439 | sc.comp_unit = cu_sp.get(); |
440 | sc.function = func_sp.get(); |
441 | sc.module_sp = func_sp->CalculateSymbolContextModule(); |
442 | sc_list.Append(sc); |
443 | } |
444 | } |
445 | } |
446 | |
447 | void SymbolFileBreakpad::FindFunctions(const RegularExpression ®ex, |
448 | bool include_inlines, |
449 | SymbolContextList &sc_list) { |
450 | // TODO |
451 | } |
452 | |
453 | void SymbolFileBreakpad::AddSymbols(Symtab &symtab) { |
454 | Log *log = GetLog(mask: LLDBLog::Symbols); |
455 | Module &module = *m_objfile_sp->GetModule(); |
456 | addr_t base = GetBaseFileAddress(); |
457 | if (base == LLDB_INVALID_ADDRESS) { |
458 | LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping " |
459 | "symtab population." ); |
460 | return; |
461 | } |
462 | |
463 | const SectionList &list = *module.GetSectionList(); |
464 | llvm::DenseSet<addr_t> found_symbol_addresses; |
465 | std::vector<Symbol> symbols; |
466 | auto add_symbol = [&](addr_t address, std::optional<addr_t> size, |
467 | llvm::StringRef name) { |
468 | address += base; |
469 | SectionSP section_sp = list.FindSectionContainingFileAddress(addr: address); |
470 | if (!section_sp) { |
471 | LLDB_LOG(log, |
472 | "Ignoring symbol {0}, whose address ({1}) is outside of the " |
473 | "object file. Mismatched symbol file?" , |
474 | name, address); |
475 | return; |
476 | } |
477 | // Keep track of what addresses were already added so far and only add |
478 | // the symbol with the first address. |
479 | if (!found_symbol_addresses.insert(V: address).second) |
480 | return; |
481 | symbols.emplace_back( |
482 | /*symID*/ args: 0, args: Mangled(name), args: eSymbolTypeCode, |
483 | /*is_global*/ args: true, /*is_debug*/ args: false, |
484 | /*is_trampoline*/ args: false, /*is_artificial*/ args: false, |
485 | args: AddressRange(section_sp, address - section_sp->GetFileAddress(), |
486 | size.value_or(u: 0)), |
487 | args: size.has_value(), /*contains_linker_annotations*/ args: false, /*flags*/ args: 0); |
488 | }; |
489 | |
490 | for (llvm::StringRef line : lines(section_type: Record::Public)) { |
491 | if (auto record = PublicRecord::parse(Line: line)) |
492 | add_symbol(record->Address, std::nullopt, record->Name); |
493 | else |
494 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , line); |
495 | } |
496 | |
497 | for (Symbol &symbol : symbols) |
498 | symtab.AddSymbol(symbol: std::move(symbol)); |
499 | symtab.Finalize(); |
500 | } |
501 | |
502 | llvm::Expected<lldb::addr_t> |
503 | SymbolFileBreakpad::GetParameterStackSize(Symbol &symbol) { |
504 | ParseUnwindData(); |
505 | if (auto *entry = m_unwind_data->win.FindEntryThatContains( |
506 | addr: symbol.GetAddress().GetFileAddress())) { |
507 | auto record = StackWinRecord::parse( |
508 | Line: *LineIterator(*m_objfile_sp, Record::StackWin, entry->data)); |
509 | assert(record); |
510 | return record->ParameterSize; |
511 | } |
512 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
513 | Msg: "Parameter size unknown." ); |
514 | } |
515 | |
516 | static std::optional<std::pair<llvm::StringRef, llvm::StringRef>> |
517 | GetRule(llvm::StringRef &unwind_rules) { |
518 | // Unwind rules are of the form |
519 | // register1: expression1 register2: expression2 ... |
520 | // We assume none of the tokens in expression<n> end with a colon. |
521 | |
522 | llvm::StringRef lhs, rest; |
523 | std::tie(args&: lhs, args&: rest) = getToken(Source: unwind_rules); |
524 | if (!lhs.consume_back(Suffix: ":" )) |
525 | return std::nullopt; |
526 | |
527 | // Seek forward to the next register: expression pair |
528 | llvm::StringRef::size_type pos = rest.find(Str: ": " ); |
529 | if (pos == llvm::StringRef::npos) { |
530 | // No pair found, this means the rest of the string is a single expression. |
531 | unwind_rules = llvm::StringRef(); |
532 | return std::make_pair(x&: lhs, y&: rest); |
533 | } |
534 | |
535 | // Go back one token to find the end of the current rule. |
536 | pos = rest.rfind(C: ' ', From: pos); |
537 | if (pos == llvm::StringRef::npos) |
538 | return std::nullopt; |
539 | |
540 | llvm::StringRef rhs = rest.take_front(N: pos); |
541 | unwind_rules = rest.drop_front(N: pos); |
542 | return std::make_pair(x&: lhs, y&: rhs); |
543 | } |
544 | |
545 | static const RegisterInfo * |
546 | ResolveRegister(const llvm::Triple &triple, |
547 | const SymbolFile::RegisterInfoResolver &resolver, |
548 | llvm::StringRef name) { |
549 | if (triple.isX86() || triple.isMIPS()) { |
550 | // X86 and MIPS registers have '$' in front of their register names. Arm and |
551 | // AArch64 don't. |
552 | if (!name.consume_front(Prefix: "$" )) |
553 | return nullptr; |
554 | } |
555 | return resolver.ResolveName(name); |
556 | } |
557 | |
558 | static const RegisterInfo * |
559 | ResolveRegisterOrRA(const llvm::Triple &triple, |
560 | const SymbolFile::RegisterInfoResolver &resolver, |
561 | llvm::StringRef name) { |
562 | if (name == ".ra" ) |
563 | return resolver.ResolveNumber(kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); |
564 | return ResolveRegister(triple, resolver, name); |
565 | } |
566 | |
567 | llvm::ArrayRef<uint8_t> SymbolFileBreakpad::SaveAsDWARF(postfix::Node &node) { |
568 | ArchSpec arch = m_objfile_sp->GetArchitecture(); |
569 | StreamString dwarf(Stream::eBinary, arch.GetAddressByteSize(), |
570 | arch.GetByteOrder()); |
571 | ToDWARF(node, stream&: dwarf); |
572 | uint8_t *saved = m_allocator.Allocate<uint8_t>(Num: dwarf.GetSize()); |
573 | std::memcpy(dest: saved, src: dwarf.GetData(), n: dwarf.GetSize()); |
574 | return {saved, dwarf.GetSize()}; |
575 | } |
576 | |
577 | bool SymbolFileBreakpad::ParseCFIUnwindRow(llvm::StringRef unwind_rules, |
578 | const RegisterInfoResolver &resolver, |
579 | UnwindPlan::Row &row) { |
580 | Log *log = GetLog(mask: LLDBLog::Symbols); |
581 | |
582 | llvm::BumpPtrAllocator node_alloc; |
583 | llvm::Triple triple = m_objfile_sp->GetArchitecture().GetTriple(); |
584 | while (auto rule = GetRule(unwind_rules)) { |
585 | node_alloc.Reset(); |
586 | llvm::StringRef lhs = rule->first; |
587 | postfix::Node *rhs = postfix::ParseOneExpression(expr: rule->second, alloc&: node_alloc); |
588 | if (!rhs) { |
589 | LLDB_LOG(log, "Could not parse `{0}` as unwind rhs." , rule->second); |
590 | return false; |
591 | } |
592 | |
593 | bool success = postfix::ResolveSymbols( |
594 | node&: rhs, replacer: [&](postfix::SymbolNode &symbol) -> postfix::Node * { |
595 | llvm::StringRef name = symbol.GetName(); |
596 | if (name == ".cfa" && lhs != ".cfa" ) |
597 | return postfix::MakeNode<postfix::InitialValueNode>(alloc&: node_alloc); |
598 | |
599 | if (const RegisterInfo *info = |
600 | ResolveRegister(triple, resolver, name)) { |
601 | return postfix::MakeNode<postfix::RegisterNode>( |
602 | alloc&: node_alloc, args: info->kinds[eRegisterKindLLDB]); |
603 | } |
604 | return nullptr; |
605 | }); |
606 | |
607 | if (!success) { |
608 | LLDB_LOG(log, "Resolving symbols in `{0}` failed." , rule->second); |
609 | return false; |
610 | } |
611 | |
612 | llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(node&: *rhs); |
613 | if (lhs == ".cfa" ) { |
614 | row.GetCFAValue().SetIsDWARFExpression(opcodes: saved.data(), len: saved.size()); |
615 | } else if (const RegisterInfo *info = |
616 | ResolveRegisterOrRA(triple, resolver, name: lhs)) { |
617 | UnwindPlan::Row::RegisterLocation loc; |
618 | loc.SetIsDWARFExpression(opcodes: saved.data(), len: saved.size()); |
619 | row.SetRegisterInfo(reg_num: info->kinds[eRegisterKindLLDB], register_location: loc); |
620 | } else |
621 | LLDB_LOG(log, "Invalid register `{0}` in unwind rule." , lhs); |
622 | } |
623 | if (unwind_rules.empty()) |
624 | return true; |
625 | |
626 | LLDB_LOG(log, "Could not parse `{0}` as an unwind rule." , unwind_rules); |
627 | return false; |
628 | } |
629 | |
630 | UnwindPlanSP |
631 | SymbolFileBreakpad::GetUnwindPlan(const Address &address, |
632 | const RegisterInfoResolver &resolver) { |
633 | ParseUnwindData(); |
634 | if (auto *entry = |
635 | m_unwind_data->cfi.FindEntryThatContains(addr: address.GetFileAddress())) |
636 | return ParseCFIUnwindPlan(bookmark: entry->data, resolver); |
637 | if (auto *entry = |
638 | m_unwind_data->win.FindEntryThatContains(addr: address.GetFileAddress())) |
639 | return ParseWinUnwindPlan(bookmark: entry->data, resolver); |
640 | return nullptr; |
641 | } |
642 | |
643 | UnwindPlanSP |
644 | SymbolFileBreakpad::ParseCFIUnwindPlan(const Bookmark &bookmark, |
645 | const RegisterInfoResolver &resolver) { |
646 | addr_t base = GetBaseFileAddress(); |
647 | if (base == LLDB_INVALID_ADDRESS) |
648 | return nullptr; |
649 | |
650 | LineIterator It(*m_objfile_sp, Record::StackCFI, bookmark), |
651 | End(*m_objfile_sp); |
652 | std::optional<StackCFIRecord> init_record = StackCFIRecord::parse(Line: *It); |
653 | assert(init_record && init_record->Size && |
654 | "Record already parsed successfully in ParseUnwindData!" ); |
655 | |
656 | auto plan_sp = std::make_shared<UnwindPlan>(args: lldb::eRegisterKindLLDB); |
657 | plan_sp->SetSourceName("breakpad STACK CFI" ); |
658 | plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
659 | plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo); |
660 | plan_sp->SetSourcedFromCompiler(eLazyBoolYes); |
661 | plan_sp->SetPlanValidAddressRange( |
662 | AddressRange(base + init_record->Address, *init_record->Size, |
663 | m_objfile_sp->GetModule()->GetSectionList())); |
664 | |
665 | auto row_sp = std::make_shared<UnwindPlan::Row>(); |
666 | row_sp->SetOffset(0); |
667 | if (!ParseCFIUnwindRow(unwind_rules: init_record->UnwindRules, resolver, row&: *row_sp)) |
668 | return nullptr; |
669 | plan_sp->AppendRow(row_sp); |
670 | for (++It; It != End; ++It) { |
671 | std::optional<StackCFIRecord> record = StackCFIRecord::parse(Line: *It); |
672 | if (!record) |
673 | return nullptr; |
674 | if (record->Size) |
675 | break; |
676 | |
677 | row_sp = std::make_shared<UnwindPlan::Row>(args&: *row_sp); |
678 | row_sp->SetOffset(record->Address - init_record->Address); |
679 | if (!ParseCFIUnwindRow(unwind_rules: record->UnwindRules, resolver, row&: *row_sp)) |
680 | return nullptr; |
681 | plan_sp->AppendRow(row_sp); |
682 | } |
683 | return plan_sp; |
684 | } |
685 | |
686 | UnwindPlanSP |
687 | SymbolFileBreakpad::ParseWinUnwindPlan(const Bookmark &bookmark, |
688 | const RegisterInfoResolver &resolver) { |
689 | Log *log = GetLog(mask: LLDBLog::Symbols); |
690 | addr_t base = GetBaseFileAddress(); |
691 | if (base == LLDB_INVALID_ADDRESS) |
692 | return nullptr; |
693 | |
694 | LineIterator It(*m_objfile_sp, Record::StackWin, bookmark); |
695 | std::optional<StackWinRecord> record = StackWinRecord::parse(Line: *It); |
696 | assert(record && "Record already parsed successfully in ParseUnwindData!" ); |
697 | |
698 | auto plan_sp = std::make_shared<UnwindPlan>(args: lldb::eRegisterKindLLDB); |
699 | plan_sp->SetSourceName("breakpad STACK WIN" ); |
700 | plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
701 | plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo); |
702 | plan_sp->SetSourcedFromCompiler(eLazyBoolYes); |
703 | plan_sp->SetPlanValidAddressRange( |
704 | AddressRange(base + record->RVA, record->CodeSize, |
705 | m_objfile_sp->GetModule()->GetSectionList())); |
706 | |
707 | auto row_sp = std::make_shared<UnwindPlan::Row>(); |
708 | row_sp->SetOffset(0); |
709 | |
710 | llvm::BumpPtrAllocator node_alloc; |
711 | std::vector<std::pair<llvm::StringRef, postfix::Node *>> program = |
712 | postfix::ParseFPOProgram(prog: record->ProgramString, alloc&: node_alloc); |
713 | |
714 | if (program.empty()) { |
715 | LLDB_LOG(log, "Invalid unwind rule: {0}." , record->ProgramString); |
716 | return nullptr; |
717 | } |
718 | auto it = program.begin(); |
719 | llvm::Triple triple = m_objfile_sp->GetArchitecture().GetTriple(); |
720 | const auto &symbol_resolver = |
721 | [&](postfix::SymbolNode &symbol) -> postfix::Node * { |
722 | llvm::StringRef name = symbol.GetName(); |
723 | for (const auto &rule : llvm::make_range(x: program.begin(), y: it)) { |
724 | if (rule.first == name) |
725 | return rule.second; |
726 | } |
727 | if (const RegisterInfo *info = ResolveRegister(triple, resolver, name)) |
728 | return postfix::MakeNode<postfix::RegisterNode>( |
729 | alloc&: node_alloc, args: info->kinds[eRegisterKindLLDB]); |
730 | return nullptr; |
731 | }; |
732 | |
733 | // We assume the first value will be the CFA. It is usually called T0, but |
734 | // clang will use T1, if it needs to realign the stack. |
735 | auto *symbol = llvm::dyn_cast<postfix::SymbolNode>(Val: it->second); |
736 | if (symbol && symbol->GetName() == ".raSearch" ) { |
737 | row_sp->GetCFAValue().SetRaSearch(record->LocalSize + |
738 | record->SavedRegisterSize); |
739 | } else { |
740 | if (!postfix::ResolveSymbols(node&: it->second, replacer: symbol_resolver)) { |
741 | LLDB_LOG(log, "Resolving symbols in `{0}` failed." , |
742 | record->ProgramString); |
743 | return nullptr; |
744 | } |
745 | llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(node&: *it->second); |
746 | row_sp->GetCFAValue().SetIsDWARFExpression(opcodes: saved.data(), len: saved.size()); |
747 | } |
748 | |
749 | // Replace the node value with InitialValueNode, so that subsequent |
750 | // expressions refer to the CFA value instead of recomputing the whole |
751 | // expression. |
752 | it->second = postfix::MakeNode<postfix::InitialValueNode>(alloc&: node_alloc); |
753 | |
754 | |
755 | // Now process the rest of the assignments. |
756 | for (++it; it != program.end(); ++it) { |
757 | const RegisterInfo *info = ResolveRegister(triple, resolver, name: it->first); |
758 | // It is not an error if the resolution fails because the program may |
759 | // contain temporary variables. |
760 | if (!info) |
761 | continue; |
762 | if (!postfix::ResolveSymbols(node&: it->second, replacer: symbol_resolver)) { |
763 | LLDB_LOG(log, "Resolving symbols in `{0}` failed." , |
764 | record->ProgramString); |
765 | return nullptr; |
766 | } |
767 | |
768 | llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(node&: *it->second); |
769 | UnwindPlan::Row::RegisterLocation loc; |
770 | loc.SetIsDWARFExpression(opcodes: saved.data(), len: saved.size()); |
771 | row_sp->SetRegisterInfo(reg_num: info->kinds[eRegisterKindLLDB], register_location: loc); |
772 | } |
773 | |
774 | plan_sp->AppendRow(row_sp); |
775 | return plan_sp; |
776 | } |
777 | |
778 | addr_t SymbolFileBreakpad::GetBaseFileAddress() { |
779 | return m_objfile_sp->GetModule() |
780 | ->GetObjectFile() |
781 | ->GetBaseAddress() |
782 | .GetFileAddress(); |
783 | } |
784 | |
785 | // Parse out all the FILE records from the breakpad file. These will be needed |
786 | // when constructing the support file lists for individual compile units. |
787 | void SymbolFileBreakpad::ParseFileRecords() { |
788 | if (m_files) |
789 | return; |
790 | m_files.emplace(); |
791 | |
792 | Log *log = GetLog(mask: LLDBLog::Symbols); |
793 | for (llvm::StringRef line : lines(section_type: Record::File)) { |
794 | auto record = FileRecord::parse(Line: line); |
795 | if (!record) { |
796 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , line); |
797 | continue; |
798 | } |
799 | |
800 | if (record->Number >= m_files->size()) |
801 | m_files->resize(new_size: record->Number + 1); |
802 | FileSpec::Style style = FileSpec::GuessPathStyle(absolute_path: record->Name) |
803 | .value_or(u: FileSpec::Style::native); |
804 | (*m_files)[record->Number] = FileSpec(record->Name, style); |
805 | } |
806 | } |
807 | |
808 | void SymbolFileBreakpad::ParseCUData() { |
809 | if (m_cu_data) |
810 | return; |
811 | |
812 | m_cu_data.emplace(); |
813 | Log *log = GetLog(mask: LLDBLog::Symbols); |
814 | addr_t base = GetBaseFileAddress(); |
815 | if (base == LLDB_INVALID_ADDRESS) { |
816 | LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address " |
817 | "of object file." ); |
818 | } |
819 | |
820 | // We shall create one compile unit for each FUNC record. So, count the number |
821 | // of FUNC records, and store them in m_cu_data, together with their ranges. |
822 | for (LineIterator It(*m_objfile_sp, Record::Func), End(*m_objfile_sp); |
823 | It != End; ++It) { |
824 | if (auto record = FuncRecord::parse(Line: *It)) { |
825 | m_cu_data->Append(entry: CompUnitMap::Entry(base + record->Address, record->Size, |
826 | CompUnitData(It.GetBookmark()))); |
827 | } else |
828 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , *It); |
829 | } |
830 | m_cu_data->Sort(); |
831 | } |
832 | |
833 | // Construct the list of support files and line table entries for the given |
834 | // compile unit. |
835 | void SymbolFileBreakpad::ParseLineTableAndSupportFiles(CompileUnit &cu, |
836 | CompUnitData &data) { |
837 | addr_t base = GetBaseFileAddress(); |
838 | assert(base != LLDB_INVALID_ADDRESS && |
839 | "How did we create compile units without a base address?" ); |
840 | |
841 | SupportFileMap map; |
842 | std::vector<std::unique_ptr<LineSequence>> sequences; |
843 | std::unique_ptr<LineSequence> line_seq_up = |
844 | LineTable::CreateLineSequenceContainer(); |
845 | std::optional<addr_t> next_addr; |
846 | auto finish_sequence = [&]() { |
847 | LineTable::AppendLineEntryToSequence( |
848 | sequence: line_seq_up.get(), file_addr: *next_addr, /*line=*/0, /*column=*/0, |
849 | /*file_idx=*/0, /*is_start_of_statement=*/false, |
850 | /*is_start_of_basic_block=*/false, /*is_prologue_end=*/false, |
851 | /*is_epilogue_begin=*/false, /*is_terminal_entry=*/true); |
852 | sequences.push_back(x: std::move(line_seq_up)); |
853 | line_seq_up = LineTable::CreateLineSequenceContainer(); |
854 | }; |
855 | |
856 | LineIterator It(*m_objfile_sp, Record::Func, data.bookmark), |
857 | End(*m_objfile_sp); |
858 | assert(Record::classify(*It) == Record::Func); |
859 | for (++It; It != End; ++It) { |
860 | // Skip INLINE records |
861 | if (Record::classify(Line: *It) == Record::Inline) |
862 | continue; |
863 | |
864 | auto record = LineRecord::parse(Line: *It); |
865 | if (!record) |
866 | break; |
867 | |
868 | record->Address += base; |
869 | |
870 | if (next_addr && *next_addr != record->Address) { |
871 | // Discontiguous entries. Finish off the previous sequence and reset. |
872 | finish_sequence(); |
873 | } |
874 | LineTable::AppendLineEntryToSequence( |
875 | sequence: line_seq_up.get(), file_addr: record->Address, line: record->LineNum, /*column=*/0, |
876 | file_idx: map[record->FileNum], /*is_start_of_statement=*/true, |
877 | /*is_start_of_basic_block=*/false, /*is_prologue_end=*/false, |
878 | /*is_epilogue_begin=*/false, /*is_terminal_entry=*/false); |
879 | next_addr = record->Address + record->Size; |
880 | } |
881 | if (next_addr) |
882 | finish_sequence(); |
883 | data.line_table_up = std::make_unique<LineTable>(args: &cu, args: std::move(sequences)); |
884 | data.support_files = map.translate(cu_spec: cu.GetPrimaryFile(), all_files: *m_files); |
885 | } |
886 | |
887 | void SymbolFileBreakpad::ParseUnwindData() { |
888 | if (m_unwind_data) |
889 | return; |
890 | m_unwind_data.emplace(); |
891 | |
892 | Log *log = GetLog(mask: LLDBLog::Symbols); |
893 | addr_t base = GetBaseFileAddress(); |
894 | if (base == LLDB_INVALID_ADDRESS) { |
895 | LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address " |
896 | "of object file." ); |
897 | } |
898 | |
899 | for (LineIterator It(*m_objfile_sp, Record::StackCFI), End(*m_objfile_sp); |
900 | It != End; ++It) { |
901 | if (auto record = StackCFIRecord::parse(Line: *It)) { |
902 | if (record->Size) |
903 | m_unwind_data->cfi.Append(entry: UnwindMap::Entry( |
904 | base + record->Address, *record->Size, It.GetBookmark())); |
905 | } else |
906 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , *It); |
907 | } |
908 | m_unwind_data->cfi.Sort(); |
909 | |
910 | for (LineIterator It(*m_objfile_sp, Record::StackWin), End(*m_objfile_sp); |
911 | It != End; ++It) { |
912 | if (auto record = StackWinRecord::parse(Line: *It)) { |
913 | m_unwind_data->win.Append(entry: UnwindMap::Entry( |
914 | base + record->RVA, record->CodeSize, It.GetBookmark())); |
915 | } else |
916 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , *It); |
917 | } |
918 | m_unwind_data->win.Sort(); |
919 | } |
920 | |
921 | uint64_t SymbolFileBreakpad::GetDebugInfoSize(bool load_all_debug_info) { |
922 | // Breakpad files are all debug info. |
923 | return m_objfile_sp->GetByteSize(); |
924 | } |
925 | |