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/DebugData.h"
13 #include "llvm/MC/MCAsmLayout.h"
14 #include <cstdint>
15 #include <memory>
16 #include <mutex>
17 #include <set>
18 #include <unordered_map>
19 #include <unordered_set>
20 #include <vector>
21 
22 namespace llvm {
23 
24 namespace bolt {
25 
26 class BinaryContext;
27 
28 class DWARFRewriter {
29 public:
30   DWARFRewriter() = delete;
31   using DebugTypesSignaturesPerCUMap =
32       std::unordered_map<uint64_t, std::unordered_set<uint64_t>>;
33 
34 private:
35   BinaryContext &BC;
36 
37   std::mutex DebugInfoPatcherMutex;
38 
39   /// Stores and serializes information that will be put into the
40   /// .debug_ranges DWARF section.
41   std::unique_ptr<DebugRangesSectionWriter> LegacyRangesSectionWriter;
42 
43   /// Stores and serializes information that will be put into the
44   /// .debug_rnglists DWARF section.
45   std::unique_ptr<DebugRangeListsSectionWriter> RangeListsSectionWriter;
46 
47   /// Stores and serializes information that will be put into the
48   /// .debug_aranges DWARF section.
49   std::unique_ptr<DebugARangesSectionWriter> ARangesSectionWriter;
50 
51   /// Stores and serializes information that will be put into the
52   /// .debug_addr DWARF section.
53   std::unique_ptr<DebugAddrWriter> AddrWriter;
54 
55   /// Stores and serializes information that will be put in to the
56   /// .debug_addr DWARF section.
57   /// Does not do de-duplication.
58   std::unique_ptr<DebugStrWriter> StrWriter;
59 
60   /// Stores and serializes information that will be put in to the
61   /// .debug_str_offsets DWARF section.
62   std::unique_ptr<DebugStrOffsetsWriter> StrOffstsWriter;
63 
64   /// .debug_abbrev section writer for the main binary.
65   std::unique_ptr<DebugAbbrevWriter> AbbrevWriter;
66 
67   using LocWriters = std::map<uint64_t, std::unique_ptr<DebugLocWriter>>;
68   /// Use a separate location list writer for each compilation unit
69   LocWriters LocListWritersByCU;
70 
71   using RangeListsDWOWriers =
72       std::unordered_map<uint64_t,
73                          std::unique_ptr<DebugRangeListsSectionWriter>>;
74   /// Store Rangelists writer for each DWO CU.
75   RangeListsDWOWriers RangeListsWritersByCU;
76 
77   using DebugAbbrevDWOWriters =
78       std::unordered_map<uint64_t, std::unique_ptr<DebugAbbrevWriter>>;
79   /// Abbrev section writers for DWOs.
80   DebugAbbrevDWOWriters BinaryDWOAbbrevWriters;
81 
82   using DebugInfoDWOPatchers =
83       std::unordered_map<uint64_t, std::unique_ptr<SimpleBinaryPatcher>>;
84   /// Binary patchers for DWO debug_info sections.
85   DebugInfoDWOPatchers BinaryDWODebugInfoPatchers;
86 
87   /// Stores all the Type Signatures for DWO CU.
88   DebugTypesSignaturesPerCUMap TypeSignaturesPerCU;
89 
90   std::mutex LocListDebugInfoPatchesMutex;
91 
92   /// DWARFLegacy is all DWARF versions before DWARF 5.
93   enum class DWARFVersion { DWARFLegacy, DWARF5 };
94 
95   /// Update debug info for all DIEs in \p Unit.
96   void updateUnitDebugInfo(DWARFUnit &Unit,
97                            DebugInfoBinaryPatcher &DebugInfoPatcher,
98                            DebugAbbrevWriter &AbbrevWriter,
99                            DebugLocWriter &DebugLocWriter,
100                            DebugRangesSectionWriter &RangesWriter,
101                            Optional<uint64_t> RangesBase = None);
102 
103   /// Patches the binary for an object's address ranges to be updated.
104   /// The object can be anything that has associated address ranges via either
105   /// DW_AT_low/high_pc or DW_AT_ranges (i.e. functions, lexical blocks, etc).
106   /// \p DebugRangesOffset is the offset in .debug_ranges of the object's
107   /// new address ranges in the output binary.
108   /// \p Unit Compile unit the object belongs to.
109   /// \p DIE is the object's DIE in the input binary.
110   /// \p RangesBase if present, update \p DIE to use  DW_AT_GNU_ranges_base
111   ///    attribute.
112   void updateDWARFObjectAddressRanges(const DWARFDie DIE,
113                                       uint64_t DebugRangesOffset,
114                                       SimpleBinaryPatcher &DebugInfoPatcher,
115                                       DebugAbbrevWriter &AbbrevWriter,
116                                       Optional<uint64_t> RangesBase = None);
117 
118   std::unique_ptr<DebugBufferVector>
119   makeFinalLocListsSection(DebugInfoBinaryPatcher &DebugInfoPatcher,
120                            DWARFVersion Version);
121 
122   /// Finalize debug sections in the main binary.
123   CUOffsetMap finalizeDebugSections(DebugInfoBinaryPatcher &DebugInfoPatcher);
124 
125   /// Patches the binary for DWARF address ranges (e.g. in functions and lexical
126   /// blocks) to be updated.
127   void updateDebugAddressRanges();
128 
129   /// Rewrite .gdb_index section if present.
130   void updateGdbIndexSection(CUOffsetMap &CUMap);
131 
132   /// Output .dwo files.
133   void writeDWOFiles(std::unordered_map<uint64_t, std::string> &DWOIdToName);
134 
135   /// Output .dwp files.
136   void writeDWP(std::unordered_map<uint64_t, std::string> &DWOIdToName);
137 
138   /// DWARFDie contains a pointer to a DIE and hence gets invalidated once the
139   /// embedded DIE is destroyed. This wrapper class stores a DIE internally and
140   /// could be cast to a DWARFDie that is valid even after the initial DIE is
141   /// destroyed.
142   struct DWARFDieWrapper {
143     DWARFUnit *Unit;
144     DWARFDebugInfoEntry DIE;
145 
DWARFDieWrapperDWARFDieWrapper146     DWARFDieWrapper(DWARFUnit *Unit, DWARFDebugInfoEntry DIE)
147         : Unit(Unit), DIE(DIE) {}
148 
DWARFDieWrapperDWARFDieWrapper149     DWARFDieWrapper(DWARFDie &Die)
150         : Unit(Die.getDwarfUnit()), DIE(*Die.getDebugInfoEntry()) {}
151 
DWARFDieDWARFDieWrapper152     operator DWARFDie() { return DWARFDie(Unit, &DIE); }
153   };
154 
155   /// DIEs with abbrevs that were not converted to DW_AT_ranges.
156   /// We only update those when all DIEs have been processed to guarantee that
157   /// the abbrev (which is shared) is intact.
158   using PendingRangesType = std::unordered_map<
159       const DWARFAbbreviationDeclaration *,
160       std::vector<std::pair<DWARFDieWrapper, DebugAddressRange>>>;
161 
162   /// Convert \p Abbrev from using a simple DW_AT_(low|high)_pc range to
163   /// DW_AT_ranges with optional \p RangesBase.
164   void convertToRangesPatchAbbrev(const DWARFUnit &Unit,
165                                   const DWARFAbbreviationDeclaration *Abbrev,
166                                   DebugAbbrevWriter &AbbrevWriter,
167                                   Optional<uint64_t> RangesBase = None);
168 
169   /// Update \p DIE that was using DW_AT_(low|high)_pc with DW_AT_ranges offset.
170   /// Updates to the DIE should be synced with abbreviation updates using the
171   /// function above.
172   void convertToRangesPatchDebugInfo(DWARFDie DIE, uint64_t RangesSectionOffset,
173                                      SimpleBinaryPatcher &DebugInfoPatcher,
174                                      Optional<uint64_t> RangesBase = None);
175 
176   /// Helper function for creating and returning per-DWO patchers/writers.
177   template <class T, class Patcher>
getBinaryDWOPatcherHelper(T & BinaryPatchers,uint64_t DwoId)178   Patcher *getBinaryDWOPatcherHelper(T &BinaryPatchers, uint64_t DwoId) {
179     std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
180     auto Iter = BinaryPatchers.find(DwoId);
181     if (Iter == BinaryPatchers.end()) {
182       // Using make_pair instead of {} to work around bug in older version of
183       // the library. https://timsong-cpp.github.io/lwg-issues/2354
184       Iter = BinaryPatchers
185                  .insert(std::make_pair(DwoId, std::make_unique<Patcher>()))
186                  .first;
187     }
188 
189     return static_cast<Patcher *>(Iter->second.get());
190   }
191 
192   /// Adds a \p Str to .debug_str section.
193   /// Uses \p AttrInfoVal to either update entry in a DIE for legacy DWARF using
194   /// \p DebugInfoPatcher, or for DWARF5 update an index in .debug_str_offsets
195   /// for this contribution of \p Unit.
196   void addStringHelper(DebugInfoBinaryPatcher &DebugInfoPatcher,
197                        const DWARFUnit &Unit, const AttrInfo &AttrInfoVal,
198                        StringRef Str);
199 
200 public:
DWARFRewriter(BinaryContext & BC)201   DWARFRewriter(BinaryContext &BC) : BC(BC) {}
202 
203   /// Main function for updating the DWARF debug info.
204   void updateDebugInfo();
205 
206   /// Update stmt_list for CUs based on the new .debug_line \p Layout.
207   void updateLineTableOffsets(const MCAsmLayout &Layout);
208 
209   /// Returns a DWO Debug Info Patcher for DWO ID.
210   /// Creates a new instance if it does not already exist.
getBinaryDWODebugInfoPatcher(uint64_t DwoId)211   SimpleBinaryPatcher *getBinaryDWODebugInfoPatcher(uint64_t DwoId) {
212     return getBinaryDWOPatcherHelper<DebugInfoDWOPatchers,
213                                      DebugInfoBinaryPatcher>(
214         BinaryDWODebugInfoPatchers, DwoId);
215   }
216 
217   /// Creates abbrev writer for DWO unit with \p DWOId.
createBinaryDWOAbbrevWriter(DWARFContext & Context,uint64_t DWOId)218   DebugAbbrevWriter *createBinaryDWOAbbrevWriter(DWARFContext &Context,
219                                                  uint64_t DWOId) {
220     std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
221     auto &Entry = BinaryDWOAbbrevWriters[DWOId];
222     Entry = std::make_unique<DebugAbbrevWriter>(Context, DWOId);
223     return Entry.get();
224   }
225 
226   /// Returns DWO abbrev writer for \p DWOId. The writer must exist.
getBinaryDWOAbbrevWriter(uint64_t DWOId)227   DebugAbbrevWriter *getBinaryDWOAbbrevWriter(uint64_t DWOId) {
228     auto Iter = BinaryDWOAbbrevWriters.find(DWOId);
229     assert(Iter != BinaryDWOAbbrevWriters.end() && "writer does not exist");
230     return Iter->second.get();
231   }
232 
233   /// Given a \p DWOId, return its DebugLocWriter if it exists.
getDebugLocWriter(uint64_t DWOId)234   DebugLocWriter *getDebugLocWriter(uint64_t DWOId) {
235     auto Iter = LocListWritersByCU.find(DWOId);
236     return Iter == LocListWritersByCU.end() ? nullptr
237                                             : LocListWritersByCU[DWOId].get();
238   }
239 };
240 
241 } // namespace bolt
242 } // namespace llvm
243 
244 #endif
245