1 | //===- bolt/Rewrite/DWARFRewriter.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 BOLT_REWRITE_DWARF_REWRITER_H |
10 | #define BOLT_REWRITE_DWARF_REWRITER_H |
11 | |
12 | #include "bolt/Core/DIEBuilder.h" |
13 | #include "bolt/Core/DebugData.h" |
14 | #include "bolt/Core/DebugNames.h" |
15 | #include "llvm/ADT/StringRef.h" |
16 | #include "llvm/CodeGen/DIE.h" |
17 | #include "llvm/DWP/DWP.h" |
18 | #include "llvm/MC/MCAsmLayout.h" |
19 | #include "llvm/MC/MCContext.h" |
20 | #include "llvm/Support/ToolOutputFile.h" |
21 | #include <cstdint> |
22 | #include <memory> |
23 | #include <mutex> |
24 | #include <optional> |
25 | #include <unordered_map> |
26 | #include <vector> |
27 | |
28 | namespace llvm { |
29 | |
30 | namespace bolt { |
31 | |
32 | class BinaryContext; |
33 | |
34 | class DWARFRewriter { |
35 | public: |
36 | DWARFRewriter() = delete; |
37 | /// Contains information about TU so we can write out correct entries in GDB |
38 | /// index. |
39 | struct GDBIndexTUEntry { |
40 | uint64_t UnitOffset; |
41 | uint64_t TypeHash; |
42 | uint64_t TypeDIERelativeOffset; |
43 | }; |
44 | /// Contains information for CU or TU so we can output correct {cu, tu}-index. |
45 | struct UnitMeta { |
46 | uint64_t Offset; |
47 | uint64_t Length; |
48 | uint64_t TUHash; |
49 | }; |
50 | using UnitMetaVectorType = std::vector<UnitMeta>; |
51 | |
52 | private: |
53 | BinaryContext &BC; |
54 | |
55 | std::mutex DWARFRewriterMutex; |
56 | |
57 | /// Stores and serializes information that will be put into the |
58 | /// .debug_ranges DWARF section. |
59 | std::unique_ptr<DebugRangesSectionWriter> LegacyRangesSectionWriter; |
60 | |
61 | /// Stores and serializes information that will be put into the |
62 | /// .debug_rnglists DWARF section. |
63 | std::unique_ptr<DebugRangeListsSectionWriter> RangeListsSectionWriter; |
64 | |
65 | /// Stores and serializes information that will be put into the |
66 | /// .debug_aranges DWARF section. |
67 | std::unique_ptr<DebugARangesSectionWriter> ARangesSectionWriter; |
68 | |
69 | /// Stores and serializes information that will be put into the |
70 | /// .debug_addr DWARF section. |
71 | std::unique_ptr<DebugAddrWriter> AddrWriter; |
72 | |
73 | /// Stores and serializes information that will be put in to the |
74 | /// .debug_addr DWARF section. |
75 | /// Does not do de-duplication. |
76 | std::unique_ptr<DebugStrWriter> StrWriter; |
77 | |
78 | /// Stores and serializes information that will be put in to the |
79 | /// .debug_str_offsets DWARF section. |
80 | std::unique_ptr<DebugStrOffsetsWriter> StrOffstsWriter; |
81 | |
82 | using LocWriters = std::map<uint64_t, std::unique_ptr<DebugLocWriter>>; |
83 | /// Use a separate location list writer for each compilation unit |
84 | LocWriters LocListWritersByCU; |
85 | |
86 | using RangeListsDWOWriers = |
87 | std::unordered_map<uint64_t, |
88 | std::unique_ptr<DebugRangeListsSectionWriter>>; |
89 | /// Store Rangelists writer for each DWO CU. |
90 | RangeListsDWOWriers RangeListsWritersByCU; |
91 | |
92 | std::mutex LocListDebugInfoPatchesMutex; |
93 | |
94 | /// Dwo id specific its RangesBase. |
95 | std::unordered_map<uint64_t, uint64_t> DwoRangesBase; |
96 | |
97 | std::unordered_map<DWARFUnit *, uint64_t> LineTablePatchMap; |
98 | std::unordered_map<const DWARFUnit *, uint64_t> TypeUnitRelocMap; |
99 | |
100 | /// Entries for GDB Index Types CU List |
101 | using GDBIndexTUEntryType = std::vector<GDBIndexTUEntry>; |
102 | GDBIndexTUEntryType GDBIndexTUEntryVector; |
103 | |
104 | /// DWARFLegacy is all DWARF versions before DWARF 5. |
105 | enum class DWARFVersion { DWARFLegacy, DWARF5 }; |
106 | |
107 | /// Used to track last CU offset for GDB Index. |
108 | uint32_t CUOffset{0}; |
109 | |
110 | /// Update debug info for all DIEs in \p Unit. |
111 | void updateUnitDebugInfo(DWARFUnit &Unit, DIEBuilder &DIEBldr, |
112 | DebugLocWriter &DebugLocWriter, |
113 | DebugRangesSectionWriter &RangesSectionWriter, |
114 | std::optional<uint64_t> RangesBase = std::nullopt); |
115 | |
116 | /// Patches the binary for an object's address ranges to be updated. |
117 | /// The object can be anything that has associated address ranges via either |
118 | /// DW_AT_low/high_pc or DW_AT_ranges (i.e. functions, lexical blocks, etc). |
119 | /// \p DebugRangesOffset is the offset in .debug_ranges of the object's |
120 | /// new address ranges in the output binary. |
121 | /// \p Unit Compile unit the object belongs to. |
122 | /// \p DIE is the object's DIE in the input binary. |
123 | /// \p RangesBase if present, update \p DIE to use DW_AT_GNU_ranges_base |
124 | /// attribute. |
125 | void updateDWARFObjectAddressRanges( |
126 | DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die, |
127 | uint64_t DebugRangesOffset, |
128 | std::optional<uint64_t> RangesBase = std::nullopt); |
129 | |
130 | std::unique_ptr<DebugBufferVector> |
131 | makeFinalLocListsSection(DWARFVersion Version); |
132 | |
133 | /// Finalize type sections in the main binary. |
134 | CUOffsetMap finalizeTypeSections(DIEBuilder &DIEBlder, DIEStreamer &Streamer); |
135 | |
136 | /// Process and write out CUs that are passsed in. |
137 | void finalizeCompileUnits(DIEBuilder &DIEBlder, DIEStreamer &Streamer, |
138 | CUOffsetMap &CUMap, |
139 | const std::list<DWARFUnit *> &CUs); |
140 | |
141 | /// Finalize debug sections in the main binary. |
142 | void finalizeDebugSections(DIEBuilder &DIEBlder, |
143 | DWARF5AcceleratorTable &DebugNamesTable, |
144 | DIEStreamer &Streamer, raw_svector_ostream &ObjOS, |
145 | CUOffsetMap &CUMap); |
146 | |
147 | /// Patches the binary for DWARF address ranges (e.g. in functions and lexical |
148 | /// blocks) to be updated. |
149 | void updateDebugAddressRanges(); |
150 | |
151 | /// Rewrite .gdb_index section if present. |
152 | void updateGdbIndexSection(CUOffsetMap &CUMap, uint32_t NumCUs); |
153 | |
154 | /// DWARFDie contains a pointer to a DIE and hence gets invalidated once the |
155 | /// embedded DIE is destroyed. This wrapper class stores a DIE internally and |
156 | /// could be cast to a DWARFDie that is valid even after the initial DIE is |
157 | /// destroyed. |
158 | struct DWARFDieWrapper { |
159 | DWARFUnit *Unit; |
160 | DWARFDebugInfoEntry DIE; |
161 | |
162 | DWARFDieWrapper(DWARFUnit *Unit, DWARFDebugInfoEntry DIE) |
163 | : Unit(Unit), DIE(DIE) {} |
164 | |
165 | DWARFDieWrapper(DWARFDie &Die) |
166 | : Unit(Die.getDwarfUnit()), DIE(*Die.getDebugInfoEntry()) {} |
167 | |
168 | operator DWARFDie() { return DWARFDie(Unit, &DIE); } |
169 | }; |
170 | |
171 | /// Update \p DIE that was using DW_AT_(low|high)_pc with DW_AT_ranges offset. |
172 | /// Updates to the DIE should be synced with abbreviation updates using the |
173 | /// function above. |
174 | void convertToRangesPatchDebugInfo( |
175 | DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die, |
176 | uint64_t RangesSectionOffset, DIEValue &LowPCAttrInfo, |
177 | DIEValue &HighPCAttrInfo, |
178 | std::optional<uint64_t> RangesBase = std::nullopt); |
179 | |
180 | /// Adds a \p Str to .debug_str section. |
181 | /// Uses \p AttrInfoVal to either update entry in a DIE for legacy DWARF using |
182 | /// \p DebugInfoPatcher, or for DWARF5 update an index in .debug_str_offsets |
183 | /// for this contribution of \p Unit. |
184 | void addStringHelper(DIEBuilder &DIEBldr, DIE &Die, const DWARFUnit &Unit, |
185 | DIEValue &DIEAttrInfo, StringRef Str); |
186 | |
187 | public: |
188 | DWARFRewriter(BinaryContext &BC) : BC(BC) {} |
189 | |
190 | /// Main function for updating the DWARF debug info. |
191 | void updateDebugInfo(); |
192 | |
193 | /// Update stmt_list for CUs based on the new .debug_line \p Layout. |
194 | void updateLineTableOffsets(const MCAsmLayout &Layout); |
195 | |
196 | uint64_t getDwoRangesBase(uint64_t DWOId) { return DwoRangesBase[DWOId]; } |
197 | |
198 | void setDwoRangesBase(uint64_t DWOId, uint64_t RangesBase) { |
199 | DwoRangesBase[DWOId] = RangesBase; |
200 | } |
201 | |
202 | /// Adds an GDBIndexTUEntry if .gdb_index seciton exists. |
203 | void addGDBTypeUnitEntry(const GDBIndexTUEntry &&Entry); |
204 | |
205 | /// Returns all entries needed for Types CU list |
206 | const GDBIndexTUEntryType &getGDBIndexTUEntryVector() const { |
207 | return GDBIndexTUEntryVector; |
208 | } |
209 | |
210 | using OverriddenSectionsMap = std::unordered_map<DWARFSectionKind, StringRef>; |
211 | /// Output .dwo files. |
212 | void writeDWOFiles(DWARFUnit &, const OverriddenSectionsMap &, |
213 | const std::string &, DebugLocWriter &); |
214 | using KnownSectionsEntry = std::pair<MCSection *, DWARFSectionKind>; |
215 | struct DWPState { |
216 | std::unique_ptr<ToolOutputFile> Out; |
217 | std::unique_ptr<BinaryContext> TmpBC; |
218 | std::unique_ptr<MCStreamer> Streamer; |
219 | std::unique_ptr<DWPStringPool> Strings; |
220 | const MCObjectFileInfo *MCOFI = nullptr; |
221 | const DWARFUnitIndex *CUIndex = nullptr; |
222 | std::deque<SmallString<32>> UncompressedSections; |
223 | MapVector<uint64_t, UnitIndexEntry> IndexEntries; |
224 | MapVector<uint64_t, UnitIndexEntry> TypeIndexEntries; |
225 | StringMap<KnownSectionsEntry> KnownSections; |
226 | uint32_t ContributionOffsets[8] = {}; |
227 | uint32_t IndexVersion = 2; |
228 | uint64_t DebugInfoSize = 0; |
229 | uint16_t Version = 0; |
230 | bool IsDWP = false; |
231 | }; |
232 | /// Init .dwp file |
233 | void initDWPState(DWPState &); |
234 | |
235 | /// Write out .dwp File |
236 | void finalizeDWP(DWPState &); |
237 | |
238 | /// add content of dwo to .dwp file. |
239 | void updateDWP(DWARFUnit &, const OverriddenSectionsMap &, const UnitMeta &, |
240 | UnitMetaVectorType &, DWPState &, DebugLocWriter &); |
241 | }; |
242 | |
243 | } // namespace bolt |
244 | } // namespace llvm |
245 | |
246 | #endif |
247 | |