1 //===-- DebugNamesDWARFIndex.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/DWARF/DebugNamesDWARFIndex.h"
10 #include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
11 #include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
12 #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
13 #include "lldb/Utility/RegularExpression.h"
14 #include "lldb/Utility/Stream.h"
15 
16 using namespace lldb_private;
17 using namespace lldb;
18 
19 static llvm::DWARFDataExtractor ToLLVM(const DWARFDataExtractor &data) {
20   return llvm::DWARFDataExtractor(
21       llvm::StringRef(reinterpret_cast<const char *>(data.GetDataStart()),
22                       data.GetByteSize()),
23       data.GetByteOrder() == eByteOrderLittle, data.GetAddressByteSize());
24 }
25 
26 llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>>
27 DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names,
28                              DWARFDataExtractor debug_str,
29                              DWARFDebugInfo *debug_info) {
30   if (!debug_info) {
31     return llvm::make_error<llvm::StringError>("debug info null",
32                                                llvm::inconvertibleErrorCode());
33   }
34   auto index_up =
35       llvm::make_unique<DebugNames>(ToLLVM(debug_names), ToLLVM(debug_str));
36   if (llvm::Error E = index_up->extract())
37     return std::move(E);
38 
39   return std::unique_ptr<DebugNamesDWARFIndex>(new DebugNamesDWARFIndex(
40       module, std::move(index_up), debug_names, debug_str, *debug_info));
41 }
42 
43 llvm::DenseSet<dw_offset_t>
44 DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) {
45   llvm::DenseSet<dw_offset_t> result;
46   for (const DebugNames::NameIndex &ni : debug_names) {
47     for (uint32_t cu = 0; cu < ni.getCUCount(); ++cu)
48       result.insert(ni.getCUOffset(cu));
49   }
50   return result;
51 }
52 
53 llvm::Optional<DIERef>
54 DebugNamesDWARFIndex::ToDIERef(const DebugNames::Entry &entry) {
55   llvm::Optional<uint64_t> cu_offset = entry.getCUOffset();
56   if (!cu_offset)
57     return llvm::None;
58 
59   DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset);
60   if (!cu)
61     return llvm::None;
62 
63   // This initializes the DWO symbol file. It's not possible for
64   // GetDwoSymbolFile to call this automatically because of mutual recursion
65   // between this and DWARFDebugInfoEntry::GetAttributeValue.
66   cu->ExtractUnitDIEIfNeeded();
67   cu = &cu->GetNonSkeletonUnit();
68 
69   if (llvm::Optional<uint64_t> die_offset = entry.getDIEUnitOffset())
70     return DIERef(cu->GetSymbolFileDWARF().GetDwoNum(),
71                   DIERef::Section::DebugInfo, cu->GetOffset() + *die_offset);
72 
73   return llvm::None;
74 }
75 
76 void DebugNamesDWARFIndex::Append(const DebugNames::Entry &entry,
77                                   DIEArray &offsets) {
78   if (llvm::Optional<DIERef> ref = ToDIERef(entry))
79     offsets.push_back(*ref);
80 }
81 
82 void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error,
83                                                const DebugNames::NameIndex &ni,
84                                                llvm::StringRef name) {
85   // Ignore SentinelErrors, log everything else.
86   LLDB_LOG_ERROR(
87       LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS),
88       handleErrors(std::move(error), [](const DebugNames::SentinelError &) {}),
89       "Failed to parse index entries for index at {1:x}, name {2}: {0}",
90       ni.getUnitOffset(), name);
91 }
92 
93 void DebugNamesDWARFIndex::GetGlobalVariables(ConstString basename,
94                                               DIEArray &offsets) {
95   m_fallback.GetGlobalVariables(basename, offsets);
96 
97   for (const DebugNames::Entry &entry :
98        m_debug_names_up->equal_range(basename.GetStringRef())) {
99     if (entry.tag() != DW_TAG_variable)
100       continue;
101 
102     Append(entry, offsets);
103   }
104 }
105 
106 void DebugNamesDWARFIndex::GetGlobalVariables(const RegularExpression &regex,
107                                               DIEArray &offsets) {
108   m_fallback.GetGlobalVariables(regex, offsets);
109 
110   for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
111     for (DebugNames::NameTableEntry nte: ni) {
112       if (!regex.Execute(nte.getString()))
113         continue;
114 
115       uint32_t entry_offset = nte.getEntryOffset();
116       llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
117       for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
118         if (entry_or->tag() != DW_TAG_variable)
119           continue;
120 
121         Append(*entry_or, offsets);
122       }
123       MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
124     }
125   }
126 }
127 
128 void DebugNamesDWARFIndex::GetGlobalVariables(const DWARFUnit &cu,
129                                               DIEArray &offsets) {
130   m_fallback.GetGlobalVariables(cu, offsets);
131 
132   uint64_t cu_offset = cu.GetOffset();
133   for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
134     for (DebugNames::NameTableEntry nte: ni) {
135       uint32_t entry_offset = nte.getEntryOffset();
136       llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
137       for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
138         if (entry_or->tag() != DW_TAG_variable)
139           continue;
140         if (entry_or->getCUOffset() != cu_offset)
141           continue;
142 
143         Append(*entry_or, offsets);
144       }
145       MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
146     }
147   }
148 }
149 
150 void DebugNamesDWARFIndex::GetCompleteObjCClass(ConstString class_name,
151                                                 bool must_be_implementation,
152                                                 DIEArray &offsets) {
153   m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, offsets);
154 
155   // Keep a list of incomplete types as fallback for when we don't find the
156   // complete type.
157   DIEArray incomplete_types;
158 
159   for (const DebugNames::Entry &entry :
160        m_debug_names_up->equal_range(class_name.GetStringRef())) {
161     if (entry.tag() != DW_TAG_structure_type &&
162         entry.tag() != DW_TAG_class_type)
163       continue;
164 
165     llvm::Optional<DIERef> ref = ToDIERef(entry);
166     if (!ref)
167       continue;
168 
169     DWARFUnit *cu = m_debug_info.GetUnit(*ref);
170     if (!cu || !cu->Supports_DW_AT_APPLE_objc_complete_type()) {
171       incomplete_types.push_back(*ref);
172       continue;
173     }
174 
175     // FIXME: We should return DWARFDIEs so we don't have to resolve it twice.
176     DWARFDIE die = m_debug_info.GetDIE(*ref);
177     if (!die)
178       continue;
179 
180     if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) {
181       // If we find the complete version we're done.
182       offsets.push_back(*ref);
183       return;
184     } else {
185       incomplete_types.push_back(*ref);
186     }
187   }
188 
189   offsets.insert(offsets.end(), incomplete_types.begin(),
190                  incomplete_types.end());
191 }
192 
193 void DebugNamesDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) {
194   m_fallback.GetTypes(name, offsets);
195 
196   for (const DebugNames::Entry &entry :
197        m_debug_names_up->equal_range(name.GetStringRef())) {
198     if (isType(entry.tag()))
199       Append(entry, offsets);
200   }
201 }
202 
203 void DebugNamesDWARFIndex::GetTypes(const DWARFDeclContext &context,
204                                     DIEArray &offsets) {
205   m_fallback.GetTypes(context, offsets);
206 
207   for (const DebugNames::Entry &entry :
208        m_debug_names_up->equal_range(context[0].name)) {
209     if (entry.tag() == context[0].tag)
210       Append(entry, offsets);
211   }
212 }
213 
214 void DebugNamesDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {
215   m_fallback.GetNamespaces(name, offsets);
216 
217   for (const DebugNames::Entry &entry :
218        m_debug_names_up->equal_range(name.GetStringRef())) {
219     if (entry.tag() == DW_TAG_namespace)
220       Append(entry, offsets);
221   }
222 }
223 
224 void DebugNamesDWARFIndex::GetFunctions(
225     ConstString name, SymbolFileDWARF &dwarf,
226     const CompilerDeclContext &parent_decl_ctx, uint32_t name_type_mask,
227     std::vector<DWARFDIE> &dies) {
228 
229   std::vector<DWARFDIE> v;
230   m_fallback.GetFunctions(name, dwarf, parent_decl_ctx, name_type_mask, v);
231 
232   for (const DebugNames::Entry &entry :
233        m_debug_names_up->equal_range(name.GetStringRef())) {
234     Tag tag = entry.tag();
235     if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
236       continue;
237 
238     if (llvm::Optional<DIERef> ref = ToDIERef(entry))
239       ProcessFunctionDIE(name.GetStringRef(), *ref, dwarf, parent_decl_ctx,
240                          name_type_mask, v);
241   }
242 
243   std::set<DWARFDebugInfoEntry *> seen;
244   for (DWARFDIE die : v)
245     if (seen.insert(die.GetDIE()).second)
246       dies.push_back(die);
247 }
248 
249 void DebugNamesDWARFIndex::GetFunctions(const RegularExpression &regex,
250                                         DIEArray &offsets) {
251   m_fallback.GetFunctions(regex, offsets);
252 
253   for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
254     for (DebugNames::NameTableEntry nte: ni) {
255       if (!regex.Execute(nte.getString()))
256         continue;
257 
258       uint32_t entry_offset = nte.getEntryOffset();
259       llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
260       for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
261         Tag tag = entry_or->tag();
262         if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
263           continue;
264 
265         Append(*entry_or, offsets);
266       }
267       MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
268     }
269   }
270 }
271 
272 void DebugNamesDWARFIndex::Dump(Stream &s) {
273   m_fallback.Dump(s);
274 
275   std::string data;
276   llvm::raw_string_ostream os(data);
277   m_debug_names_up->dump(os);
278   s.PutCString(os.str());
279 }
280