1 //=== AcceleratorRecordsSaver.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 "AcceleratorRecordsSaver.h"
10 #include "llvm/DWARFLinker/Utils.h"
11 #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
12 #include "llvm/Support/DJB.h"
13 
14 using namespace llvm;
15 using namespace dwarf_linker;
16 using namespace dwarf_linker::parallel;
17 
18 static uint32_t hashFullyQualifiedName(CompileUnit &InputCU, DWARFDie &InputDIE,
19                                        int ChildRecurseDepth = 0) {
20   const char *Name = nullptr;
21   CompileUnit *CU = &InputCU;
22   std::optional<DWARFFormValue> RefVal;
23 
24   if (Error Err = finiteLoop([&]() -> Expected<bool> {
25         if (const char *CurrentName = InputDIE.getName(DINameKind::ShortName))
26           Name = CurrentName;
27 
28         if (!(RefVal = InputDIE.find(dwarf::DW_AT_specification)) &&
29             !(RefVal = InputDIE.find(dwarf::DW_AT_abstract_origin)))
30           return false;
31 
32         if (!RefVal->isFormClass(DWARFFormValue::FC_Reference))
33           return false;
34 
35         std::optional<UnitEntryPairTy> RefDie = CU->resolveDIEReference(
36             *RefVal, ResolveInterCUReferencesMode::Resolve);
37         if (!RefDie)
38           return false;
39 
40         if (!RefDie->DieEntry)
41           return false;
42 
43         CU = RefDie->CU;
44         InputDIE = RefDie->CU->getDIE(RefDie->DieEntry);
45         return true;
46       })) {
47     consumeError(std::move(Err));
48   }
49 
50   if (!Name && InputDIE.getTag() == dwarf::DW_TAG_namespace)
51     Name = "(anonymous namespace)";
52 
53   DWARFDie ParentDie = InputDIE.getParent();
54   if (!ParentDie.isValid() || ParentDie.getTag() == dwarf::DW_TAG_compile_unit)
55     return djbHash(Name ? Name : "", djbHash(ChildRecurseDepth ? "" : "::"));
56 
57   return djbHash(
58       (Name ? Name : ""),
59       djbHash((Name ? "::" : ""),
60               hashFullyQualifiedName(*CU, ParentDie, ++ChildRecurseDepth)));
61 }
62 
63 void AcceleratorRecordsSaver::save(const DWARFDebugInfoEntry *InputDieEntry,
64                                    DIE *OutDIE, AttributesInfo &AttrInfo,
65                                    TypeEntry *TypeEntry) {
66   if (GlobalData.getOptions().AccelTables.empty())
67     return;
68 
69   DWARFDie InputDIE = InUnit.getDIE(InputDieEntry);
70 
71   // Look for short name recursively if short name is not known yet.
72   if (AttrInfo.Name == nullptr)
73     if (const char *ShortName = InputDIE.getShortName())
74       AttrInfo.Name = GlobalData.getStringPool().insert(ShortName).first;
75 
76   switch (InputDieEntry->getTag()) {
77   case dwarf::DW_TAG_array_type:
78   case dwarf::DW_TAG_class_type:
79   case dwarf::DW_TAG_enumeration_type:
80   case dwarf::DW_TAG_pointer_type:
81   case dwarf::DW_TAG_reference_type:
82   case dwarf::DW_TAG_string_type:
83   case dwarf::DW_TAG_structure_type:
84   case dwarf::DW_TAG_subroutine_type:
85   case dwarf::DW_TAG_typedef:
86   case dwarf::DW_TAG_union_type:
87   case dwarf::DW_TAG_ptr_to_member_type:
88   case dwarf::DW_TAG_set_type:
89   case dwarf::DW_TAG_subrange_type:
90   case dwarf::DW_TAG_base_type:
91   case dwarf::DW_TAG_const_type:
92   case dwarf::DW_TAG_constant:
93   case dwarf::DW_TAG_file_type:
94   case dwarf::DW_TAG_namelist:
95   case dwarf::DW_TAG_packed_type:
96   case dwarf::DW_TAG_volatile_type:
97   case dwarf::DW_TAG_restrict_type:
98   case dwarf::DW_TAG_atomic_type:
99   case dwarf::DW_TAG_interface_type:
100   case dwarf::DW_TAG_unspecified_type:
101   case dwarf::DW_TAG_shared_type:
102   case dwarf::DW_TAG_immutable_type:
103   case dwarf::DW_TAG_rvalue_reference_type: {
104     if (!AttrInfo.IsDeclaration && AttrInfo.Name != nullptr &&
105         !AttrInfo.Name->getKey().empty()) {
106       uint32_t Hash = hashFullyQualifiedName(InUnit, InputDIE);
107 
108       uint64_t RuntimeLang =
109           dwarf::toUnsigned(InputDIE.find(dwarf::DW_AT_APPLE_runtime_class))
110               .value_or(0);
111 
112       bool ObjCClassIsImplementation =
113           (RuntimeLang == dwarf::DW_LANG_ObjC ||
114            RuntimeLang == dwarf::DW_LANG_ObjC_plus_plus) &&
115           dwarf::toUnsigned(
116               InputDIE.find(dwarf::DW_AT_APPLE_objc_complete_type))
117               .value_or(0);
118 
119       saveTypeRecord(AttrInfo.Name, OutDIE, InputDieEntry->getTag(), Hash,
120                      ObjCClassIsImplementation, TypeEntry);
121     }
122   } break;
123   case dwarf::DW_TAG_namespace: {
124     if (AttrInfo.Name == nullptr)
125       AttrInfo.Name =
126           GlobalData.getStringPool().insert("(anonymous namespace)").first;
127 
128     saveNamespaceRecord(AttrInfo.Name, OutDIE, InputDieEntry->getTag(),
129                         TypeEntry);
130   } break;
131   case dwarf::DW_TAG_imported_declaration: {
132     if (AttrInfo.Name != nullptr)
133       saveNamespaceRecord(AttrInfo.Name, OutDIE, InputDieEntry->getTag(),
134                           TypeEntry);
135   } break;
136   case dwarf::DW_TAG_compile_unit:
137   case dwarf::DW_TAG_lexical_block: {
138     // Nothing to do.
139   } break;
140   default:
141     if (TypeEntry)
142       // Do not store this kind of accelerator entries for type entries.
143       return;
144 
145     if (AttrInfo.HasLiveAddress || AttrInfo.HasRanges) {
146       if (AttrInfo.Name)
147         saveNameRecord(AttrInfo.Name, OutDIE, InputDieEntry->getTag(),
148                        InputDieEntry->getTag() ==
149                            dwarf::DW_TAG_inlined_subroutine);
150 
151       // Look for mangled name recursively if mangled name is not known yet.
152       if (!AttrInfo.MangledName)
153         if (const char *LinkageName = InputDIE.getLinkageName())
154           AttrInfo.MangledName =
155               GlobalData.getStringPool().insert(LinkageName).first;
156 
157       if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name)
158         saveNameRecord(AttrInfo.MangledName, OutDIE, InputDieEntry->getTag(),
159                        InputDieEntry->getTag() ==
160                            dwarf::DW_TAG_inlined_subroutine);
161 
162       // Strip template parameters from the short name.
163       if (AttrInfo.Name && AttrInfo.MangledName != AttrInfo.Name &&
164           (InputDieEntry->getTag() != dwarf::DW_TAG_inlined_subroutine)) {
165         if (std::optional<StringRef> Name =
166                 StripTemplateParameters(AttrInfo.Name->getKey())) {
167           StringEntry *NameWithoutTemplateParams =
168               GlobalData.getStringPool().insert(*Name).first;
169 
170           saveNameRecord(NameWithoutTemplateParams, OutDIE,
171                          InputDieEntry->getTag(), true);
172         }
173       }
174 
175       if (AttrInfo.Name)
176         saveObjC(InputDieEntry, OutDIE, AttrInfo);
177     }
178     break;
179   }
180 }
181 
182 void AcceleratorRecordsSaver::saveObjC(const DWARFDebugInfoEntry *InputDieEntry,
183                                        DIE *OutDIE, AttributesInfo &AttrInfo) {
184   std::optional<ObjCSelectorNames> Names =
185       getObjCNamesIfSelector(AttrInfo.Name->getKey());
186   if (!Names)
187     return;
188 
189   StringEntry *Selector =
190       GlobalData.getStringPool().insert(Names->Selector).first;
191   saveNameRecord(Selector, OutDIE, InputDieEntry->getTag(), true);
192   StringEntry *ClassName =
193       GlobalData.getStringPool().insert(Names->ClassName).first;
194   saveObjCNameRecord(ClassName, OutDIE, InputDieEntry->getTag());
195   if (Names->ClassNameNoCategory) {
196     StringEntry *ClassNameNoCategory =
197         GlobalData.getStringPool().insert(*Names->ClassNameNoCategory).first;
198     saveObjCNameRecord(ClassNameNoCategory, OutDIE, InputDieEntry->getTag());
199   }
200   if (Names->MethodNameNoCategory) {
201     StringEntry *MethodNameNoCategory =
202         GlobalData.getStringPool().insert(*Names->MethodNameNoCategory).first;
203     saveNameRecord(MethodNameNoCategory, OutDIE, InputDieEntry->getTag(), true);
204   }
205 }
206 
207 void AcceleratorRecordsSaver::saveNameRecord(StringEntry *Name, DIE *OutDIE,
208                                              dwarf::Tag Tag,
209                                              bool AvoidForPubSections) {
210   DwarfUnit::AccelInfo Info;
211 
212   Info.Type = DwarfUnit::AccelType::Name;
213   Info.String = Name;
214   Info.OutOffset = OutDIE->getOffset();
215   Info.Tag = Tag;
216   Info.AvoidForPubSections = AvoidForPubSections;
217 
218   OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
219 }
220 void AcceleratorRecordsSaver::saveNamespaceRecord(StringEntry *Name,
221                                                   DIE *OutDIE, dwarf::Tag Tag,
222                                                   TypeEntry *TypeEntry) {
223   if (OutUnit.isCompileUnit()) {
224     assert(TypeEntry == nullptr);
225     DwarfUnit::AccelInfo Info;
226 
227     Info.Type = DwarfUnit::AccelType::Namespace;
228     Info.String = Name;
229     Info.OutOffset = OutDIE->getOffset();
230     Info.Tag = Tag;
231 
232     OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
233     return;
234   }
235 
236   assert(TypeEntry != nullptr);
237   TypeUnit::TypeUnitAccelInfo Info;
238   Info.Type = DwarfUnit::AccelType::Namespace;
239   Info.String = Name;
240   Info.OutOffset = 0xbaddef;
241   Info.Tag = Tag;
242   Info.OutDIE = OutDIE;
243   Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
244 
245   OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
246 }
247 
248 void AcceleratorRecordsSaver::saveObjCNameRecord(StringEntry *Name, DIE *OutDIE,
249                                                  dwarf::Tag Tag) {
250   DwarfUnit::AccelInfo Info;
251 
252   Info.Type = DwarfUnit::AccelType::ObjC;
253   Info.String = Name;
254   Info.OutOffset = OutDIE->getOffset();
255   Info.Tag = Tag;
256   Info.AvoidForPubSections = true;
257 
258   OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
259 }
260 
261 void AcceleratorRecordsSaver::saveTypeRecord(StringEntry *Name, DIE *OutDIE,
262                                              dwarf::Tag Tag,
263                                              uint32_t QualifiedNameHash,
264                                              bool ObjcClassImplementation,
265                                              TypeEntry *TypeEntry) {
266   if (OutUnit.isCompileUnit()) {
267     assert(TypeEntry == nullptr);
268     DwarfUnit::AccelInfo Info;
269 
270     Info.Type = DwarfUnit::AccelType::Type;
271     Info.String = Name;
272     Info.OutOffset = OutDIE->getOffset();
273     Info.Tag = Tag;
274     Info.QualifiedNameHash = QualifiedNameHash;
275     Info.ObjcClassImplementation = ObjcClassImplementation;
276 
277     OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
278     return;
279   }
280 
281   assert(TypeEntry != nullptr);
282   TypeUnit::TypeUnitAccelInfo Info;
283 
284   Info.Type = DwarfUnit::AccelType::Type;
285   Info.String = Name;
286   Info.OutOffset = 0xbaddef;
287   Info.Tag = Tag;
288   Info.QualifiedNameHash = QualifiedNameHash;
289   Info.ObjcClassImplementation = ObjcClassImplementation;
290   Info.OutDIE = OutDIE;
291   Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
292   OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
293 }
294