1 | //===- DWARFLinkerCompileUnit.h ---------------------------------*- 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 | #ifndef LLVM_DWARFLINKER_CLASSIC_DWARFLINKERCOMPILEUNIT_H |
10 | #define LLVM_DWARFLINKER_CLASSIC_DWARFLINKERCOMPILEUNIT_H |
11 | |
12 | #include "llvm/ADT/AddressRanges.h" |
13 | #include "llvm/ADT/DenseMap.h" |
14 | #include "llvm/CodeGen/DIE.h" |
15 | #include "llvm/DebugInfo/DWARF/DWARFUnit.h" |
16 | #include <optional> |
17 | |
18 | namespace llvm { |
19 | namespace dwarf_linker { |
20 | namespace classic { |
21 | |
22 | class DeclContext; |
23 | |
24 | /// Mapped value in the address map is the offset to apply to the |
25 | /// linked address. |
26 | using RangesTy = AddressRangesMap; |
27 | |
28 | // This structure keeps patch for the attribute and, optionally, |
29 | // the value of relocation which should be applied. Currently, |
30 | // only location attribute needs to have relocation: either to the |
31 | // function ranges if location attribute is of type 'loclist', |
32 | // either to the operand of DW_OP_addr/DW_OP_addrx if location attribute |
33 | // is of type 'exprloc'. |
34 | // ASSUMPTION: Location attributes of 'loclist' type containing 'exprloc' |
35 | // with address expression operands are not supported yet. |
36 | struct PatchLocation { |
37 | DIE::value_iterator I; |
38 | int64_t RelocAdjustment = 0; |
39 | |
40 | PatchLocation() = default; |
41 | PatchLocation(DIE::value_iterator I) : I(I) {} |
42 | PatchLocation(DIE::value_iterator I, int64_t Reloc) |
43 | : I(I), RelocAdjustment(Reloc) {} |
44 | |
45 | void set(uint64_t New) const { |
46 | assert(I); |
47 | const auto &Old = *I; |
48 | assert(Old.getType() == DIEValue::isInteger); |
49 | *I = DIEValue(Old.getAttribute(), Old.getForm(), DIEInteger(New)); |
50 | } |
51 | |
52 | uint64_t get() const { |
53 | assert(I); |
54 | return I->getDIEInteger().getValue(); |
55 | } |
56 | }; |
57 | |
58 | using RngListAttributesTy = SmallVector<PatchLocation>; |
59 | using LocListAttributesTy = SmallVector<PatchLocation>; |
60 | |
61 | /// Stores all information relating to a compile unit, be it in its original |
62 | /// instance in the object file to its brand new cloned and generated DIE tree. |
63 | class CompileUnit { |
64 | public: |
65 | /// Information gathered about a DIE in the object file. |
66 | struct DIEInfo { |
67 | /// Address offset to apply to the described entity. |
68 | int64_t AddrAdjust; |
69 | |
70 | /// ODR Declaration context. |
71 | DeclContext *Ctxt; |
72 | |
73 | /// Cloned version of that DIE. |
74 | DIE *Clone; |
75 | |
76 | /// The index of this DIE's parent. |
77 | uint32_t ParentIdx; |
78 | |
79 | /// Is the DIE part of the linked output? |
80 | bool Keep : 1; |
81 | |
82 | /// Was this DIE's entity found in the map? |
83 | bool InDebugMap : 1; |
84 | |
85 | /// Is this a pure forward declaration we can strip? |
86 | bool Prune : 1; |
87 | |
88 | /// Does DIE transitively refer an incomplete decl? |
89 | bool Incomplete : 1; |
90 | |
91 | /// Is DIE in the clang module scope? |
92 | bool InModuleScope : 1; |
93 | |
94 | /// Is ODR marking done? |
95 | bool ODRMarkingDone : 1; |
96 | |
97 | /// Is this a reference to a DIE that hasn't been cloned yet? |
98 | bool UnclonedReference : 1; |
99 | |
100 | /// Is this a variable with a location attribute referencing address? |
101 | bool HasLocationExpressionAddr : 1; |
102 | |
103 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
104 | LLVM_DUMP_METHOD void dump(); |
105 | #endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
106 | }; |
107 | |
108 | CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR, |
109 | StringRef ClangModuleName) |
110 | : OrigUnit(OrigUnit), ID(ID), ClangModuleName(ClangModuleName) { |
111 | Info.resize(new_size: OrigUnit.getNumDIEs()); |
112 | |
113 | auto CUDie = OrigUnit.getUnitDIE(ExtractUnitDIEOnly: false); |
114 | if (!CUDie) { |
115 | HasODR = false; |
116 | return; |
117 | } |
118 | if (auto Lang = dwarf::toUnsigned(V: CUDie.find(Attr: dwarf::DW_AT_language))) |
119 | HasODR = CanUseODR && (*Lang == dwarf::DW_LANG_C_plus_plus || |
120 | *Lang == dwarf::DW_LANG_C_plus_plus_03 || |
121 | *Lang == dwarf::DW_LANG_C_plus_plus_11 || |
122 | *Lang == dwarf::DW_LANG_C_plus_plus_14 || |
123 | *Lang == dwarf::DW_LANG_ObjC_plus_plus); |
124 | else |
125 | HasODR = false; |
126 | } |
127 | |
128 | DWARFUnit &getOrigUnit() const { return OrigUnit; } |
129 | |
130 | unsigned getUniqueID() const { return ID; } |
131 | |
132 | void createOutputDIE() { NewUnit.emplace(args: OrigUnit.getUnitDIE().getTag()); } |
133 | |
134 | DIE *getOutputUnitDIE() const { |
135 | if (NewUnit) |
136 | return &const_cast<BasicDIEUnit &>(*NewUnit).getUnitDie(); |
137 | return nullptr; |
138 | } |
139 | |
140 | bool hasODR() const { return HasODR; } |
141 | bool isClangModule() const { return !ClangModuleName.empty(); } |
142 | uint16_t getLanguage(); |
143 | /// Return the DW_AT_LLVM_sysroot of the compile unit or an empty StringRef. |
144 | StringRef getSysRoot(); |
145 | |
146 | const std::string &getClangModuleName() const { return ClangModuleName; } |
147 | |
148 | DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } |
149 | const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; } |
150 | |
151 | DIEInfo &getInfo(const DWARFDie &Die) { |
152 | unsigned Idx = getOrigUnit().getDIEIndex(D: Die); |
153 | return Info[Idx]; |
154 | } |
155 | |
156 | uint64_t getStartOffset() const { return StartOffset; } |
157 | uint64_t getNextUnitOffset() const { return NextUnitOffset; } |
158 | void setStartOffset(uint64_t DebugInfoSize) { StartOffset = DebugInfoSize; } |
159 | |
160 | std::optional<uint64_t> getLowPc() const { return LowPc; } |
161 | uint64_t getHighPc() const { return HighPc; } |
162 | bool hasLabelAt(uint64_t Addr) const { return Labels.count(Val: Addr); } |
163 | |
164 | const RangesTy &getFunctionRanges() const { return Ranges; } |
165 | |
166 | const RngListAttributesTy &getRangesAttributes() { return RangeAttributes; } |
167 | |
168 | std::optional<PatchLocation> getUnitRangesAttribute() const { |
169 | return UnitRangeAttribute; |
170 | } |
171 | |
172 | const LocListAttributesTy &getLocationAttributes() const { |
173 | return LocationAttributes; |
174 | } |
175 | |
176 | /// Mark every DIE in this unit as kept. This function also |
177 | /// marks variables as InDebugMap so that they appear in the |
178 | /// reconstructed accelerator tables. |
179 | void markEverythingAsKept(); |
180 | |
181 | /// Compute the end offset for this unit. Must be called after the CU's DIEs |
182 | /// have been cloned. \returns the next unit offset (which is also the |
183 | /// current debug_info section size). |
184 | uint64_t computeNextUnitOffset(uint16_t DwarfVersion); |
185 | |
186 | /// Keep track of a forward reference to DIE \p Die in \p RefUnit by \p |
187 | /// Attr. The attribute should be fixed up later to point to the absolute |
188 | /// offset of \p Die in the debug_info section or to the canonical offset of |
189 | /// \p Ctxt if it is non-null. |
190 | void noteForwardReference(DIE *Die, const CompileUnit *RefUnit, |
191 | DeclContext *Ctxt, PatchLocation Attr); |
192 | |
193 | /// Apply all fixups recorded by noteForwardReference(). |
194 | void fixupForwardReferences(); |
195 | |
196 | /// Add the low_pc of a label that is relocated by applying |
197 | /// offset \p PCOffset. |
198 | void addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset); |
199 | |
200 | /// Add a function range [\p LowPC, \p HighPC) that is relocated by applying |
201 | /// offset \p PCOffset. |
202 | void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset); |
203 | |
204 | /// Keep track of a DW_AT_range attribute that we will need to patch up later. |
205 | void noteRangeAttribute(const DIE &Die, PatchLocation Attr); |
206 | |
207 | /// Keep track of a location attribute pointing to a location list in the |
208 | /// debug_loc section. |
209 | void noteLocationAttribute(PatchLocation Attr); |
210 | |
211 | /// Add a name accelerator entry for \a Die with \a Name. |
212 | void addNamespaceAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name); |
213 | |
214 | /// Add a name accelerator entry for \a Die with \a Name. |
215 | void addNameAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name, |
216 | bool SkipPubnamesSection = false); |
217 | |
218 | /// Add various accelerator entries for \p Die with \p Name which is stored |
219 | /// in the string table at \p Offset. \p Name must be an Objective-C |
220 | /// selector. |
221 | void addObjCAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name, |
222 | bool SkipPubnamesSection = false); |
223 | |
224 | /// Add a type accelerator entry for \p Die with \p Name which is stored in |
225 | /// the string table at \p Offset. |
226 | void addTypeAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name, |
227 | bool ObjcClassImplementation, |
228 | uint32_t QualifiedNameHash); |
229 | |
230 | struct AccelInfo { |
231 | /// Name of the entry. |
232 | DwarfStringPoolEntryRef Name; |
233 | |
234 | /// DIE this entry describes. |
235 | const DIE *Die; |
236 | |
237 | /// Hash of the fully qualified name. |
238 | uint32_t QualifiedNameHash; |
239 | |
240 | /// Emit this entry only in the apple_* sections. |
241 | bool SkipPubSection; |
242 | |
243 | /// Is this an ObjC class implementation? |
244 | bool ObjcClassImplementation; |
245 | |
246 | AccelInfo(DwarfStringPoolEntryRef Name, const DIE *Die, |
247 | bool SkipPubSection = false) |
248 | : Name(Name), Die(Die), SkipPubSection(SkipPubSection) {} |
249 | |
250 | AccelInfo(DwarfStringPoolEntryRef Name, const DIE *Die, |
251 | uint32_t QualifiedNameHash, bool ObjCClassIsImplementation) |
252 | : Name(Name), Die(Die), QualifiedNameHash(QualifiedNameHash), |
253 | SkipPubSection(false), |
254 | ObjcClassImplementation(ObjCClassIsImplementation) {} |
255 | }; |
256 | |
257 | const std::vector<AccelInfo> &getPubnames() const { return Pubnames; } |
258 | const std::vector<AccelInfo> &getPubtypes() const { return Pubtypes; } |
259 | const std::vector<AccelInfo> &getNamespaces() const { return Namespaces; } |
260 | const std::vector<AccelInfo> &getObjC() const { return ObjC; } |
261 | |
262 | MCSymbol *getLabelBegin() { return LabelBegin; } |
263 | void setLabelBegin(MCSymbol *S) { LabelBegin = S; } |
264 | |
265 | private: |
266 | DWARFUnit &OrigUnit; |
267 | unsigned ID; |
268 | std::vector<DIEInfo> Info; ///< DIE info indexed by DIE index. |
269 | std::optional<BasicDIEUnit> NewUnit; |
270 | MCSymbol *LabelBegin = nullptr; |
271 | |
272 | uint64_t StartOffset; |
273 | uint64_t NextUnitOffset; |
274 | |
275 | std::optional<uint64_t> LowPc; |
276 | uint64_t HighPc = 0; |
277 | |
278 | /// A list of attributes to fixup with the absolute offset of |
279 | /// a DIE in the debug_info section. |
280 | /// |
281 | /// The offsets for the attributes in this array couldn't be set while |
282 | /// cloning because for cross-cu forward references the target DIE's offset |
283 | /// isn't known you emit the reference attribute. |
284 | std::vector< |
285 | std::tuple<DIE *, const CompileUnit *, DeclContext *, PatchLocation>> |
286 | ForwardDIEReferences; |
287 | |
288 | /// The ranges in that map are the PC ranges for functions in this unit, |
289 | /// associated with the PC offset to apply to the addresses to get |
290 | /// the linked address. |
291 | RangesTy Ranges; |
292 | |
293 | /// The DW_AT_low_pc of each DW_TAG_label. |
294 | SmallDenseMap<uint64_t, uint64_t, 1> Labels; |
295 | |
296 | /// 'rnglist'(DW_AT_ranges, DW_AT_start_scope) attributes to patch after |
297 | /// we have gathered all the unit's function addresses. |
298 | /// @{ |
299 | RngListAttributesTy RangeAttributes; |
300 | std::optional<PatchLocation> UnitRangeAttribute; |
301 | /// @} |
302 | |
303 | /// Location attributes that need to be transferred from the |
304 | /// original debug_loc section to the linked one. They are stored |
305 | /// along with the PC offset that is to be applied to their |
306 | /// function's address or to be applied to address operands of |
307 | /// location expression. |
308 | LocListAttributesTy LocationAttributes; |
309 | |
310 | /// Accelerator entries for the unit, both for the pub* |
311 | /// sections and the apple* ones. |
312 | /// @{ |
313 | std::vector<AccelInfo> Pubnames; |
314 | std::vector<AccelInfo> Pubtypes; |
315 | std::vector<AccelInfo> Namespaces; |
316 | std::vector<AccelInfo> ObjC; |
317 | /// @} |
318 | |
319 | /// Is this unit subject to the ODR rule? |
320 | bool HasODR; |
321 | |
322 | /// The DW_AT_language of this unit. |
323 | uint16_t Language = 0; |
324 | |
325 | /// The DW_AT_LLVM_sysroot of this unit. |
326 | std::string SysRoot; |
327 | |
328 | /// If this is a Clang module, this holds the module's name. |
329 | std::string ClangModuleName; |
330 | }; |
331 | |
332 | } // end of namespace classic |
333 | } // end of namespace dwarf_linker |
334 | } // end of namespace llvm |
335 | |
336 | #endif // LLVM_DWARFLINKER_CLASSIC_DWARFLINKERCOMPILEUNIT_H |
337 | |