1 | //===- bolt/Core/DebugData.h - Debugging information handling ---*- 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 | // This file contains declaration of classes that represent and serialize |
10 | // DWARF-related entities. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef BOLT_CORE_DEBUG_DATA_H |
15 | #define BOLT_CORE_DEBUG_DATA_H |
16 | |
17 | #include "llvm/ADT/SmallVector.h" |
18 | #include "llvm/CodeGen/DIE.h" |
19 | #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
20 | #include "llvm/MC/MCDwarf.h" |
21 | #include "llvm/Support/FormatVariadic.h" |
22 | #include "llvm/Support/SMLoc.h" |
23 | #include "llvm/Support/raw_ostream.h" |
24 | #include <cstdint> |
25 | #include <map> |
26 | #include <memory> |
27 | #include <mutex> |
28 | #include <string> |
29 | #include <unordered_map> |
30 | #include <utility> |
31 | #include <vector> |
32 | |
33 | #define DWARF2_FLAG_END_SEQUENCE (1 << 4) |
34 | |
35 | namespace llvm { |
36 | |
37 | namespace bolt { |
38 | |
39 | class DIEBuilder; |
40 | struct AttrInfo { |
41 | DWARFFormValue V; |
42 | const DWARFAbbreviationDeclaration *AbbrevDecl; |
43 | uint64_t Offset; |
44 | uint32_t Size; // Size of the attribute. |
45 | }; |
46 | |
47 | /// Finds attributes FormValue and Offset. |
48 | /// |
49 | /// \param DIE die to look up in. |
50 | /// \param AbbrevDecl abbrev declaration for the die. |
51 | /// \param Index an index in Abbrev declaration entry. |
52 | std::optional<AttrInfo> |
53 | findAttributeInfo(const DWARFDie DIE, |
54 | const DWARFAbbreviationDeclaration *AbbrevDecl, |
55 | uint32_t Index); |
56 | |
57 | /// Finds attributes FormValue and Offset. |
58 | /// |
59 | /// \param DIE die to look up in. |
60 | /// \param Attr the attribute to extract. |
61 | /// \return an optional AttrInfo with DWARFFormValue and Offset. |
62 | std::optional<AttrInfo> findAttributeInfo(const DWARFDie DIE, |
63 | dwarf::Attribute Attr); |
64 | |
65 | // DWARF5 Header in order of encoding. |
66 | // Types represent encoding sizes. |
67 | using UnitLengthType = uint32_t; |
68 | using VersionType = uint16_t; |
69 | using AddressSizeType = uint8_t; |
70 | using SegmentSelectorType = uint8_t; |
71 | using OffsetEntryCountType = uint32_t; |
72 | /// Get DWARF5 Header size. |
73 | /// Rangelists and Loclists have the same header. |
74 | constexpr uint32_t () { |
75 | return sizeof(UnitLengthType) + sizeof(VersionType) + |
76 | sizeof(AddressSizeType) + sizeof(SegmentSelectorType) + |
77 | sizeof(OffsetEntryCountType); |
78 | } |
79 | |
80 | class BinaryContext; |
81 | |
82 | /// Address range representation. Takes less space than DWARFAddressRange. |
83 | struct DebugAddressRange { |
84 | uint64_t LowPC{0}; |
85 | uint64_t HighPC{0}; |
86 | |
87 | DebugAddressRange() = default; |
88 | |
89 | DebugAddressRange(uint64_t LowPC, uint64_t HighPC) |
90 | : LowPC(LowPC), HighPC(HighPC) {} |
91 | }; |
92 | |
93 | static inline bool operator<(const DebugAddressRange &LHS, |
94 | const DebugAddressRange &RHS) { |
95 | return std::tie(args: LHS.LowPC, args: LHS.HighPC) < std::tie(args: RHS.LowPC, args: RHS.HighPC); |
96 | } |
97 | |
98 | inline raw_ostream &operator<<(raw_ostream &OS, |
99 | const DebugAddressRange &Range) { |
100 | OS << formatv(Fmt: "[{0:x}, {1:x})" , Vals: Range.LowPC, Vals: Range.HighPC); |
101 | return OS; |
102 | } |
103 | |
104 | /// DebugAddressRangesVector - represents a set of absolute address ranges. |
105 | using DebugAddressRangesVector = SmallVector<DebugAddressRange, 2>; |
106 | |
107 | /// Address range with location used by .debug_loc section. |
108 | /// More compact than DWARFLocationEntry and uses absolute addresses. |
109 | struct DebugLocationEntry { |
110 | uint64_t LowPC; |
111 | uint64_t HighPC; |
112 | SmallVector<uint8_t, 4> Expr; |
113 | }; |
114 | |
115 | inline raw_ostream &operator<<(raw_ostream &OS, |
116 | const DebugLocationEntry &Entry) { |
117 | OS << formatv(Fmt: "[{0:x}, {1:x}) : [" , Vals: Entry.LowPC, Vals: Entry.HighPC); |
118 | const char *Sep = "" ; |
119 | for (unsigned Byte : Entry.Expr) { |
120 | OS << Sep << Byte; |
121 | Sep = ", " ; |
122 | } |
123 | OS << "]" ; |
124 | return OS; |
125 | } |
126 | |
127 | using DebugLocationsVector = SmallVector<DebugLocationEntry, 4>; |
128 | |
129 | /// References a row in a DWARFDebugLine::LineTable by the DWARF |
130 | /// Context index of the DWARF Compile Unit that owns the Line Table and the row |
131 | /// index. This is tied to our IR during disassembly so that we can later update |
132 | /// .debug_line information. RowIndex has a base of 1, which means a RowIndex |
133 | /// of 1 maps to the first row of the line table and a RowIndex of 0 is invalid. |
134 | struct DebugLineTableRowRef { |
135 | uint32_t DwCompileUnitIndex; |
136 | uint32_t RowIndex; |
137 | |
138 | const static DebugLineTableRowRef NULL_ROW; |
139 | |
140 | bool operator==(const DebugLineTableRowRef &Rhs) const { |
141 | return DwCompileUnitIndex == Rhs.DwCompileUnitIndex && |
142 | RowIndex == Rhs.RowIndex; |
143 | } |
144 | |
145 | bool operator!=(const DebugLineTableRowRef &Rhs) const { |
146 | return !(*this == Rhs); |
147 | } |
148 | |
149 | static DebugLineTableRowRef fromSMLoc(const SMLoc &Loc) { |
150 | union { |
151 | decltype(Loc.getPointer()) Ptr; |
152 | DebugLineTableRowRef Ref; |
153 | } U; |
154 | U.Ptr = Loc.getPointer(); |
155 | return U.Ref; |
156 | } |
157 | |
158 | SMLoc toSMLoc() const { |
159 | union { |
160 | decltype(SMLoc().getPointer()) Ptr; |
161 | DebugLineTableRowRef Ref; |
162 | } U; |
163 | U.Ref = *this; |
164 | return SMLoc::getFromPointer(Ptr: U.Ptr); |
165 | } |
166 | }; |
167 | |
168 | /// Common buffer vector used for debug info handling. |
169 | using DebugBufferVector = SmallVector<char, 16>; |
170 | |
171 | /// Map of old CU offset to new offset and length. |
172 | struct CUInfo { |
173 | uint32_t Offset; |
174 | uint32_t Length; |
175 | }; |
176 | using CUOffsetMap = std::map<uint32_t, CUInfo>; |
177 | |
178 | enum class RangesWriterKind { DebugRangesWriter, DebugRangeListsWriter }; |
179 | /// Serializes the .debug_ranges DWARF section. |
180 | class DebugRangesSectionWriter { |
181 | public: |
182 | DebugRangesSectionWriter(); |
183 | |
184 | DebugRangesSectionWriter(RangesWriterKind K) : Kind(K){}; |
185 | |
186 | virtual ~DebugRangesSectionWriter(){}; |
187 | |
188 | /// Add ranges with caching. |
189 | virtual uint64_t |
190 | addRanges(DebugAddressRangesVector &&Ranges, |
191 | std::map<DebugAddressRangesVector, uint64_t> &CachedRanges); |
192 | |
193 | /// Add ranges and return offset into section. |
194 | virtual uint64_t addRanges(DebugAddressRangesVector &Ranges); |
195 | |
196 | /// Returns an offset of an empty address ranges list that is always written |
197 | /// to .debug_ranges |
198 | uint64_t getEmptyRangesOffset() const { return EmptyRangesOffset; } |
199 | |
200 | /// Returns the SectionOffset. |
201 | uint64_t getSectionOffset(); |
202 | |
203 | /// Returns a buffer containing Ranges. |
204 | virtual std::unique_ptr<DebugBufferVector> releaseBuffer() { |
205 | return std::move(RangesBuffer); |
206 | } |
207 | |
208 | RangesWriterKind getKind() const { return Kind; } |
209 | |
210 | static bool classof(const DebugRangesSectionWriter *Writer) { |
211 | return Writer->getKind() == RangesWriterKind::DebugRangesWriter; |
212 | } |
213 | |
214 | /// Writes out range lists for a current CU being processed. |
215 | void virtual finalizeSection(){}; |
216 | |
217 | /// Needs to be invoked before each \p CU is processed. |
218 | void virtual initSection(DWARFUnit &CU){}; |
219 | |
220 | protected: |
221 | std::unique_ptr<DebugBufferVector> RangesBuffer; |
222 | |
223 | std::unique_ptr<raw_svector_ostream> RangesStream; |
224 | |
225 | std::mutex WriterMutex; |
226 | |
227 | /// Current offset in the section (updated as new entries are written). |
228 | /// Starts with 16 since the first 16 bytes are reserved for an empty range. |
229 | uint32_t SectionOffset{0}; |
230 | |
231 | /// Offset of an empty address ranges list. |
232 | static constexpr uint64_t EmptyRangesOffset{0}; |
233 | |
234 | private: |
235 | RangesWriterKind Kind; |
236 | }; |
237 | |
238 | class DebugAddrWriter; |
239 | class DebugRangeListsSectionWriter : public DebugRangesSectionWriter { |
240 | public: |
241 | DebugRangeListsSectionWriter() |
242 | : DebugRangesSectionWriter(RangesWriterKind::DebugRangeListsWriter) { |
243 | RangesBuffer = std::make_unique<DebugBufferVector>(); |
244 | RangesStream = std::make_unique<raw_svector_ostream>(args&: *RangesBuffer); |
245 | }; |
246 | virtual ~DebugRangeListsSectionWriter(){}; |
247 | |
248 | static void setAddressWriter(DebugAddrWriter *AddrW) { AddrWriter = AddrW; } |
249 | |
250 | /// Add ranges with caching. |
251 | uint64_t addRanges( |
252 | DebugAddressRangesVector &&Ranges, |
253 | std::map<DebugAddressRangesVector, uint64_t> &CachedRanges) override; |
254 | |
255 | /// Add ranges and return offset into section. |
256 | uint64_t addRanges(DebugAddressRangesVector &Ranges) override; |
257 | |
258 | std::unique_ptr<DebugBufferVector> releaseBuffer() override { |
259 | return std::move(RangesBuffer); |
260 | } |
261 | |
262 | /// Needs to be invoked before each \p CU is processed. |
263 | void initSection(DWARFUnit &CU) override; |
264 | |
265 | /// Writes out range lists for a current CU being processed. |
266 | void finalizeSection() override; |
267 | |
268 | // Returns true if section is empty. |
269 | bool empty() { return RangesBuffer->empty(); } |
270 | |
271 | static bool classof(const DebugRangesSectionWriter *Writer) { |
272 | return Writer->getKind() == RangesWriterKind::DebugRangeListsWriter; |
273 | } |
274 | |
275 | private: |
276 | static DebugAddrWriter *AddrWriter; |
277 | /// Used to find unique CU ID. |
278 | DWARFUnit *CU; |
279 | /// Current relative offset of range list entry within this CUs rangelist |
280 | /// body. |
281 | uint32_t CurrentOffset{0}; |
282 | /// Contains relative offset of each range list entry. |
283 | SmallVector<uint32_t, 1> RangeEntries; |
284 | |
285 | std::unique_ptr<DebugBufferVector> CUBodyBuffer; |
286 | std::unique_ptr<raw_svector_ostream> CUBodyStream; |
287 | }; |
288 | |
289 | /// Serializes the .debug_aranges DWARF section. |
290 | class DebugARangesSectionWriter { |
291 | public: |
292 | /// Add ranges for CU matching \p CUOffset. |
293 | void addCURanges(uint64_t CUOffset, DebugAddressRangesVector &&Ranges); |
294 | |
295 | /// Writes .debug_aranges with the added ranges to the MCObjectWriter. |
296 | /// Takes in \p RangesStream to write into, and \p CUMap which maps CU |
297 | /// original offsets to new ones. |
298 | void writeARangesSection(raw_svector_ostream &RangesStream, |
299 | const CUOffsetMap &CUMap) const; |
300 | |
301 | /// Resets the writer to a clear state. |
302 | void reset() { CUAddressRanges.clear(); } |
303 | |
304 | /// Map DWARFCompileUnit index to ranges. |
305 | using CUAddressRangesType = std::map<uint64_t, DebugAddressRangesVector>; |
306 | |
307 | /// Return ranges for a given CU. |
308 | const CUAddressRangesType &getCUAddressRanges() const { |
309 | return CUAddressRanges; |
310 | } |
311 | |
312 | private: |
313 | /// Map from compile unit offset to the list of address intervals that belong |
314 | /// to that compile unit. Each interval is a pair |
315 | /// (first address, interval size). |
316 | CUAddressRangesType CUAddressRanges; |
317 | |
318 | std::mutex CUAddressRangesMutex; |
319 | }; |
320 | |
321 | using IndexAddressPair = std::pair<uint32_t, uint64_t>; |
322 | using AddressToIndexMap = std::unordered_map<uint64_t, uint32_t>; |
323 | using IndexToAddressMap = std::unordered_map<uint32_t, uint64_t>; |
324 | using AddressSectionBuffer = SmallVector<char, 4>; |
325 | class DebugAddrWriter { |
326 | public: |
327 | DebugAddrWriter() = delete; |
328 | DebugAddrWriter(BinaryContext *BC_); |
329 | virtual ~DebugAddrWriter(){}; |
330 | /// Given an address returns an index in .debug_addr. |
331 | /// Adds Address to map. |
332 | uint32_t getIndexFromAddress(uint64_t Address, DWARFUnit &CU); |
333 | |
334 | /// Write out entries in to .debug_addr section for CUs. |
335 | virtual void update(DIEBuilder &DIEBlder, DWARFUnit &CUs); |
336 | |
337 | /// Return buffer with all the entries in .debug_addr already writen out using |
338 | /// update(...). |
339 | virtual AddressSectionBuffer &finalize() { return *Buffer; } |
340 | |
341 | /// Returns False if .debug_addr section was created.. |
342 | bool isInitialized() const { return !AddressMaps.empty(); } |
343 | |
344 | protected: |
345 | class AddressForDWOCU { |
346 | public: |
347 | AddressToIndexMap::iterator find(uint64_t Address) { |
348 | return AddressToIndex.find(x: Address); |
349 | } |
350 | AddressToIndexMap::iterator end() { return AddressToIndex.end(); } |
351 | AddressToIndexMap::iterator begin() { return AddressToIndex.begin(); } |
352 | |
353 | IndexToAddressMap::iterator indexToAdddessEnd() { |
354 | return IndexToAddress.end(); |
355 | } |
356 | IndexToAddressMap::iterator indexToAddressBegin() { |
357 | return IndexToAddress.begin(); |
358 | } |
359 | uint32_t getNextIndex() { |
360 | while (IndexToAddress.count(x: CurrentIndex)) |
361 | ++CurrentIndex; |
362 | return CurrentIndex; |
363 | } |
364 | |
365 | /// Inserts elements in to IndexToAddress and AddressToIndex. |
366 | /// Follows the same semantics as unordered_map insert. |
367 | std::pair<AddressToIndexMap::iterator, bool> insert(uint64_t Address, |
368 | uint32_t Index) { |
369 | IndexToAddress.insert(x: {Index, Address}); |
370 | return AddressToIndex.insert(x: {Address, Index}); |
371 | } |
372 | |
373 | /// Updates AddressToIndex Map. |
374 | /// Follows the same semantics as unordered map []. |
375 | void updateAddressToIndex(uint64_t Address, uint32_t Index) { |
376 | AddressToIndex[Address] = Index; |
377 | } |
378 | |
379 | /// Updates IndexToAddress Map. |
380 | /// Follows the same semantics as unordered map []. |
381 | void (uint64_t Address, uint32_t Index) { |
382 | IndexToAddress[Index] = Address; |
383 | } |
384 | |
385 | void dump(); |
386 | |
387 | private: |
388 | AddressToIndexMap AddressToIndex; |
389 | IndexToAddressMap IndexToAddress; |
390 | uint32_t CurrentIndex{0}; |
391 | }; |
392 | |
393 | virtual uint64_t getCUID(DWARFUnit &Unit) { |
394 | assert(Unit.getDWOId() && "Unit is not Skeleton CU." ); |
395 | return *Unit.getDWOId(); |
396 | } |
397 | |
398 | BinaryContext *BC; |
399 | /// Maps DWOID to AddressForDWOCU. |
400 | std::unordered_map<uint64_t, AddressForDWOCU> AddressMaps; |
401 | /// Mutex used for parallel processing of debug info. |
402 | std::mutex WriterMutex; |
403 | std::unique_ptr<AddressSectionBuffer> Buffer; |
404 | std::unique_ptr<raw_svector_ostream> AddressStream; |
405 | /// Used to track sections that were not modified so that they can be re-used. |
406 | DenseMap<uint64_t, uint64_t> UnmodifiedAddressOffsets; |
407 | }; |
408 | |
409 | class DebugAddrWriterDwarf5 : public DebugAddrWriter { |
410 | public: |
411 | DebugAddrWriterDwarf5() = delete; |
412 | DebugAddrWriterDwarf5(BinaryContext *BC) : DebugAddrWriter(BC) {} |
413 | |
414 | /// Write out entries in to .debug_addr section for CUs. |
415 | virtual void update(DIEBuilder &DIEBlder, DWARFUnit &CUs) override; |
416 | |
417 | protected: |
418 | /// Given DWARFUnit \p Unit returns either DWO ID or it's offset within |
419 | /// .debug_info. |
420 | uint64_t getCUID(DWARFUnit &Unit) override { |
421 | if (Unit.isDWOUnit()) { |
422 | DWARFUnit *SkeletonCU = Unit.getLinkedUnit(); |
423 | return SkeletonCU->getOffset(); |
424 | } |
425 | return Unit.getOffset(); |
426 | } |
427 | }; |
428 | |
429 | /// This class is NOT thread safe. |
430 | using DebugStrOffsetsBufferVector = SmallVector<char, 16>; |
431 | class DebugStrOffsetsWriter { |
432 | public: |
433 | DebugStrOffsetsWriter() { |
434 | StrOffsetsBuffer = std::make_unique<DebugStrOffsetsBufferVector>(); |
435 | StrOffsetsStream = std::make_unique<raw_svector_ostream>(args&: *StrOffsetsBuffer); |
436 | } |
437 | |
438 | /// Update Str offset in .debug_str in .debug_str_offsets. |
439 | void updateAddressMap(uint32_t Index, uint32_t Address); |
440 | |
441 | /// Get offset for given index in original .debug_str_offsets section. |
442 | uint64_t getOffset(uint32_t Index) const { return StrOffsets[Index]; } |
443 | /// Writes out current sections entry into .debug_str_offsets. |
444 | void finalizeSection(DWARFUnit &Unit, DIEBuilder &DIEBldr); |
445 | |
446 | /// Returns False if no strings were added to .debug_str. |
447 | bool isFinalized() const { return !StrOffsetsBuffer->empty(); } |
448 | |
449 | /// Returns buffer containing .debug_str_offsets. |
450 | std::unique_ptr<DebugStrOffsetsBufferVector> releaseBuffer() { |
451 | return std::move(StrOffsetsBuffer); |
452 | } |
453 | |
454 | /// Initializes Buffer and Stream. |
455 | void initialize(DWARFUnit &Unit); |
456 | |
457 | /// Clear data. |
458 | void clear() { |
459 | IndexToAddressMap.clear(); |
460 | StrOffsets.clear(); |
461 | } |
462 | |
463 | private: |
464 | std::unique_ptr<DebugStrOffsetsBufferVector> StrOffsetsBuffer; |
465 | std::unique_ptr<raw_svector_ostream> StrOffsetsStream; |
466 | std::map<uint32_t, uint32_t> IndexToAddressMap; |
467 | SmallVector<uint32_t, 5> StrOffsets; |
468 | std::unordered_map<uint64_t, uint64_t> ProcessedBaseOffsets; |
469 | bool StrOffsetSectionWasModified = false; |
470 | }; |
471 | |
472 | using DebugStrBufferVector = SmallVector<char, 16>; |
473 | class DebugStrWriter { |
474 | public: |
475 | DebugStrWriter() = delete; |
476 | DebugStrWriter(BinaryContext &BC) : BC(BC) { create(); } |
477 | std::unique_ptr<DebugStrBufferVector> releaseBuffer() { |
478 | return std::move(StrBuffer); |
479 | } |
480 | |
481 | /// Adds string to .debug_str. |
482 | /// On first invocation it initializes internal data structures. |
483 | uint32_t addString(StringRef Str); |
484 | |
485 | /// Returns False if no strings were added to .debug_str. |
486 | bool isInitialized() const { return !StrBuffer->empty(); } |
487 | |
488 | /// Initializes Buffer and Stream. |
489 | void initialize(); |
490 | |
491 | private: |
492 | /// Mutex used for parallel processing of debug info. |
493 | std::mutex WriterMutex; |
494 | /// Creates internal data structures. |
495 | void create(); |
496 | std::unique_ptr<DebugStrBufferVector> StrBuffer; |
497 | std::unique_ptr<raw_svector_ostream> StrStream; |
498 | BinaryContext &BC; |
499 | }; |
500 | |
501 | enum class LocWriterKind { DebugLocWriter, DebugLoclistWriter }; |
502 | |
503 | /// Serializes part of a .debug_loc DWARF section with LocationLists. |
504 | class SimpleBinaryPatcher; |
505 | class DebugLocWriter { |
506 | protected: |
507 | DebugLocWriter(uint8_t DwarfVersion, LocWriterKind Kind) |
508 | : DwarfVersion(DwarfVersion), Kind(Kind) { |
509 | init(); |
510 | } |
511 | |
512 | public: |
513 | DebugLocWriter() { init(); }; |
514 | virtual ~DebugLocWriter(){}; |
515 | |
516 | /// Writes out location lists and stores internal patches. |
517 | virtual void addList(DIEBuilder &DIEBldr, DIE &Die, DIEValue &AttrInfo, |
518 | DebugLocationsVector &LocList); |
519 | |
520 | /// Writes out locations in to a local buffer, and adds Debug Info patches. |
521 | virtual void finalize(DIEBuilder &DIEBldr, DIE &Die); |
522 | |
523 | /// Return internal buffer. |
524 | virtual std::unique_ptr<DebugBufferVector> getBuffer(); |
525 | |
526 | /// Returns DWARF version. |
527 | uint8_t getDwarfVersion() const { return DwarfVersion; } |
528 | |
529 | /// Offset of an empty location list. |
530 | static constexpr uint32_t EmptyListOffset = 0; |
531 | |
532 | LocWriterKind getKind() const { return Kind; } |
533 | |
534 | static bool classof(const DebugLocWriter *Writer) { |
535 | return Writer->getKind() == LocWriterKind::DebugLocWriter; |
536 | } |
537 | |
538 | protected: |
539 | std::unique_ptr<DebugBufferVector> LocBuffer; |
540 | std::unique_ptr<raw_svector_ostream> LocStream; |
541 | /// Current offset in the section (updated as new entries are written). |
542 | /// Starts with 0 here since this only writes part of a full location lists |
543 | /// section. In the final section, for DWARF4, the first 16 bytes are reserved |
544 | /// for an empty list. |
545 | static uint32_t LocSectionOffset; |
546 | uint8_t DwarfVersion{4}; |
547 | LocWriterKind Kind{LocWriterKind::DebugLocWriter}; |
548 | |
549 | private: |
550 | /// Inits all the related data structures. |
551 | void init(); |
552 | struct LocListDebugInfoPatchType { |
553 | uint64_t DebugInfoAttrOffset; |
554 | uint64_t LocListOffset; |
555 | }; |
556 | using VectorLocListDebugInfoPatchType = |
557 | std::vector<LocListDebugInfoPatchType>; |
558 | /// The list of debug info patches to be made once individual |
559 | /// location list writers have been filled |
560 | VectorLocListDebugInfoPatchType LocListDebugInfoPatches; |
561 | }; |
562 | |
563 | class DebugLoclistWriter : public DebugLocWriter { |
564 | public: |
565 | ~DebugLoclistWriter() {} |
566 | DebugLoclistWriter() = delete; |
567 | DebugLoclistWriter(DWARFUnit &Unit, uint8_t DV, bool SD) |
568 | : DebugLocWriter(DV, LocWriterKind::DebugLoclistWriter), CU(Unit), |
569 | IsSplitDwarf(SD) { |
570 | assert(DebugLoclistWriter::AddrWriter && |
571 | "Please use SetAddressWriter to initialize " |
572 | "DebugAddrWriter before instantiation." ); |
573 | if (DwarfVersion >= 5) { |
574 | LocBodyBuffer = std::make_unique<DebugBufferVector>(); |
575 | LocBodyStream = std::make_unique<raw_svector_ostream>(args&: *LocBodyBuffer); |
576 | } else { |
577 | // Writing out empty location list to which all references to empty |
578 | // location lists will point. |
579 | const char Zeroes[16] = {0}; |
580 | *LocStream << StringRef(Zeroes, 16); |
581 | } |
582 | } |
583 | |
584 | static void setAddressWriter(DebugAddrWriter *AddrW) { AddrWriter = AddrW; } |
585 | |
586 | /// Stores location lists internally to be written out during finalize phase. |
587 | virtual void addList(DIEBuilder &DIEBldr, DIE &Die, DIEValue &AttrInfo, |
588 | DebugLocationsVector &LocList) override; |
589 | |
590 | /// Writes out locations in to a local buffer and applies debug info patches. |
591 | void finalize(DIEBuilder &DIEBldr, DIE &Die) override; |
592 | |
593 | /// Returns CU ID. |
594 | /// For Skeleton CU it is a CU Offset. |
595 | /// For DWO CU it is a DWO ID. |
596 | uint64_t getCUID() const { |
597 | return CU.isDWOUnit() ? *CU.getDWOId() : CU.getOffset(); |
598 | } |
599 | |
600 | LocWriterKind getKind() const { return DebugLocWriter::getKind(); } |
601 | |
602 | static bool classof(const DebugLocWriter *Writer) { |
603 | return Writer->getKind() == LocWriterKind::DebugLoclistWriter; |
604 | } |
605 | |
606 | bool isSplitDwarf() const { return IsSplitDwarf; } |
607 | |
608 | constexpr static uint32_t InvalidIndex = UINT32_MAX; |
609 | |
610 | private: |
611 | /// Writes out locations in to a local buffer and applies debug info patches. |
612 | void finalizeDWARF5(DIEBuilder &DIEBldr, DIE &Die); |
613 | |
614 | static DebugAddrWriter *AddrWriter; |
615 | DWARFUnit &CU; |
616 | bool IsSplitDwarf{false}; |
617 | // Used for DWARF5 to store location lists before being finalized. |
618 | std::unique_ptr<DebugBufferVector> LocBodyBuffer; |
619 | std::unique_ptr<raw_svector_ostream> LocBodyStream; |
620 | std::vector<uint32_t> RelativeLocListOffsets; |
621 | uint32_t NumberOfEntries{0}; |
622 | static uint32_t LoclistBaseOffset; |
623 | }; |
624 | |
625 | /// Abstract interface for classes that apply modifications to a binary string. |
626 | class BinaryPatcher { |
627 | public: |
628 | virtual ~BinaryPatcher() {} |
629 | /// Applies modifications to the copy of binary string \p BinaryContents . |
630 | /// Implementations do not need to guarantee that size of a new \p |
631 | /// BinaryContents remains unchanged. |
632 | virtual std::string patchBinary(StringRef BinaryContents) = 0; |
633 | }; |
634 | |
635 | /// Applies simple modifications to a binary string, such as directly replacing |
636 | /// the contents of a certain portion with a string or an integer. |
637 | class SimpleBinaryPatcher : public BinaryPatcher { |
638 | private: |
639 | std::vector<std::pair<uint32_t, std::string>> Patches; |
640 | |
641 | /// Adds a patch to replace the contents of \p ByteSize bytes with the integer |
642 | /// \p NewValue encoded in little-endian, with the least-significant byte |
643 | /// being written at the offset \p Offset. |
644 | void addLEPatch(uint64_t Offset, uint64_t NewValue, size_t ByteSize); |
645 | |
646 | /// RangeBase for DWO DebugInfo Patcher. |
647 | uint64_t RangeBase{0}; |
648 | |
649 | /// Gets reset to false when setRangeBase is invoked. |
650 | /// Gets set to true when getRangeBase is called |
651 | uint64_t WasRangeBaseUsed{false}; |
652 | |
653 | public: |
654 | virtual ~SimpleBinaryPatcher() {} |
655 | |
656 | /// Adds a patch to replace the contents of the binary string starting at the |
657 | /// specified \p Offset with the string \p NewValue. |
658 | /// The \p OldValueSize is the size of the old value that will be replaced. |
659 | void addBinaryPatch(uint64_t Offset, std::string &&NewValue, |
660 | uint32_t OldValueSize); |
661 | |
662 | /// Adds a patch to replace the contents of a single byte of the string, at |
663 | /// the offset \p Offset, with the value \Value. |
664 | void addBytePatch(uint64_t Offset, uint8_t Value); |
665 | |
666 | /// Adds a patch to put the integer \p NewValue encoded as a 64-bit |
667 | /// little-endian value at offset \p Offset. |
668 | virtual void addLE64Patch(uint64_t Offset, uint64_t NewValue); |
669 | |
670 | /// Adds a patch to put the integer \p NewValue encoded as a 32-bit |
671 | /// little-endian value at offset \p Offset. |
672 | /// The \p OldValueSize is the size of the old value that will be replaced. |
673 | virtual void addLE32Patch(uint64_t Offset, uint32_t NewValue, |
674 | uint32_t OldValueSize = 4); |
675 | |
676 | /// Add a patch at \p Offset with \p Value using unsigned LEB128 encoding with |
677 | /// size \p OldValueSize. |
678 | /// The \p OldValueSize is the size of the old value that will be replaced. |
679 | virtual void addUDataPatch(uint64_t Offset, uint64_t Value, |
680 | uint32_t OldValueSize); |
681 | |
682 | /// Setting DW_AT_GNU_ranges_base |
683 | void setRangeBase(uint64_t Rb) { |
684 | WasRangeBaseUsed = false; |
685 | RangeBase = Rb; |
686 | } |
687 | |
688 | /// Gets DW_AT_GNU_ranges_base |
689 | uint64_t getRangeBase() { |
690 | WasRangeBaseUsed = true; |
691 | return RangeBase; |
692 | } |
693 | |
694 | /// Proxy for if we broke up low_pc/high_pc to ranges. |
695 | bool getWasRangBasedUsed() const { return WasRangeBaseUsed; } |
696 | |
697 | /// This function takes in \p BinaryContents, applies patches to it and |
698 | /// returns an updated string. |
699 | std::string patchBinary(StringRef BinaryContents) override; |
700 | }; |
701 | |
702 | /// Similar to MCDwarfLineEntry, but identifies the location by its address |
703 | /// instead of MCLabel. |
704 | class BinaryDwarfLineEntry : public MCDwarfLoc { |
705 | uint64_t Address; |
706 | |
707 | public: |
708 | // Constructor to create an BinaryDwarfLineEntry given a symbol and the dwarf |
709 | // loc. |
710 | BinaryDwarfLineEntry(uint64_t Address, const MCDwarfLoc loc) |
711 | : MCDwarfLoc(loc), Address(Address) {} |
712 | |
713 | uint64_t getAddress() const { return Address; } |
714 | }; |
715 | |
716 | /// Line number information for the output binary. One instance per CU. |
717 | /// |
718 | /// For any given CU, we may: |
719 | /// 1. Generate new line table using: |
720 | /// a) emitted code: getMCLineSections().addEntry() |
721 | /// b) information from the input line table: addLineTableSequence() |
722 | /// or |
723 | /// 2. Copy line table from the input file: addRawContents(). |
724 | class DwarfLineTable { |
725 | public: |
726 | /// Line number information on contiguous code region from the input binary. |
727 | /// It is represented by [FirstIndex, LastIndex] rows range in the input |
728 | /// line table, and the end address of the sequence used for issuing the end |
729 | /// of the sequence directive. |
730 | struct RowSequence { |
731 | uint32_t FirstIndex; |
732 | uint32_t LastIndex; |
733 | uint64_t EndAddress; |
734 | }; |
735 | |
736 | private: |
737 | MCDwarfLineTableHeader ; |
738 | |
739 | /// MC line tables for the code generated via MC layer. |
740 | MCLineSection MCLineSections; |
741 | |
742 | /// Line info for the original code. To be merged with tables for new code. |
743 | const DWARFDebugLine::LineTable *InputTable{nullptr}; |
744 | std::vector<RowSequence> InputSequences; |
745 | |
746 | /// Raw data representing complete debug line section for the unit. |
747 | StringRef RawData; |
748 | |
749 | /// DWARF Version |
750 | uint16_t DwarfVersion; |
751 | |
752 | public: |
753 | /// Emit line info for all units in the binary context. |
754 | static void emit(BinaryContext &BC, MCStreamer &Streamer); |
755 | |
756 | /// Emit the Dwarf file and the line tables for a given CU. |
757 | void emitCU(MCStreamer *MCOS, MCDwarfLineTableParams Params, |
758 | std::optional<MCDwarfLineStr> &LineStr, BinaryContext &BC) const; |
759 | |
760 | Expected<unsigned> tryGetFile(StringRef &Directory, StringRef &FileName, |
761 | std::optional<MD5::MD5Result> Checksum, |
762 | std::optional<StringRef> Source, |
763 | uint16_t DwarfVersion, |
764 | unsigned FileNumber = 0) { |
765 | assert(RawData.empty() && "cannot use with raw data" ); |
766 | return Header.tryGetFile(Directory, FileName, Checksum, Source, |
767 | DwarfVersion, FileNumber); |
768 | } |
769 | |
770 | /// Return label at the start of the emitted debug line for the unit. |
771 | MCSymbol *getLabel() const { return Header.Label; } |
772 | |
773 | void setLabel(MCSymbol *Label) { Header.Label = Label; } |
774 | |
775 | /// Sets the root file \p Directory, \p FileName, optional \p CheckSum, and |
776 | /// optional \p Source. |
777 | void setRootFile(StringRef Directory, StringRef FileName, |
778 | std::optional<MD5::MD5Result> Checksum, |
779 | std::optional<StringRef> Source) { |
780 | Header.setRootFile(Directory, FileName, Checksum, Source); |
781 | } |
782 | |
783 | /// Access to MC line info. |
784 | MCLineSection &getMCLineSections() { return MCLineSections; } |
785 | |
786 | /// Add line information using the sequence from the input line \p Table. |
787 | void addLineTableSequence(const DWARFDebugLine::LineTable *Table, |
788 | uint32_t FirstRow, uint32_t LastRow, |
789 | uint64_t EndOfSequenceAddress) { |
790 | assert((!InputTable || InputTable == Table) && |
791 | "expected same table for CU" ); |
792 | InputTable = Table; |
793 | InputSequences.emplace_back( |
794 | args: RowSequence{.FirstIndex: FirstRow, .LastIndex: LastRow, .EndAddress: EndOfSequenceAddress}); |
795 | } |
796 | |
797 | /// Indicate that for the unit we should emit specified contents instead of |
798 | /// generating a new line info table. |
799 | void addRawContents(StringRef DebugLineContents) { |
800 | RawData = DebugLineContents; |
801 | } |
802 | |
803 | /// Sets DWARF version for this line table. |
804 | void setDwarfVersion(uint16_t V) { DwarfVersion = V; } |
805 | |
806 | // Returns DWARF Version for this line table. |
807 | uint16_t getDwarfVersion() const { return DwarfVersion; } |
808 | }; |
809 | } // namespace bolt |
810 | } // namespace llvm |
811 | |
812 | #endif |
813 | |