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 | Address func_addr(section_sp, address - section_sp->GetFileAddress()); |
255 | // Use the CU's id because every CU has only one function inside. |
256 | func_sp = std::make_shared<Function>( |
257 | args: &comp_unit, args&: id, args: 0, args&: func_name, args: nullptr, args&: func_addr, |
258 | args: AddressRanges{AddressRange(func_addr, record->Size)}); |
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 | blocks.push_back(x: &func.GetBlock(can_create: false)); |
303 | |
304 | size_t blocks_added = 0; |
305 | addr_t func_base = func.GetAddress().GetOffset(); |
306 | CompUnitData &data = m_cu_data->GetEntryRef(i: comp_unit->GetID()).data; |
307 | LineIterator It(*m_objfile_sp, Record::Func, data.bookmark), |
308 | End(*m_objfile_sp); |
309 | ++It; // Skip the FUNC record. |
310 | size_t last_added_nest_level = 0; |
311 | while (It != End && Record::classify(Line: *It) == Record::Inline) { |
312 | if (auto record = InlineRecord::parse(Line: *It)) { |
313 | if (record->InlineNestLevel == 0 || |
314 | record->InlineNestLevel <= last_added_nest_level + 1) { |
315 | last_added_nest_level = record->InlineNestLevel; |
316 | BlockSP block_sp = blocks[record->InlineNestLevel]->CreateChild( |
317 | uid: It.GetBookmark().offset); |
318 | FileSpec callsite_file; |
319 | if (record->CallSiteFileNum < m_files->size()) |
320 | callsite_file = (*m_files)[record->CallSiteFileNum]; |
321 | llvm::StringRef name; |
322 | if (record->OriginNum < m_inline_origins->size()) |
323 | name = (*m_inline_origins)[record->OriginNum]; |
324 | |
325 | Declaration callsite(callsite_file, record->CallSiteLineNum); |
326 | block_sp->SetInlinedFunctionInfo(name: name.str().c_str(), |
327 | /*mangled=*/nullptr, |
328 | /*decl_ptr=*/nullptr, call_decl_ptr: &callsite); |
329 | for (const auto &range : record->Ranges) { |
330 | block_sp->AddRange( |
331 | range: Block::Range(range.first - func_base, range.second)); |
332 | } |
333 | block_sp->FinalizeRanges(); |
334 | |
335 | if (record->InlineNestLevel + 1 >= blocks.size()) { |
336 | blocks.resize(new_size: blocks.size() + 1); |
337 | } |
338 | blocks[record->InlineNestLevel + 1] = block_sp.get(); |
339 | ++blocks_added; |
340 | } |
341 | } |
342 | ++It; |
343 | } |
344 | return blocks_added; |
345 | } |
346 | |
347 | void SymbolFileBreakpad::ParseInlineOriginRecords() { |
348 | if (m_inline_origins) |
349 | return; |
350 | m_inline_origins.emplace(); |
351 | |
352 | Log *log = GetLog(mask: LLDBLog::Symbols); |
353 | for (llvm::StringRef line : lines(section_type: Record::InlineOrigin)) { |
354 | auto record = InlineOriginRecord::parse(Line: line); |
355 | if (!record) { |
356 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , line); |
357 | continue; |
358 | } |
359 | |
360 | if (record->Number >= m_inline_origins->size()) |
361 | m_inline_origins->resize(new_size: record->Number + 1); |
362 | (*m_inline_origins)[record->Number] = record->Name; |
363 | } |
364 | } |
365 | |
366 | uint32_t |
367 | SymbolFileBreakpad::ResolveSymbolContext(const Address &so_addr, |
368 | SymbolContextItem resolve_scope, |
369 | SymbolContext &sc) { |
370 | std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); |
371 | if (!(resolve_scope & (eSymbolContextCompUnit | eSymbolContextLineEntry | |
372 | eSymbolContextFunction | eSymbolContextBlock))) |
373 | return 0; |
374 | |
375 | ParseCUData(); |
376 | uint32_t idx = |
377 | m_cu_data->FindEntryIndexThatContains(addr: so_addr.GetFileAddress()); |
378 | if (idx == UINT32_MAX) |
379 | return 0; |
380 | |
381 | sc.comp_unit = GetCompileUnitAtIndex(idx).get(); |
382 | SymbolContextItem result = eSymbolContextCompUnit; |
383 | if (resolve_scope & eSymbolContextLineEntry) { |
384 | if (sc.comp_unit->GetLineTable()->FindLineEntryByAddress(so_addr, |
385 | line_entry&: sc.line_entry)) { |
386 | result |= eSymbolContextLineEntry; |
387 | } |
388 | } |
389 | |
390 | if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) { |
391 | FunctionSP func_sp = GetOrCreateFunction(comp_unit&: *sc.comp_unit); |
392 | if (func_sp) { |
393 | sc.function = func_sp.get(); |
394 | result |= eSymbolContextFunction; |
395 | if (resolve_scope & eSymbolContextBlock) { |
396 | Block &block = func_sp->GetBlock(can_create: true); |
397 | sc.block = block.FindInnermostBlockByOffset( |
398 | offset: so_addr.GetFileAddress() - |
399 | sc.function->GetAddress().GetFileAddress()); |
400 | if (sc.block) |
401 | result |= eSymbolContextBlock; |
402 | } |
403 | } |
404 | } |
405 | |
406 | return result; |
407 | } |
408 | |
409 | uint32_t SymbolFileBreakpad::ResolveSymbolContext( |
410 | const SourceLocationSpec &src_location_spec, |
411 | lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) { |
412 | std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); |
413 | if (!(resolve_scope & eSymbolContextCompUnit)) |
414 | return 0; |
415 | |
416 | uint32_t old_size = sc_list.GetSize(); |
417 | for (size_t i = 0, size = GetNumCompileUnits(); i < size; ++i) { |
418 | CompileUnit &cu = *GetCompileUnitAtIndex(idx: i); |
419 | cu.ResolveSymbolContext(src_location_spec, resolve_scope, sc_list); |
420 | } |
421 | return sc_list.GetSize() - old_size; |
422 | } |
423 | |
424 | void SymbolFileBreakpad::FindFunctions( |
425 | const Module::LookupInfo &lookup_info, |
426 | const CompilerDeclContext &parent_decl_ctx, bool include_inlines, |
427 | SymbolContextList &sc_list) { |
428 | std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); |
429 | // TODO: Implement this with supported FunctionNameType. |
430 | |
431 | ConstString name = lookup_info.GetLookupName(); |
432 | for (uint32_t i = 0; i < GetNumCompileUnits(); ++i) { |
433 | CompUnitSP cu_sp = GetCompileUnitAtIndex(idx: i); |
434 | FunctionSP func_sp = GetOrCreateFunction(comp_unit&: *cu_sp); |
435 | if (func_sp && name == func_sp->GetNameNoArguments()) { |
436 | SymbolContext sc; |
437 | sc.comp_unit = cu_sp.get(); |
438 | sc.function = func_sp.get(); |
439 | sc.module_sp = func_sp->CalculateSymbolContextModule(); |
440 | sc_list.Append(sc); |
441 | } |
442 | } |
443 | } |
444 | |
445 | void SymbolFileBreakpad::FindFunctions(const RegularExpression ®ex, |
446 | bool include_inlines, |
447 | SymbolContextList &sc_list) { |
448 | // TODO |
449 | } |
450 | |
451 | void SymbolFileBreakpad::AddSymbols(Symtab &symtab) { |
452 | Log *log = GetLog(mask: LLDBLog::Symbols); |
453 | Module &module = *m_objfile_sp->GetModule(); |
454 | addr_t base = GetBaseFileAddress(); |
455 | if (base == LLDB_INVALID_ADDRESS) { |
456 | LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping " |
457 | "symtab population." ); |
458 | return; |
459 | } |
460 | |
461 | const SectionList &list = *module.GetSectionList(); |
462 | llvm::DenseSet<addr_t> found_symbol_addresses; |
463 | std::vector<Symbol> symbols; |
464 | auto add_symbol = [&](addr_t address, std::optional<addr_t> size, |
465 | llvm::StringRef name) { |
466 | address += base; |
467 | SectionSP section_sp = list.FindSectionContainingFileAddress(addr: address); |
468 | if (!section_sp) { |
469 | LLDB_LOG(log, |
470 | "Ignoring symbol {0}, whose address ({1}) is outside of the " |
471 | "object file. Mismatched symbol file?" , |
472 | name, address); |
473 | return; |
474 | } |
475 | // Keep track of what addresses were already added so far and only add |
476 | // the symbol with the first address. |
477 | if (!found_symbol_addresses.insert(V: address).second) |
478 | return; |
479 | symbols.emplace_back( |
480 | /*symID*/ args: 0, args: Mangled(name), args: eSymbolTypeCode, |
481 | /*is_global*/ args: true, /*is_debug*/ args: false, |
482 | /*is_trampoline*/ args: false, /*is_artificial*/ args: false, |
483 | args: AddressRange(section_sp, address - section_sp->GetFileAddress(), |
484 | size.value_or(u: 0)), |
485 | args: size.has_value(), /*contains_linker_annotations*/ args: false, /*flags*/ args: 0); |
486 | }; |
487 | |
488 | for (llvm::StringRef line : lines(section_type: Record::Public)) { |
489 | if (auto record = PublicRecord::parse(Line: line)) |
490 | add_symbol(record->Address, std::nullopt, record->Name); |
491 | else |
492 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , line); |
493 | } |
494 | |
495 | for (Symbol &symbol : symbols) |
496 | symtab.AddSymbol(symbol: std::move(symbol)); |
497 | symtab.Finalize(); |
498 | } |
499 | |
500 | llvm::Expected<lldb::addr_t> |
501 | SymbolFileBreakpad::GetParameterStackSize(Symbol &symbol) { |
502 | ParseUnwindData(); |
503 | if (auto *entry = m_unwind_data->win.FindEntryThatContains( |
504 | addr: symbol.GetAddress().GetFileAddress())) { |
505 | auto record = StackWinRecord::parse( |
506 | Line: *LineIterator(*m_objfile_sp, Record::StackWin, entry->data)); |
507 | assert(record); |
508 | return record->ParameterSize; |
509 | } |
510 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
511 | S: "Parameter size unknown." ); |
512 | } |
513 | |
514 | static std::optional<std::pair<llvm::StringRef, llvm::StringRef>> |
515 | GetRule(llvm::StringRef &unwind_rules) { |
516 | // Unwind rules are of the form |
517 | // register1: expression1 register2: expression2 ... |
518 | // We assume none of the tokens in expression<n> end with a colon. |
519 | |
520 | llvm::StringRef lhs, rest; |
521 | std::tie(args&: lhs, args&: rest) = getToken(Source: unwind_rules); |
522 | if (!lhs.consume_back(Suffix: ":" )) |
523 | return std::nullopt; |
524 | |
525 | // Seek forward to the next register: expression pair |
526 | llvm::StringRef::size_type pos = rest.find(Str: ": " ); |
527 | if (pos == llvm::StringRef::npos) { |
528 | // No pair found, this means the rest of the string is a single expression. |
529 | unwind_rules = llvm::StringRef(); |
530 | return std::make_pair(x&: lhs, y&: rest); |
531 | } |
532 | |
533 | // Go back one token to find the end of the current rule. |
534 | pos = rest.rfind(C: ' ', From: pos); |
535 | if (pos == llvm::StringRef::npos) |
536 | return std::nullopt; |
537 | |
538 | llvm::StringRef rhs = rest.take_front(N: pos); |
539 | unwind_rules = rest.drop_front(N: pos); |
540 | return std::make_pair(x&: lhs, y&: rhs); |
541 | } |
542 | |
543 | static const RegisterInfo * |
544 | ResolveRegister(const llvm::Triple &triple, |
545 | const SymbolFile::RegisterInfoResolver &resolver, |
546 | llvm::StringRef name) { |
547 | if (triple.isX86() || triple.isMIPS()) { |
548 | // X86 and MIPS registers have '$' in front of their register names. Arm and |
549 | // AArch64 don't. |
550 | if (!name.consume_front(Prefix: "$" )) |
551 | return nullptr; |
552 | } |
553 | return resolver.ResolveName(name); |
554 | } |
555 | |
556 | static const RegisterInfo * |
557 | ResolveRegisterOrRA(const llvm::Triple &triple, |
558 | const SymbolFile::RegisterInfoResolver &resolver, |
559 | llvm::StringRef name) { |
560 | if (name == ".ra" ) |
561 | return resolver.ResolveNumber(kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); |
562 | return ResolveRegister(triple, resolver, name); |
563 | } |
564 | |
565 | llvm::ArrayRef<uint8_t> SymbolFileBreakpad::SaveAsDWARF(postfix::Node &node) { |
566 | ArchSpec arch = m_objfile_sp->GetArchitecture(); |
567 | StreamString dwarf(Stream::eBinary, arch.GetAddressByteSize(), |
568 | arch.GetByteOrder()); |
569 | ToDWARF(node, stream&: dwarf); |
570 | uint8_t *saved = m_allocator.Allocate<uint8_t>(Num: dwarf.GetSize()); |
571 | std::memcpy(dest: saved, src: dwarf.GetData(), n: dwarf.GetSize()); |
572 | return {saved, dwarf.GetSize()}; |
573 | } |
574 | |
575 | bool SymbolFileBreakpad::ParseCFIUnwindRow(llvm::StringRef unwind_rules, |
576 | const RegisterInfoResolver &resolver, |
577 | UnwindPlan::Row &row) { |
578 | Log *log = GetLog(mask: LLDBLog::Symbols); |
579 | |
580 | llvm::BumpPtrAllocator node_alloc; |
581 | llvm::Triple triple = m_objfile_sp->GetArchitecture().GetTriple(); |
582 | while (auto rule = GetRule(unwind_rules)) { |
583 | node_alloc.Reset(); |
584 | llvm::StringRef lhs = rule->first; |
585 | postfix::Node *rhs = postfix::ParseOneExpression(expr: rule->second, alloc&: node_alloc); |
586 | if (!rhs) { |
587 | LLDB_LOG(log, "Could not parse `{0}` as unwind rhs." , rule->second); |
588 | return false; |
589 | } |
590 | |
591 | bool success = postfix::ResolveSymbols( |
592 | node&: rhs, replacer: [&](postfix::SymbolNode &symbol) -> postfix::Node * { |
593 | llvm::StringRef name = symbol.GetName(); |
594 | if (name == ".cfa" && lhs != ".cfa" ) |
595 | return postfix::MakeNode<postfix::InitialValueNode>(alloc&: node_alloc); |
596 | |
597 | if (const RegisterInfo *info = |
598 | ResolveRegister(triple, resolver, name)) { |
599 | return postfix::MakeNode<postfix::RegisterNode>( |
600 | alloc&: node_alloc, args: info->kinds[eRegisterKindLLDB]); |
601 | } |
602 | return nullptr; |
603 | }); |
604 | |
605 | if (!success) { |
606 | LLDB_LOG(log, "Resolving symbols in `{0}` failed." , rule->second); |
607 | return false; |
608 | } |
609 | |
610 | llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(node&: *rhs); |
611 | if (lhs == ".cfa" ) { |
612 | row.GetCFAValue().SetIsDWARFExpression(opcodes: saved.data(), len: saved.size()); |
613 | } else if (const RegisterInfo *info = |
614 | ResolveRegisterOrRA(triple, resolver, name: lhs)) { |
615 | UnwindPlan::Row::AbstractRegisterLocation loc; |
616 | loc.SetIsDWARFExpression(opcodes: saved.data(), len: saved.size()); |
617 | row.SetRegisterInfo(reg_num: info->kinds[eRegisterKindLLDB], register_location: loc); |
618 | } else |
619 | LLDB_LOG(log, "Invalid register `{0}` in unwind rule." , lhs); |
620 | } |
621 | if (unwind_rules.empty()) |
622 | return true; |
623 | |
624 | LLDB_LOG(log, "Could not parse `{0}` as an unwind rule." , unwind_rules); |
625 | return false; |
626 | } |
627 | |
628 | UnwindPlanSP |
629 | SymbolFileBreakpad::GetUnwindPlan(const Address &address, |
630 | const RegisterInfoResolver &resolver) { |
631 | ParseUnwindData(); |
632 | if (auto *entry = |
633 | m_unwind_data->cfi.FindEntryThatContains(addr: address.GetFileAddress())) |
634 | return ParseCFIUnwindPlan(bookmark: entry->data, resolver); |
635 | if (auto *entry = |
636 | m_unwind_data->win.FindEntryThatContains(addr: address.GetFileAddress())) |
637 | return ParseWinUnwindPlan(bookmark: entry->data, resolver); |
638 | return nullptr; |
639 | } |
640 | |
641 | UnwindPlanSP |
642 | SymbolFileBreakpad::ParseCFIUnwindPlan(const Bookmark &bookmark, |
643 | const RegisterInfoResolver &resolver) { |
644 | addr_t base = GetBaseFileAddress(); |
645 | if (base == LLDB_INVALID_ADDRESS) |
646 | return nullptr; |
647 | |
648 | LineIterator It(*m_objfile_sp, Record::StackCFI, bookmark), |
649 | End(*m_objfile_sp); |
650 | std::optional<StackCFIRecord> init_record = StackCFIRecord::parse(Line: *It); |
651 | assert(init_record && init_record->Size && |
652 | "Record already parsed successfully in ParseUnwindData!" ); |
653 | |
654 | auto plan_sp = std::make_shared<UnwindPlan>(args: lldb::eRegisterKindLLDB); |
655 | plan_sp->SetSourceName("breakpad STACK CFI" ); |
656 | plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
657 | plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo); |
658 | plan_sp->SetSourcedFromCompiler(eLazyBoolYes); |
659 | plan_sp->SetPlanValidAddressRanges( |
660 | {AddressRange(base + init_record->Address, *init_record->Size, |
661 | m_objfile_sp->GetModule()->GetSectionList())}); |
662 | |
663 | UnwindPlan::Row row; |
664 | if (!ParseCFIUnwindRow(unwind_rules: init_record->UnwindRules, resolver, row)) |
665 | return nullptr; |
666 | plan_sp->AppendRow(row); |
667 | for (++It; It != End; ++It) { |
668 | std::optional<StackCFIRecord> record = StackCFIRecord::parse(Line: *It); |
669 | if (!record) |
670 | return nullptr; |
671 | if (record->Size) |
672 | break; |
673 | |
674 | row.SetOffset(record->Address - init_record->Address); |
675 | if (!ParseCFIUnwindRow(unwind_rules: record->UnwindRules, resolver, row)) |
676 | return nullptr; |
677 | plan_sp->AppendRow(row); |
678 | } |
679 | return plan_sp; |
680 | } |
681 | |
682 | UnwindPlanSP |
683 | SymbolFileBreakpad::ParseWinUnwindPlan(const Bookmark &bookmark, |
684 | const RegisterInfoResolver &resolver) { |
685 | Log *log = GetLog(mask: LLDBLog::Symbols); |
686 | addr_t base = GetBaseFileAddress(); |
687 | if (base == LLDB_INVALID_ADDRESS) |
688 | return nullptr; |
689 | |
690 | LineIterator It(*m_objfile_sp, Record::StackWin, bookmark); |
691 | std::optional<StackWinRecord> record = StackWinRecord::parse(Line: *It); |
692 | assert(record && "Record already parsed successfully in ParseUnwindData!" ); |
693 | |
694 | auto plan_sp = std::make_shared<UnwindPlan>(args: lldb::eRegisterKindLLDB); |
695 | plan_sp->SetSourceName("breakpad STACK WIN" ); |
696 | plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
697 | plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo); |
698 | plan_sp->SetSourcedFromCompiler(eLazyBoolYes); |
699 | plan_sp->SetPlanValidAddressRanges( |
700 | {AddressRange(base + record->RVA, record->CodeSize, |
701 | m_objfile_sp->GetModule()->GetSectionList())}); |
702 | |
703 | UnwindPlan::Row row; |
704 | |
705 | llvm::BumpPtrAllocator node_alloc; |
706 | std::vector<std::pair<llvm::StringRef, postfix::Node *>> program = |
707 | postfix::ParseFPOProgram(prog: record->ProgramString, alloc&: node_alloc); |
708 | |
709 | if (program.empty()) { |
710 | LLDB_LOG(log, "Invalid unwind rule: {0}." , record->ProgramString); |
711 | return nullptr; |
712 | } |
713 | auto it = program.begin(); |
714 | llvm::Triple triple = m_objfile_sp->GetArchitecture().GetTriple(); |
715 | const auto &symbol_resolver = |
716 | [&](postfix::SymbolNode &symbol) -> postfix::Node * { |
717 | llvm::StringRef name = symbol.GetName(); |
718 | for (const auto &rule : llvm::make_range(x: program.begin(), y: it)) { |
719 | if (rule.first == name) |
720 | return rule.second; |
721 | } |
722 | if (const RegisterInfo *info = ResolveRegister(triple, resolver, name)) |
723 | return postfix::MakeNode<postfix::RegisterNode>( |
724 | alloc&: node_alloc, args: info->kinds[eRegisterKindLLDB]); |
725 | return nullptr; |
726 | }; |
727 | |
728 | // We assume the first value will be the CFA. It is usually called T0, but |
729 | // clang will use T1, if it needs to realign the stack. |
730 | auto *symbol = llvm::dyn_cast<postfix::SymbolNode>(Val: it->second); |
731 | if (symbol && symbol->GetName() == ".raSearch" ) { |
732 | row.GetCFAValue().SetRaSearch(record->LocalSize + |
733 | record->SavedRegisterSize); |
734 | } else { |
735 | if (!postfix::ResolveSymbols(node&: it->second, replacer: symbol_resolver)) { |
736 | LLDB_LOG(log, "Resolving symbols in `{0}` failed." , |
737 | record->ProgramString); |
738 | return nullptr; |
739 | } |
740 | llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(node&: *it->second); |
741 | row.GetCFAValue().SetIsDWARFExpression(opcodes: saved.data(), len: saved.size()); |
742 | } |
743 | |
744 | // Replace the node value with InitialValueNode, so that subsequent |
745 | // expressions refer to the CFA value instead of recomputing the whole |
746 | // expression. |
747 | it->second = postfix::MakeNode<postfix::InitialValueNode>(alloc&: node_alloc); |
748 | |
749 | |
750 | // Now process the rest of the assignments. |
751 | for (++it; it != program.end(); ++it) { |
752 | const RegisterInfo *info = ResolveRegister(triple, resolver, name: it->first); |
753 | // It is not an error if the resolution fails because the program may |
754 | // contain temporary variables. |
755 | if (!info) |
756 | continue; |
757 | if (!postfix::ResolveSymbols(node&: it->second, replacer: symbol_resolver)) { |
758 | LLDB_LOG(log, "Resolving symbols in `{0}` failed." , |
759 | record->ProgramString); |
760 | return nullptr; |
761 | } |
762 | |
763 | llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(node&: *it->second); |
764 | UnwindPlan::Row::AbstractRegisterLocation loc; |
765 | loc.SetIsDWARFExpression(opcodes: saved.data(), len: saved.size()); |
766 | row.SetRegisterInfo(reg_num: info->kinds[eRegisterKindLLDB], register_location: loc); |
767 | } |
768 | |
769 | plan_sp->AppendRow(row: std::move(row)); |
770 | return plan_sp; |
771 | } |
772 | |
773 | addr_t SymbolFileBreakpad::GetBaseFileAddress() { |
774 | return m_objfile_sp->GetModule() |
775 | ->GetObjectFile() |
776 | ->GetBaseAddress() |
777 | .GetFileAddress(); |
778 | } |
779 | |
780 | // Parse out all the FILE records from the breakpad file. These will be needed |
781 | // when constructing the support file lists for individual compile units. |
782 | void SymbolFileBreakpad::ParseFileRecords() { |
783 | if (m_files) |
784 | return; |
785 | m_files.emplace(); |
786 | |
787 | Log *log = GetLog(mask: LLDBLog::Symbols); |
788 | for (llvm::StringRef line : lines(section_type: Record::File)) { |
789 | auto record = FileRecord::parse(Line: line); |
790 | if (!record) { |
791 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , line); |
792 | continue; |
793 | } |
794 | |
795 | if (record->Number >= m_files->size()) |
796 | m_files->resize(new_size: record->Number + 1); |
797 | FileSpec::Style style = FileSpec::GuessPathStyle(absolute_path: record->Name) |
798 | .value_or(u: FileSpec::Style::native); |
799 | (*m_files)[record->Number] = FileSpec(record->Name, style); |
800 | } |
801 | } |
802 | |
803 | void SymbolFileBreakpad::ParseCUData() { |
804 | if (m_cu_data) |
805 | return; |
806 | |
807 | m_cu_data.emplace(); |
808 | Log *log = GetLog(mask: LLDBLog::Symbols); |
809 | addr_t base = GetBaseFileAddress(); |
810 | if (base == LLDB_INVALID_ADDRESS) { |
811 | LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address " |
812 | "of object file." ); |
813 | } |
814 | |
815 | // We shall create one compile unit for each FUNC record. So, count the number |
816 | // of FUNC records, and store them in m_cu_data, together with their ranges. |
817 | for (LineIterator It(*m_objfile_sp, Record::Func), End(*m_objfile_sp); |
818 | It != End; ++It) { |
819 | if (auto record = FuncRecord::parse(Line: *It)) { |
820 | m_cu_data->Append(entry: CompUnitMap::Entry(base + record->Address, record->Size, |
821 | CompUnitData(It.GetBookmark()))); |
822 | } else |
823 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , *It); |
824 | } |
825 | m_cu_data->Sort(); |
826 | } |
827 | |
828 | // Construct the list of support files and line table entries for the given |
829 | // compile unit. |
830 | void SymbolFileBreakpad::ParseLineTableAndSupportFiles(CompileUnit &cu, |
831 | CompUnitData &data) { |
832 | addr_t base = GetBaseFileAddress(); |
833 | assert(base != LLDB_INVALID_ADDRESS && |
834 | "How did we create compile units without a base address?" ); |
835 | |
836 | SupportFileMap map; |
837 | std::vector<LineTable::Sequence> sequences; |
838 | LineTable::Sequence sequence; |
839 | std::optional<addr_t> next_addr; |
840 | auto finish_sequence = [&]() { |
841 | LineTable::AppendLineEntryToSequence( |
842 | sequence, file_addr: *next_addr, /*line=*/0, /*column=*/0, |
843 | /*file_idx=*/0, /*is_start_of_statement=*/false, |
844 | /*is_start_of_basic_block=*/false, /*is_prologue_end=*/false, |
845 | /*is_epilogue_begin=*/false, /*is_terminal_entry=*/true); |
846 | sequences.push_back(x: std::move(sequence)); |
847 | }; |
848 | |
849 | LineIterator It(*m_objfile_sp, Record::Func, data.bookmark), |
850 | End(*m_objfile_sp); |
851 | assert(Record::classify(*It) == Record::Func); |
852 | for (++It; It != End; ++It) { |
853 | // Skip INLINE records |
854 | if (Record::classify(Line: *It) == Record::Inline) |
855 | continue; |
856 | |
857 | auto record = LineRecord::parse(Line: *It); |
858 | if (!record) |
859 | break; |
860 | |
861 | record->Address += base; |
862 | |
863 | if (next_addr && *next_addr != record->Address) { |
864 | // Discontiguous entries. Finish off the previous sequence and reset. |
865 | finish_sequence(); |
866 | } |
867 | LineTable::AppendLineEntryToSequence( |
868 | sequence, file_addr: record->Address, line: record->LineNum, /*column=*/0, |
869 | file_idx: map[record->FileNum], /*is_start_of_statement=*/true, |
870 | /*is_start_of_basic_block=*/false, /*is_prologue_end=*/false, |
871 | /*is_epilogue_begin=*/false, /*is_terminal_entry=*/false); |
872 | next_addr = record->Address + record->Size; |
873 | } |
874 | if (next_addr) |
875 | finish_sequence(); |
876 | data.line_table_up = std::make_unique<LineTable>(args: &cu, args: std::move(sequences)); |
877 | data.support_files = map.translate(cu_spec: cu.GetPrimaryFile(), all_files: *m_files); |
878 | } |
879 | |
880 | void SymbolFileBreakpad::ParseUnwindData() { |
881 | if (m_unwind_data) |
882 | return; |
883 | m_unwind_data.emplace(); |
884 | |
885 | Log *log = GetLog(mask: LLDBLog::Symbols); |
886 | addr_t base = GetBaseFileAddress(); |
887 | if (base == LLDB_INVALID_ADDRESS) { |
888 | LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address " |
889 | "of object file." ); |
890 | } |
891 | |
892 | for (LineIterator It(*m_objfile_sp, Record::StackCFI), End(*m_objfile_sp); |
893 | It != End; ++It) { |
894 | if (auto record = StackCFIRecord::parse(Line: *It)) { |
895 | if (record->Size) |
896 | m_unwind_data->cfi.Append(entry: UnwindMap::Entry( |
897 | base + record->Address, *record->Size, It.GetBookmark())); |
898 | } else |
899 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , *It); |
900 | } |
901 | m_unwind_data->cfi.Sort(); |
902 | |
903 | for (LineIterator It(*m_objfile_sp, Record::StackWin), End(*m_objfile_sp); |
904 | It != End; ++It) { |
905 | if (auto record = StackWinRecord::parse(Line: *It)) { |
906 | m_unwind_data->win.Append(entry: UnwindMap::Entry( |
907 | base + record->RVA, record->CodeSize, It.GetBookmark())); |
908 | } else |
909 | LLDB_LOG(log, "Failed to parse: {0}. Skipping record." , *It); |
910 | } |
911 | m_unwind_data->win.Sort(); |
912 | } |
913 | |
914 | uint64_t SymbolFileBreakpad::GetDebugInfoSize(bool load_all_debug_info) { |
915 | // Breakpad files are all debug info. |
916 | return m_objfile_sp->GetByteSize(); |
917 | } |
918 | |