1 | //===- bolt/Core/DebugData.cpp - Debugging information handling -----------===// |
---|---|
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 implements functions and classes for handling debug info. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "bolt/Core/DebugData.h" |
14 | #include "bolt/Core/BinaryContext.h" |
15 | #include "bolt/Core/DIEBuilder.h" |
16 | #include "bolt/Utils/Utils.h" |
17 | #include "llvm/BinaryFormat/Dwarf.h" |
18 | #include "llvm/CodeGen/DIE.h" |
19 | #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" |
20 | #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" |
21 | #include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" |
22 | #include "llvm/MC/MCAssembler.h" |
23 | #include "llvm/MC/MCContext.h" |
24 | #include "llvm/MC/MCObjectStreamer.h" |
25 | #include "llvm/Support/CommandLine.h" |
26 | #include "llvm/Support/EndianStream.h" |
27 | #include "llvm/Support/LEB128.h" |
28 | #include "llvm/Support/SHA1.h" |
29 | #include <algorithm> |
30 | #include <cassert> |
31 | #include <cstdint> |
32 | #include <functional> |
33 | #include <memory> |
34 | #include <optional> |
35 | #include <unordered_map> |
36 | #include <vector> |
37 | |
38 | #define DEBUG_TYPE "bolt-debug-info" |
39 | |
40 | namespace opts { |
41 | extern llvm::cl::opt<unsigned> Verbosity; |
42 | } // namespace opts |
43 | |
44 | namespace llvm { |
45 | class MCSymbol; |
46 | |
47 | namespace bolt { |
48 | |
49 | static void replaceLocValbyForm(DIEBuilder &DIEBldr, DIE &Die, DIEValue DIEVal, |
50 | dwarf::Form Format, uint64_t NewVal) { |
51 | if (Format == dwarf::DW_FORM_loclistx) |
52 | DIEBldr.replaceValue(Die: &Die, Attribute: DIEVal.getAttribute(), Form: Format, |
53 | NewValue: DIELocList(NewVal)); |
54 | else |
55 | DIEBldr.replaceValue(Die: &Die, Attribute: DIEVal.getAttribute(), Form: Format, |
56 | NewValue: DIEInteger(NewVal)); |
57 | } |
58 | |
59 | std::optional<AttrInfo> |
60 | findAttributeInfo(const DWARFDie DIE, |
61 | const DWARFAbbreviationDeclaration *AbbrevDecl, |
62 | uint32_t Index) { |
63 | const DWARFUnit &U = *DIE.getDwarfUnit(); |
64 | uint64_t Offset = |
65 | AbbrevDecl->getAttributeOffsetFromIndex(AttrIndex: Index, DIEOffset: DIE.getOffset(), U); |
66 | std::optional<DWARFFormValue> Value = |
67 | AbbrevDecl->getAttributeValueFromOffset(AttrIndex: Index, Offset, U); |
68 | if (!Value) |
69 | return std::nullopt; |
70 | // AttributeSpec |
71 | const DWARFAbbreviationDeclaration::AttributeSpec *AttrVal = |
72 | AbbrevDecl->attributes().begin() + Index; |
73 | uint32_t ValSize = 0; |
74 | std::optional<int64_t> ValSizeOpt = AttrVal->getByteSize(U); |
75 | if (ValSizeOpt) { |
76 | ValSize = static_cast<uint32_t>(*ValSizeOpt); |
77 | } else { |
78 | DWARFDataExtractor DebugInfoData = U.getDebugInfoExtractor(); |
79 | uint64_t NewOffset = Offset; |
80 | DWARFFormValue::skipValue(Form: Value->getForm(), DebugInfoData, OffsetPtr: &NewOffset, |
81 | FormParams: U.getFormParams()); |
82 | // This includes entire size of the entry, which might not be just the |
83 | // encoding part. For example for DW_AT_loc it will include expression |
84 | // location. |
85 | ValSize = NewOffset - Offset; |
86 | } |
87 | return AttrInfo{.V: *Value, .AbbrevDecl: DIE.getAbbreviationDeclarationPtr(), .Offset: Offset, .Size: ValSize}; |
88 | } |
89 | |
90 | std::optional<AttrInfo> findAttributeInfo(const DWARFDie DIE, |
91 | dwarf::Attribute Attr) { |
92 | if (!DIE.isValid()) |
93 | return std::nullopt; |
94 | const DWARFAbbreviationDeclaration *AbbrevDecl = |
95 | DIE.getAbbreviationDeclarationPtr(); |
96 | if (!AbbrevDecl) |
97 | return std::nullopt; |
98 | std::optional<uint32_t> Index = AbbrevDecl->findAttributeIndex(attr: Attr); |
99 | if (!Index) |
100 | return std::nullopt; |
101 | return findAttributeInfo(DIE, AbbrevDecl, Index: *Index); |
102 | } |
103 | |
104 | const DebugLineTableRowRef DebugLineTableRowRef::NULL_ROW{.DwCompileUnitIndex: 0, .RowIndex: 0}; |
105 | |
106 | LLVM_ATTRIBUTE_UNUSED |
107 | static void printLE64(const std::string &S) { |
108 | for (uint32_t I = 0, Size = S.size(); I < Size; ++I) { |
109 | errs() << Twine::utohexstr(Val: S[I]); |
110 | errs() << Twine::utohexstr(Val: (int8_t)S[I]); |
111 | } |
112 | errs() << "\n"; |
113 | } |
114 | |
115 | // Writes address ranges to Writer as pairs of 64-bit (address, size). |
116 | // If RelativeRange is true, assumes the address range to be written must be of |
117 | // the form (begin address, range size), otherwise (begin address, end address). |
118 | // Terminates the list by writing a pair of two zeroes. |
119 | // Returns the number of written bytes. |
120 | static uint64_t |
121 | writeAddressRanges(raw_svector_ostream &Stream, |
122 | const DebugAddressRangesVector &AddressRanges, |
123 | const bool WriteRelativeRanges = false) { |
124 | for (const DebugAddressRange &Range : AddressRanges) { |
125 | support::endian::write(os&: Stream, value: Range.LowPC, endian: llvm::endianness::little); |
126 | support::endian::write( |
127 | os&: Stream, value: WriteRelativeRanges ? Range.HighPC - Range.LowPC : Range.HighPC, |
128 | endian: llvm::endianness::little); |
129 | } |
130 | // Finish with 0 entries. |
131 | support::endian::write(os&: Stream, value: 0ULL, endian: llvm::endianness::little); |
132 | support::endian::write(os&: Stream, value: 0ULL, endian: llvm::endianness::little); |
133 | return AddressRanges.size() * 16 + 16; |
134 | } |
135 | |
136 | DebugRangesSectionWriter::DebugRangesSectionWriter() { |
137 | RangesBuffer = std::make_unique<DebugBufferVector>(); |
138 | RangesStream = std::make_unique<raw_svector_ostream>(args&: *RangesBuffer); |
139 | |
140 | Kind = RangesWriterKind::DebugRangesWriter; |
141 | } |
142 | |
143 | void DebugRangesSectionWriter::initSection() { |
144 | // Adds an empty range to the buffer. |
145 | writeAddressRanges(Stream&: *RangesStream, AddressRanges: DebugAddressRangesVector{}); |
146 | } |
147 | |
148 | uint64_t DebugRangesSectionWriter::addRanges( |
149 | DebugAddressRangesVector &&Ranges, |
150 | std::map<DebugAddressRangesVector, uint64_t> &CachedRanges) { |
151 | if (Ranges.empty()) |
152 | return getEmptyRangesOffset(); |
153 | |
154 | const auto RI = CachedRanges.find(x: Ranges); |
155 | if (RI != CachedRanges.end()) |
156 | return RI->second; |
157 | |
158 | const uint64_t EntryOffset = addRanges(Ranges); |
159 | CachedRanges.emplace(args: std::move(Ranges), args: EntryOffset); |
160 | |
161 | return EntryOffset; |
162 | } |
163 | |
164 | uint64_t DebugRangesSectionWriter::addRanges(DebugAddressRangesVector &Ranges) { |
165 | if (Ranges.empty()) |
166 | return getEmptyRangesOffset(); |
167 | |
168 | // Reading the SectionOffset and updating it should be atomic to guarantee |
169 | // unique and correct offsets in patches. |
170 | std::lock_guard<std::mutex> Lock(WriterMutex); |
171 | const uint32_t EntryOffset = RangesBuffer->size(); |
172 | writeAddressRanges(Stream&: *RangesStream, AddressRanges: Ranges); |
173 | |
174 | return EntryOffset; |
175 | } |
176 | |
177 | uint64_t DebugRangesSectionWriter::getSectionOffset() { |
178 | std::lock_guard<std::mutex> Lock(WriterMutex); |
179 | return RangesBuffer->size(); |
180 | } |
181 | |
182 | void DebugRangesSectionWriter::appendToRangeBuffer( |
183 | const DebugBufferVector &CUBuffer) { |
184 | *RangesStream << CUBuffer; |
185 | } |
186 | |
187 | uint64_t DebugRangeListsSectionWriter::addRanges( |
188 | DebugAddressRangesVector &&Ranges, |
189 | std::map<DebugAddressRangesVector, uint64_t> &CachedRanges) { |
190 | return addRanges(Ranges); |
191 | } |
192 | |
193 | struct LocListsRangelistsHeader { |
194 | UnitLengthType UnitLength; // Size of loclist entries section, not including |
195 | // size of header. |
196 | VersionType Version; |
197 | AddressSizeType AddressSize; |
198 | SegmentSelectorType SegmentSelector; |
199 | OffsetEntryCountType OffsetEntryCount; |
200 | }; |
201 | |
202 | static std::unique_ptr<DebugBufferVector> |
203 | getDWARF5Header(const LocListsRangelistsHeader &Header) { |
204 | std::unique_ptr<DebugBufferVector> HeaderBuffer = |
205 | std::make_unique<DebugBufferVector>(); |
206 | std::unique_ptr<raw_svector_ostream> HeaderStream = |
207 | std::make_unique<raw_svector_ostream>(args&: *HeaderBuffer); |
208 | |
209 | // 7.29 length of the set of entries for this compilation unit, not including |
210 | // the length field itself |
211 | const uint32_t HeaderSize = |
212 | getDWARF5RngListLocListHeaderSize() - sizeof(UnitLengthType); |
213 | |
214 | support::endian::write(os&: *HeaderStream, value: Header.UnitLength + HeaderSize, |
215 | endian: llvm::endianness::little); |
216 | support::endian::write(os&: *HeaderStream, value: Header.Version, |
217 | endian: llvm::endianness::little); |
218 | support::endian::write(os&: *HeaderStream, value: Header.AddressSize, |
219 | endian: llvm::endianness::little); |
220 | support::endian::write(os&: *HeaderStream, value: Header.SegmentSelector, |
221 | endian: llvm::endianness::little); |
222 | support::endian::write(os&: *HeaderStream, value: Header.OffsetEntryCount, |
223 | endian: llvm::endianness::little); |
224 | return HeaderBuffer; |
225 | } |
226 | |
227 | struct OffsetEntry { |
228 | uint32_t Index; |
229 | uint32_t StartOffset; |
230 | uint32_t EndOffset; |
231 | }; |
232 | template <typename DebugVector, typename ListEntry, typename DebugAddressEntry> |
233 | static bool emitWithBase(raw_ostream &OS, const DebugVector &Entries, |
234 | DebugAddrWriter &AddrWriter, DWARFUnit &CU, |
235 | uint32_t &Index, const ListEntry BaseAddressx, |
236 | const ListEntry OffsetPair, |
237 | const std::function<void(uint32_t)> &Func) { |
238 | if (Entries.size() < 2) |
239 | return false; |
240 | uint64_t Base = Entries[Index].LowPC; |
241 | std::vector<OffsetEntry> Offsets; |
242 | uint8_t TempBuffer[64]; |
243 | while (Index < Entries.size()) { |
244 | const DebugAddressEntry &Entry = Entries[Index]; |
245 | if (Entry.LowPC == 0) |
246 | break; |
247 | // In case rnglists or loclists are not sorted. |
248 | if (Base > Entry.LowPC) |
249 | break; |
250 | uint32_t StartOffset = Entry.LowPC - Base; |
251 | uint32_t EndOffset = Entry.HighPC - Base; |
252 | if (encodeULEB128(Value: EndOffset, p: TempBuffer) > 2) |
253 | break; |
254 | Offsets.push_back(x: {.Index: Index, .StartOffset: StartOffset, .EndOffset: EndOffset}); |
255 | ++Index; |
256 | } |
257 | |
258 | if (Offsets.size() < 2) { |
259 | Index -= Offsets.size(); |
260 | return false; |
261 | } |
262 | |
263 | support::endian::write(os&: OS, value: static_cast<uint8_t>(BaseAddressx), |
264 | endian: llvm::endianness::little); |
265 | uint32_t BaseIndex = AddrWriter.getIndexFromAddress(Address: Base, CU); |
266 | encodeULEB128(Value: BaseIndex, OS); |
267 | for (auto &OffsetEntry : Offsets) { |
268 | support::endian::write(os&: OS, value: static_cast<uint8_t>(OffsetPair), |
269 | endian: llvm::endianness::little); |
270 | encodeULEB128(Value: OffsetEntry.StartOffset, OS); |
271 | encodeULEB128(Value: OffsetEntry.EndOffset, OS); |
272 | Func(OffsetEntry.Index); |
273 | } |
274 | return true; |
275 | } |
276 | |
277 | uint64_t |
278 | DebugRangeListsSectionWriter::addRanges(DebugAddressRangesVector &Ranges) { |
279 | std::lock_guard<std::mutex> Lock(WriterMutex); |
280 | |
281 | RangeEntries.push_back(Elt: CurrentOffset); |
282 | std::sort( |
283 | first: Ranges.begin(), last: Ranges.end(), |
284 | comp: [](const DebugAddressRange &R1, const DebugAddressRange &R2) -> bool { |
285 | return R1.LowPC < R2.LowPC; |
286 | }); |
287 | for (unsigned I = 0; I < Ranges.size();) { |
288 | if (emitWithBase<DebugAddressRangesVector, dwarf::RnglistEntries, |
289 | DebugAddressRange>(OS&: *CUBodyStream, Entries: Ranges, AddrWriter&: *AddrWriter, CU&: *CU, |
290 | Index&: I, BaseAddressx: dwarf::DW_RLE_base_addressx, |
291 | OffsetPair: dwarf::DW_RLE_offset_pair, |
292 | Func: [](uint32_t Index) -> void {})) |
293 | continue; |
294 | |
295 | const DebugAddressRange &Range = Ranges[I]; |
296 | support::endian::write(os&: *CUBodyStream, |
297 | value: static_cast<uint8_t>(dwarf::DW_RLE_startx_length), |
298 | endian: llvm::endianness::little); |
299 | uint32_t Index = AddrWriter->getIndexFromAddress(Address: Range.LowPC, CU&: *CU); |
300 | encodeULEB128(Value: Index, OS&: *CUBodyStream); |
301 | encodeULEB128(Value: Range.HighPC - Range.LowPC, OS&: *CUBodyStream); |
302 | ++I; |
303 | } |
304 | |
305 | support::endian::write(os&: *CUBodyStream, |
306 | value: static_cast<uint8_t>(dwarf::DW_RLE_end_of_list), |
307 | endian: llvm::endianness::little); |
308 | CurrentOffset = CUBodyBuffer->size(); |
309 | return RangeEntries.size() - 1; |
310 | } |
311 | |
312 | void DebugRangeListsSectionWriter::finalizeSection() { |
313 | std::unique_ptr<DebugBufferVector> CUArrayBuffer = |
314 | std::make_unique<DebugBufferVector>(); |
315 | std::unique_ptr<raw_svector_ostream> CUArrayStream = |
316 | std::make_unique<raw_svector_ostream>(args&: *CUArrayBuffer); |
317 | constexpr uint32_t SizeOfArrayEntry = 4; |
318 | const uint32_t SizeOfArraySection = RangeEntries.size() * SizeOfArrayEntry; |
319 | for (uint32_t Offset : RangeEntries) |
320 | support::endian::write(os&: *CUArrayStream, value: Offset + SizeOfArraySection, |
321 | endian: llvm::endianness::little); |
322 | |
323 | std::unique_ptr<DebugBufferVector> Header = getDWARF5Header( |
324 | Header: {.UnitLength: static_cast<uint32_t>(SizeOfArraySection + CUBodyBuffer->size()), .Version: 5, .AddressSize: 8, |
325 | .SegmentSelector: 0, .OffsetEntryCount: static_cast<uint32_t>(RangeEntries.size())}); |
326 | *RangesStream << *Header; |
327 | *RangesStream << *CUArrayBuffer; |
328 | *RangesStream << *CUBodyBuffer; |
329 | } |
330 | |
331 | void DebugRangeListsSectionWriter::initSection(DWARFUnit &Unit) { |
332 | CUBodyBuffer = std::make_unique<DebugBufferVector>(); |
333 | CUBodyStream = std::make_unique<raw_svector_ostream>(args&: *CUBodyBuffer); |
334 | RangeEntries.clear(); |
335 | CurrentOffset = 0; |
336 | CU = &Unit; |
337 | } |
338 | |
339 | void DebugARangesSectionWriter::addCURanges(uint64_t CUOffset, |
340 | DebugAddressRangesVector &&Ranges) { |
341 | std::lock_guard<std::mutex> Lock(CUAddressRangesMutex); |
342 | CUAddressRanges.emplace(args&: CUOffset, args: std::move(Ranges)); |
343 | } |
344 | |
345 | void DebugARangesSectionWriter::writeARangesSection( |
346 | raw_svector_ostream &RangesStream, const CUOffsetMap &CUMap) const { |
347 | // For reference on the format of the .debug_aranges section, see the DWARF4 |
348 | // specification, section 6.1.4 Lookup by Address |
349 | // http://www.dwarfstd.org/doc/DWARF4.pdf |
350 | for (const auto &CUOffsetAddressRangesPair : CUAddressRanges) { |
351 | const uint64_t Offset = CUOffsetAddressRangesPair.first; |
352 | const DebugAddressRangesVector &AddressRanges = |
353 | CUOffsetAddressRangesPair.second; |
354 | |
355 | // Emit header. |
356 | |
357 | // Size of this set: 8 (size of the header) + 4 (padding after header) |
358 | // + 2*sizeof(uint64_t) bytes for each of the ranges, plus an extra |
359 | // pair of uint64_t's for the terminating, zero-length range. |
360 | // Does not include size field itself. |
361 | uint32_t Size = 8 + 4 + 2 * sizeof(uint64_t) * (AddressRanges.size() + 1); |
362 | |
363 | // Header field #1: set size. |
364 | support::endian::write(os&: RangesStream, value: Size, endian: llvm::endianness::little); |
365 | |
366 | // Header field #2: version number, 2 as per the specification. |
367 | support::endian::write(os&: RangesStream, value: static_cast<uint16_t>(2), |
368 | endian: llvm::endianness::little); |
369 | |
370 | assert(CUMap.count(Offset) && "Original CU offset is not found in CU Map"); |
371 | // Header field #3: debug info offset of the correspondent compile unit. |
372 | support::endian::write( |
373 | os&: RangesStream, value: static_cast<uint32_t>(CUMap.find(x: Offset)->second.Offset), |
374 | endian: llvm::endianness::little); |
375 | |
376 | // Header field #4: address size. |
377 | // 8 since we only write ELF64 binaries for now. |
378 | RangesStream << char(8); |
379 | |
380 | // Header field #5: segment size of target architecture. |
381 | RangesStream << char(0); |
382 | |
383 | // Padding before address table - 4 bytes in the 64-bit-pointer case. |
384 | support::endian::write(os&: RangesStream, value: static_cast<uint32_t>(0), |
385 | endian: llvm::endianness::little); |
386 | |
387 | writeAddressRanges(Stream&: RangesStream, AddressRanges, WriteRelativeRanges: true); |
388 | } |
389 | } |
390 | |
391 | DebugAddrWriter::DebugAddrWriter(BinaryContext *BC, |
392 | const uint8_t AddressByteSize) |
393 | : BC(BC), AddressByteSize(AddressByteSize) { |
394 | Buffer = std::make_unique<AddressSectionBuffer>(); |
395 | AddressStream = std::make_unique<raw_svector_ostream>(args&: *Buffer); |
396 | } |
397 | |
398 | void DebugAddrWriter::AddressForDWOCU::dump() { |
399 | std::vector<IndexAddressPair> SortedMap(indexToAddressBegin(), |
400 | indexToAdddessEnd()); |
401 | // Sorting address in increasing order of indices. |
402 | llvm::sort(C&: SortedMap, Comp: llvm::less_first()); |
403 | for (auto &Pair : SortedMap) |
404 | dbgs() << Twine::utohexstr(Val: Pair.second) << "\t"<< Pair.first << "\n"; |
405 | } |
406 | uint32_t DebugAddrWriter::getIndexFromAddress(uint64_t Address, DWARFUnit &CU) { |
407 | std::lock_guard<std::mutex> Lock(WriterMutex); |
408 | auto Entry = Map.find(Address); |
409 | if (Entry == Map.end()) { |
410 | auto Index = Map.getNextIndex(); |
411 | Entry = Map.insert(Address, Index).first; |
412 | } |
413 | return Entry->second; |
414 | } |
415 | |
416 | static void updateAddressBase(DIEBuilder &DIEBlder, DebugAddrWriter &AddrWriter, |
417 | DWARFUnit &CU, const uint64_t Offset) { |
418 | DIE *Die = DIEBlder.getUnitDIEbyUnit(DU: CU); |
419 | DIEValue GnuAddrBaseAttrInfo = Die->findAttribute(Attribute: dwarf::DW_AT_GNU_addr_base); |
420 | DIEValue AddrBaseAttrInfo = Die->findAttribute(Attribute: dwarf::DW_AT_addr_base); |
421 | dwarf::Form BaseAttrForm; |
422 | dwarf::Attribute BaseAttr; |
423 | // For cases where Skeleton CU does not have DW_AT_GNU_addr_base |
424 | if (!GnuAddrBaseAttrInfo && CU.getVersion() < 5) |
425 | return; |
426 | |
427 | if (GnuAddrBaseAttrInfo) { |
428 | BaseAttrForm = GnuAddrBaseAttrInfo.getForm(); |
429 | BaseAttr = GnuAddrBaseAttrInfo.getAttribute(); |
430 | } |
431 | |
432 | if (AddrBaseAttrInfo) { |
433 | BaseAttrForm = AddrBaseAttrInfo.getForm(); |
434 | BaseAttr = AddrBaseAttrInfo.getAttribute(); |
435 | } |
436 | |
437 | if (GnuAddrBaseAttrInfo || AddrBaseAttrInfo) { |
438 | DIEBlder.replaceValue(Die, Attribute: BaseAttr, Form: BaseAttrForm, NewValue: DIEInteger(Offset)); |
439 | } else if (CU.getVersion() >= 5) { |
440 | // A case where we were not using .debug_addr section, but after update |
441 | // now using it. |
442 | DIEBlder.addValue(Die, Attribute: dwarf::DW_AT_addr_base, Form: dwarf::DW_FORM_sec_offset, |
443 | Value: DIEInteger(Offset)); |
444 | } |
445 | } |
446 | |
447 | void DebugAddrWriter::updateAddrBase(DIEBuilder &DIEBlder, DWARFUnit &CU, |
448 | const uint64_t Offset) { |
449 | updateAddressBase(DIEBlder, AddrWriter&: *this, CU, Offset); |
450 | } |
451 | |
452 | std::optional<uint64_t> DebugAddrWriter::finalize(const size_t BufferSize) { |
453 | if (Map.begin() == Map.end()) |
454 | return std::nullopt; |
455 | std::vector<IndexAddressPair> SortedMap(Map.indexToAddressBegin(), |
456 | Map.indexToAdddessEnd()); |
457 | // Sorting address in increasing order of indices. |
458 | llvm::sort(C&: SortedMap, Comp: llvm::less_first()); |
459 | |
460 | uint32_t Counter = 0; |
461 | auto WriteAddress = [&](uint64_t Address) -> void { |
462 | ++Counter; |
463 | switch (AddressByteSize) { |
464 | default: |
465 | assert(false && "Address Size is invalid."); |
466 | break; |
467 | case 4: |
468 | support::endian::write(os&: *AddressStream, value: static_cast<uint32_t>(Address), |
469 | endian: llvm::endianness::little); |
470 | break; |
471 | case 8: |
472 | support::endian::write(os&: *AddressStream, value: Address, endian: llvm::endianness::little); |
473 | break; |
474 | } |
475 | }; |
476 | |
477 | for (const IndexAddressPair &Val : SortedMap) { |
478 | while (Val.first > Counter) |
479 | WriteAddress(0); |
480 | WriteAddress(Val.second); |
481 | } |
482 | return std::nullopt; |
483 | } |
484 | |
485 | void DebugAddrWriterDwarf5::updateAddrBase(DIEBuilder &DIEBlder, DWARFUnit &CU, |
486 | const uint64_t Offset) { |
487 | /// Header for DWARF5 has size 8, so we add it to the offset. |
488 | updateAddressBase(DIEBlder, AddrWriter&: *this, CU, Offset: Offset + HeaderSize); |
489 | } |
490 | |
491 | DenseMap<uint64_t, uint64_t> DebugAddrWriter::UnmodifiedAddressOffsets; |
492 | |
493 | std::optional<uint64_t> |
494 | DebugAddrWriterDwarf5::finalize(const size_t BufferSize) { |
495 | // Need to layout all sections within .debug_addr |
496 | // Within each section sort Address by index. |
497 | const endianness Endian = BC->DwCtx->isLittleEndian() |
498 | ? llvm::endianness::little |
499 | : llvm::endianness::big; |
500 | const DWARFSection &AddrSec = BC->DwCtx->getDWARFObj().getAddrSection(); |
501 | DWARFDataExtractor AddrData(BC->DwCtx->getDWARFObj(), AddrSec, |
502 | Endian == llvm::endianness::little, 0); |
503 | DWARFDebugAddrTable AddrTable; |
504 | DIDumpOptions DumpOpts; |
505 | // A case where CU has entry in .debug_addr, but we don't modify addresses |
506 | // for it. |
507 | if (Map.begin() == Map.end()) { |
508 | if (!AddrOffsetSectionBase) |
509 | return std::nullopt; |
510 | // Address base offset is to the first entry. |
511 | // The size of header is 8 bytes. |
512 | uint64_t Offset = *AddrOffsetSectionBase - HeaderSize; |
513 | auto Iter = UnmodifiedAddressOffsets.find(Val: Offset); |
514 | if (Iter != UnmodifiedAddressOffsets.end()) |
515 | return Iter->second; |
516 | UnmodifiedAddressOffsets[Offset] = BufferSize; |
517 | if (Error Err = AddrTable.extract(Data: AddrData, OffsetPtr: &Offset, CUVersion: 5, CUAddrSize: AddressByteSize, |
518 | WarnCallback: DumpOpts.WarningHandler)) { |
519 | DumpOpts.RecoverableErrorHandler(std::move(Err)); |
520 | return std::nullopt; |
521 | } |
522 | uint32_t Index = 0; |
523 | for (uint64_t Addr : AddrTable.getAddressEntries()) |
524 | Map.insert(Address: Addr, Index: Index++); |
525 | } |
526 | |
527 | std::vector<IndexAddressPair> SortedMap(Map.indexToAddressBegin(), |
528 | Map.indexToAdddessEnd()); |
529 | // Sorting address in increasing order of indices. |
530 | llvm::sort(C&: SortedMap, Comp: llvm::less_first()); |
531 | // Writing out Header |
532 | const uint32_t Length = SortedMap.size() * AddressByteSize + 4; |
533 | support::endian::write(os&: *AddressStream, value: Length, endian: Endian); |
534 | support::endian::write(os&: *AddressStream, value: static_cast<uint16_t>(5), endian: Endian); |
535 | support::endian::write(os&: *AddressStream, value: static_cast<uint8_t>(AddressByteSize), |
536 | endian: Endian); |
537 | support::endian::write(os&: *AddressStream, value: static_cast<uint8_t>(0), endian: Endian); |
538 | |
539 | uint32_t Counter = 0; |
540 | auto writeAddress = [&](uint64_t Address) -> void { |
541 | ++Counter; |
542 | switch (AddressByteSize) { |
543 | default: |
544 | llvm_unreachable("Address Size is invalid."); |
545 | break; |
546 | case 4: |
547 | support::endian::write(os&: *AddressStream, value: static_cast<uint32_t>(Address), |
548 | endian: Endian); |
549 | break; |
550 | case 8: |
551 | support::endian::write(os&: *AddressStream, value: Address, endian: Endian); |
552 | break; |
553 | } |
554 | }; |
555 | |
556 | for (const IndexAddressPair &Val : SortedMap) { |
557 | while (Val.first > Counter) |
558 | writeAddress(0); |
559 | writeAddress(Val.second); |
560 | } |
561 | return std::nullopt; |
562 | } |
563 | |
564 | void DebugLocWriter::init() { |
565 | LocBuffer = std::make_unique<DebugBufferVector>(); |
566 | LocStream = std::make_unique<raw_svector_ostream>(args&: *LocBuffer); |
567 | // Writing out empty location list to which all references to empty location |
568 | // lists will point. |
569 | if (!LocSectionOffset && DwarfVersion < 5) { |
570 | const char Zeroes[16] = {0}; |
571 | *LocStream << StringRef(Zeroes, 16); |
572 | LocSectionOffset += 16; |
573 | } |
574 | } |
575 | |
576 | uint32_t DebugLocWriter::LocSectionOffset = 0; |
577 | void DebugLocWriter::addList(DIEBuilder &DIEBldr, DIE &Die, DIEValue &AttrInfo, |
578 | DebugLocationsVector &LocList) { |
579 | if (LocList.empty()) { |
580 | replaceLocValbyForm(DIEBldr, Die, DIEVal: AttrInfo, Format: AttrInfo.getForm(), |
581 | NewVal: DebugLocWriter::EmptyListOffset); |
582 | return; |
583 | } |
584 | // Since there is a separate DebugLocWriter for each thread, |
585 | // we don't need a lock to read the SectionOffset and update it. |
586 | const uint32_t EntryOffset = LocSectionOffset; |
587 | |
588 | for (const DebugLocationEntry &Entry : LocList) { |
589 | support::endian::write(os&: *LocStream, value: static_cast<uint64_t>(Entry.LowPC), |
590 | endian: llvm::endianness::little); |
591 | support::endian::write(os&: *LocStream, value: static_cast<uint64_t>(Entry.HighPC), |
592 | endian: llvm::endianness::little); |
593 | support::endian::write(os&: *LocStream, value: static_cast<uint16_t>(Entry.Expr.size()), |
594 | endian: llvm::endianness::little); |
595 | *LocStream << StringRef(reinterpret_cast<const char *>(Entry.Expr.data()), |
596 | Entry.Expr.size()); |
597 | LocSectionOffset += 2 * 8 + 2 + Entry.Expr.size(); |
598 | } |
599 | LocStream->write_zeros(NumZeros: 16); |
600 | LocSectionOffset += 16; |
601 | LocListDebugInfoPatches.push_back(x: {.DebugInfoAttrOffset: 0xdeadbeee, .LocListOffset: EntryOffset}); // never seen |
602 | // use |
603 | replaceLocValbyForm(DIEBldr, Die, DIEVal: AttrInfo, Format: AttrInfo.getForm(), NewVal: EntryOffset); |
604 | } |
605 | |
606 | std::unique_ptr<DebugBufferVector> DebugLocWriter::getBuffer() { |
607 | return std::move(LocBuffer); |
608 | } |
609 | |
610 | // DWARF 4: 2.6.2 |
611 | void DebugLocWriter::finalize(DIEBuilder &DIEBldr, DIE &Die) {} |
612 | |
613 | static void writeEmptyListDwarf5(raw_svector_ostream &Stream) { |
614 | support::endian::write(os&: Stream, value: static_cast<uint32_t>(4), |
615 | endian: llvm::endianness::little); |
616 | support::endian::write(os&: Stream, value: static_cast<uint8_t>(dwarf::DW_LLE_start_end), |
617 | endian: llvm::endianness::little); |
618 | |
619 | const char Zeroes[16] = {0}; |
620 | Stream << StringRef(Zeroes, 16); |
621 | encodeULEB128(Value: 0, OS&: Stream); |
622 | support::endian::write(os&: Stream, |
623 | value: static_cast<uint8_t>(dwarf::DW_LLE_end_of_list), |
624 | endian: llvm::endianness::little); |
625 | } |
626 | |
627 | static void writeLegacyLocList(DIEValue &AttrInfo, |
628 | DebugLocationsVector &LocList, |
629 | DIEBuilder &DIEBldr, DIE &Die, |
630 | DebugAddrWriter &AddrWriter, |
631 | DebugBufferVector &LocBuffer, DWARFUnit &CU, |
632 | raw_svector_ostream &LocStream) { |
633 | if (LocList.empty()) { |
634 | replaceLocValbyForm(DIEBldr, Die, DIEVal: AttrInfo, Format: AttrInfo.getForm(), |
635 | NewVal: DebugLocWriter::EmptyListOffset); |
636 | return; |
637 | } |
638 | |
639 | const uint32_t EntryOffset = LocBuffer.size(); |
640 | for (const DebugLocationEntry &Entry : LocList) { |
641 | support::endian::write(os&: LocStream, |
642 | value: static_cast<uint8_t>(dwarf::DW_LLE_startx_length), |
643 | endian: llvm::endianness::little); |
644 | const uint32_t Index = AddrWriter.getIndexFromAddress(Address: Entry.LowPC, CU); |
645 | encodeULEB128(Value: Index, OS&: LocStream); |
646 | |
647 | support::endian::write(os&: LocStream, |
648 | value: static_cast<uint32_t>(Entry.HighPC - Entry.LowPC), |
649 | endian: llvm::endianness::little); |
650 | support::endian::write(os&: LocStream, value: static_cast<uint16_t>(Entry.Expr.size()), |
651 | endian: llvm::endianness::little); |
652 | LocStream << StringRef(reinterpret_cast<const char *>(Entry.Expr.data()), |
653 | Entry.Expr.size()); |
654 | } |
655 | support::endian::write(os&: LocStream, |
656 | value: static_cast<uint8_t>(dwarf::DW_LLE_end_of_list), |
657 | endian: llvm::endianness::little); |
658 | replaceLocValbyForm(DIEBldr, Die, DIEVal: AttrInfo, Format: AttrInfo.getForm(), NewVal: EntryOffset); |
659 | } |
660 | |
661 | static void writeDWARF5LocList(uint32_t &NumberOfEntries, DIEValue &AttrInfo, |
662 | DebugLocationsVector &LocList, DIE &Die, |
663 | DIEBuilder &DIEBldr, DebugAddrWriter &AddrWriter, |
664 | DebugBufferVector &LocBodyBuffer, |
665 | std::vector<uint32_t> &RelativeLocListOffsets, |
666 | DWARFUnit &CU, |
667 | raw_svector_ostream &LocBodyStream) { |
668 | |
669 | replaceLocValbyForm(DIEBldr, Die, DIEVal: AttrInfo, Format: dwarf::DW_FORM_loclistx, |
670 | NewVal: NumberOfEntries); |
671 | |
672 | RelativeLocListOffsets.push_back(x: LocBodyBuffer.size()); |
673 | ++NumberOfEntries; |
674 | if (LocList.empty()) { |
675 | writeEmptyListDwarf5(Stream&: LocBodyStream); |
676 | return; |
677 | } |
678 | |
679 | auto writeExpression = [&](uint32_t Index) -> void { |
680 | const DebugLocationEntry &Entry = LocList[Index]; |
681 | encodeULEB128(Value: Entry.Expr.size(), OS&: LocBodyStream); |
682 | LocBodyStream << StringRef( |
683 | reinterpret_cast<const char *>(Entry.Expr.data()), Entry.Expr.size()); |
684 | }; |
685 | for (unsigned I = 0; I < LocList.size();) { |
686 | if (emitWithBase<DebugLocationsVector, dwarf::LoclistEntries, |
687 | DebugLocationEntry>(OS&: LocBodyStream, Entries: LocList, AddrWriter, CU, |
688 | Index&: I, BaseAddressx: dwarf::DW_LLE_base_addressx, |
689 | OffsetPair: dwarf::DW_LLE_offset_pair, |
690 | Func: writeExpression)) |
691 | continue; |
692 | |
693 | const DebugLocationEntry &Entry = LocList[I]; |
694 | support::endian::write(os&: LocBodyStream, |
695 | value: static_cast<uint8_t>(dwarf::DW_LLE_startx_length), |
696 | endian: llvm::endianness::little); |
697 | const uint32_t Index = AddrWriter.getIndexFromAddress(Address: Entry.LowPC, CU); |
698 | encodeULEB128(Value: Index, OS&: LocBodyStream); |
699 | encodeULEB128(Value: Entry.HighPC - Entry.LowPC, OS&: LocBodyStream); |
700 | writeExpression(I); |
701 | ++I; |
702 | } |
703 | |
704 | support::endian::write(os&: LocBodyStream, |
705 | value: static_cast<uint8_t>(dwarf::DW_LLE_end_of_list), |
706 | endian: llvm::endianness::little); |
707 | } |
708 | |
709 | void DebugLoclistWriter::addList(DIEBuilder &DIEBldr, DIE &Die, |
710 | DIEValue &AttrInfo, |
711 | DebugLocationsVector &LocList) { |
712 | if (DwarfVersion < 5) |
713 | writeLegacyLocList(AttrInfo, LocList, DIEBldr, Die, AddrWriter, LocBuffer&: *LocBuffer, |
714 | CU, LocStream&: *LocStream); |
715 | else |
716 | writeDWARF5LocList(NumberOfEntries, AttrInfo, LocList, Die, DIEBldr, |
717 | AddrWriter, LocBodyBuffer&: *LocBodyBuffer, RelativeLocListOffsets, CU, |
718 | LocBodyStream&: *LocBodyStream); |
719 | } |
720 | |
721 | uint32_t DebugLoclistWriter::LoclistBaseOffset = 0; |
722 | void DebugLoclistWriter::finalizeDWARF5(DIEBuilder &DIEBldr, DIE &Die) { |
723 | if (LocBodyBuffer->empty()) { |
724 | DIEValue LocListBaseAttrInfo = |
725 | Die.findAttribute(Attribute: dwarf::DW_AT_loclists_base); |
726 | // Pointing to first one, because it doesn't matter. There are no uses of it |
727 | // in this CU. |
728 | if (!isSplitDwarf() && LocListBaseAttrInfo.getType()) |
729 | DIEBldr.replaceValue(Die: &Die, Attribute: dwarf::DW_AT_loclists_base, |
730 | Form: LocListBaseAttrInfo.getForm(), |
731 | NewValue: DIEInteger(getDWARF5RngListLocListHeaderSize())); |
732 | return; |
733 | } |
734 | |
735 | std::unique_ptr<DebugBufferVector> LocArrayBuffer = |
736 | std::make_unique<DebugBufferVector>(); |
737 | std::unique_ptr<raw_svector_ostream> LocArrayStream = |
738 | std::make_unique<raw_svector_ostream>(args&: *LocArrayBuffer); |
739 | |
740 | const uint32_t SizeOfArraySection = NumberOfEntries * sizeof(uint32_t); |
741 | // Write out IndexArray |
742 | for (uint32_t RelativeOffset : RelativeLocListOffsets) |
743 | support::endian::write( |
744 | os&: *LocArrayStream, |
745 | value: static_cast<uint32_t>(SizeOfArraySection + RelativeOffset), |
746 | endian: llvm::endianness::little); |
747 | |
748 | std::unique_ptr<DebugBufferVector> Header = getDWARF5Header( |
749 | Header: {.UnitLength: static_cast<uint32_t>(SizeOfArraySection + LocBodyBuffer->size()), .Version: 5, .AddressSize: 8, |
750 | .SegmentSelector: 0, .OffsetEntryCount: NumberOfEntries}); |
751 | *LocStream << *Header; |
752 | *LocStream << *LocArrayBuffer; |
753 | *LocStream << *LocBodyBuffer; |
754 | |
755 | if (!isSplitDwarf()) { |
756 | DIEValue LocListBaseAttrInfo = |
757 | Die.findAttribute(Attribute: dwarf::DW_AT_loclists_base); |
758 | if (LocListBaseAttrInfo.getType()) { |
759 | DIEBldr.replaceValue( |
760 | Die: &Die, Attribute: dwarf::DW_AT_loclists_base, Form: LocListBaseAttrInfo.getForm(), |
761 | NewValue: DIEInteger(LoclistBaseOffset + getDWARF5RngListLocListHeaderSize())); |
762 | } else { |
763 | DIEBldr.addValue(Die: &Die, Attribute: dwarf::DW_AT_loclists_base, |
764 | Form: dwarf::DW_FORM_sec_offset, |
765 | Value: DIEInteger(LoclistBaseOffset + Header->size())); |
766 | } |
767 | LoclistBaseOffset += LocBuffer->size(); |
768 | } |
769 | clearList(List&: RelativeLocListOffsets); |
770 | clearList(List&: *LocArrayBuffer); |
771 | clearList(List&: *LocBodyBuffer); |
772 | } |
773 | |
774 | void DebugLoclistWriter::finalize(DIEBuilder &DIEBldr, DIE &Die) { |
775 | if (DwarfVersion >= 5) |
776 | finalizeDWARF5(DIEBldr, Die); |
777 | } |
778 | |
779 | static std::string encodeLE(size_t ByteSize, uint64_t NewValue) { |
780 | std::string LE64(ByteSize, 0); |
781 | for (size_t I = 0; I < ByteSize; ++I) { |
782 | LE64[I] = NewValue & 0xff; |
783 | NewValue >>= 8; |
784 | } |
785 | return LE64; |
786 | } |
787 | |
788 | void SimpleBinaryPatcher::addBinaryPatch(uint64_t Offset, |
789 | std::string &&NewValue, |
790 | uint32_t OldValueSize) { |
791 | Patches.emplace_back(args&: Offset, args: std::move(NewValue)); |
792 | } |
793 | |
794 | void SimpleBinaryPatcher::addBytePatch(uint64_t Offset, uint8_t Value) { |
795 | auto Str = std::string(1, Value); |
796 | Patches.emplace_back(args&: Offset, args: std::move(Str)); |
797 | } |
798 | |
799 | void SimpleBinaryPatcher::addLEPatch(uint64_t Offset, uint64_t NewValue, |
800 | size_t ByteSize) { |
801 | Patches.emplace_back(args&: Offset, args: encodeLE(ByteSize, NewValue)); |
802 | } |
803 | |
804 | void SimpleBinaryPatcher::addUDataPatch(uint64_t Offset, uint64_t Value, |
805 | uint32_t OldValueSize) { |
806 | std::string Buff; |
807 | raw_string_ostream OS(Buff); |
808 | encodeULEB128(Value, OS, PadTo: OldValueSize); |
809 | |
810 | Patches.emplace_back(args&: Offset, args: std::move(Buff)); |
811 | } |
812 | |
813 | void SimpleBinaryPatcher::addLE64Patch(uint64_t Offset, uint64_t NewValue) { |
814 | addLEPatch(Offset, NewValue, ByteSize: 8); |
815 | } |
816 | |
817 | void SimpleBinaryPatcher::addLE32Patch(uint64_t Offset, uint32_t NewValue, |
818 | uint32_t OldValueSize) { |
819 | addLEPatch(Offset, NewValue, ByteSize: 4); |
820 | } |
821 | |
822 | std::string SimpleBinaryPatcher::patchBinary(StringRef BinaryContents) { |
823 | std::string BinaryContentsStr = std::string(BinaryContents); |
824 | for (const auto &Patch : Patches) { |
825 | uint32_t Offset = Patch.first; |
826 | const std::string &ByteSequence = Patch.second; |
827 | assert(Offset + ByteSequence.size() <= BinaryContents.size() && |
828 | "Applied patch runs over binary size."); |
829 | for (uint64_t I = 0, Size = ByteSequence.size(); I < Size; ++I) { |
830 | BinaryContentsStr[Offset + I] = ByteSequence[I]; |
831 | } |
832 | } |
833 | return BinaryContentsStr; |
834 | } |
835 | |
836 | void DebugStrOffsetsWriter::initialize(DWARFUnit &Unit) { |
837 | if (Unit.getVersion() < 5) |
838 | return; |
839 | const DWARFSection &StrOffsetsSection = Unit.getStringOffsetSection(); |
840 | const std::optional<StrOffsetsContributionDescriptor> &Contr = |
841 | Unit.getStringOffsetsTableContribution(); |
842 | if (!Contr) |
843 | return; |
844 | const uint8_t DwarfOffsetByteSize = Contr->getDwarfOffsetByteSize(); |
845 | assert(DwarfOffsetByteSize == 4 && |
846 | "Dwarf String Offsets Byte Size is not supported."); |
847 | StrOffsets.reserve(N: Contr->Size); |
848 | for (uint64_t Offset = 0; Offset < Contr->Size; Offset += DwarfOffsetByteSize) |
849 | StrOffsets.push_back(Elt: support::endian::read32le( |
850 | P: StrOffsetsSection.Data.data() + Contr->Base + Offset)); |
851 | } |
852 | |
853 | void DebugStrOffsetsWriter::updateAddressMap(uint32_t Index, uint32_t Address, |
854 | const DWARFUnit &Unit) { |
855 | assert(DebugStrOffsetFinalized.count(Unit.getOffset()) == 0 && |
856 | "Cannot update address map since debug_str_offsets was already " |
857 | "finalized for this CU."); |
858 | IndexToAddressMap[Index] = Address; |
859 | StrOffsetSectionWasModified = true; |
860 | } |
861 | |
862 | void DebugStrOffsetsWriter::finalizeSection(DWARFUnit &Unit, |
863 | DIEBuilder &DIEBldr) { |
864 | std::optional<AttrInfo> AttrVal = |
865 | findAttributeInfo(DIE: Unit.getUnitDIE(), Attr: dwarf::DW_AT_str_offsets_base); |
866 | if (!AttrVal && !Unit.isDWOUnit()) |
867 | return; |
868 | std::optional<uint64_t> Val = std::nullopt; |
869 | if (AttrVal) { |
870 | Val = AttrVal->V.getAsSectionOffset(); |
871 | } else { |
872 | if (!Unit.isDWOUnit()) |
873 | BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: " |
874 | "DW_AT_str_offsets_base Value not present\n"; |
875 | Val = 0; |
876 | } |
877 | DIE &Die = *DIEBldr.getUnitDIEbyUnit(DU: Unit); |
878 | DIEValue StrListBaseAttrInfo = |
879 | Die.findAttribute(Attribute: dwarf::DW_AT_str_offsets_base); |
880 | auto RetVal = ProcessedBaseOffsets.find(x: *Val); |
881 | // Handling re-use of str-offsets section. |
882 | if (RetVal == ProcessedBaseOffsets.end() || StrOffsetSectionWasModified) { |
883 | initialize(Unit); |
884 | // Update String Offsets that were modified. |
885 | for (const auto &Entry : IndexToAddressMap) |
886 | StrOffsets[Entry.first] = Entry.second; |
887 | // Writing out the header for each section. |
888 | support::endian::write(os&: *StrOffsetsStream, |
889 | value: static_cast<uint32_t>(StrOffsets.size() * 4 + 4), |
890 | endian: llvm::endianness::little); |
891 | support::endian::write(os&: *StrOffsetsStream, value: static_cast<uint16_t>(5), |
892 | endian: llvm::endianness::little); |
893 | support::endian::write(os&: *StrOffsetsStream, value: static_cast<uint16_t>(0), |
894 | endian: llvm::endianness::little); |
895 | |
896 | uint64_t BaseOffset = StrOffsetsBuffer->size(); |
897 | ProcessedBaseOffsets[*Val] = BaseOffset; |
898 | if (StrListBaseAttrInfo.getType()) |
899 | DIEBldr.replaceValue(Die: &Die, Attribute: dwarf::DW_AT_str_offsets_base, |
900 | Form: StrListBaseAttrInfo.getForm(), |
901 | NewValue: DIEInteger(BaseOffset)); |
902 | for (const uint32_t Offset : StrOffsets) |
903 | support::endian::write(os&: *StrOffsetsStream, value: Offset, |
904 | endian: llvm::endianness::little); |
905 | } else { |
906 | DIEBldr.replaceValue(Die: &Die, Attribute: dwarf::DW_AT_str_offsets_base, |
907 | Form: StrListBaseAttrInfo.getForm(), |
908 | NewValue: DIEInteger(RetVal->second)); |
909 | } |
910 | |
911 | StrOffsetSectionWasModified = false; |
912 | assert(DebugStrOffsetFinalized.insert(Unit.getOffset()).second && |
913 | "debug_str_offsets was already finalized for this CU."); |
914 | clear(); |
915 | } |
916 | |
917 | void DebugStrWriter::create() { |
918 | StrBuffer = std::make_unique<DebugStrBufferVector>(); |
919 | StrStream = std::make_unique<raw_svector_ostream>(args&: *StrBuffer); |
920 | } |
921 | |
922 | void DebugStrWriter::initialize() { |
923 | StringRef StrSection; |
924 | if (IsDWO) |
925 | StrSection = DwCtx.getDWARFObj().getStrDWOSection(); |
926 | else |
927 | StrSection = DwCtx.getDWARFObj().getStrSection(); |
928 | (*StrStream) << StrSection; |
929 | } |
930 | |
931 | uint32_t DebugStrWriter::addString(StringRef Str) { |
932 | std::lock_guard<std::mutex> Lock(WriterMutex); |
933 | if (StrBuffer->empty()) |
934 | initialize(); |
935 | auto Offset = StrBuffer->size(); |
936 | (*StrStream) << Str; |
937 | StrStream->write_zeros(NumZeros: 1); |
938 | return Offset; |
939 | } |
940 | |
941 | static void emitDwarfSetLineAddrAbs(MCStreamer &OS, |
942 | MCDwarfLineTableParams Params, |
943 | int64_t LineDelta, uint64_t Address, |
944 | int PointerSize) { |
945 | // emit the sequence to set the address |
946 | OS.emitIntValue(Value: dwarf::DW_LNS_extended_op, Size: 1); |
947 | OS.emitULEB128IntValue(Value: PointerSize + 1); |
948 | OS.emitIntValue(Value: dwarf::DW_LNE_set_address, Size: 1); |
949 | OS.emitIntValue(Value: Address, Size: PointerSize); |
950 | |
951 | // emit the sequence for the LineDelta (from 1) and a zero address delta. |
952 | MCDwarfLineAddr::Emit(MCOS: &OS, Params, LineDelta, AddrDelta: 0); |
953 | } |
954 | |
955 | static inline void emitBinaryDwarfLineTable( |
956 | MCStreamer *MCOS, MCDwarfLineTableParams Params, |
957 | const DWARFDebugLine::LineTable *Table, |
958 | const std::vector<DwarfLineTable::RowSequence> &InputSequences) { |
959 | if (InputSequences.empty()) |
960 | return; |
961 | |
962 | constexpr uint64_t InvalidAddress = UINT64_MAX; |
963 | unsigned FileNum = 1; |
964 | unsigned LastLine = 1; |
965 | unsigned Column = 0; |
966 | unsigned Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; |
967 | unsigned Isa = 0; |
968 | unsigned Discriminator = 0; |
969 | uint64_t LastAddress = InvalidAddress; |
970 | uint64_t PrevEndOfSequence = InvalidAddress; |
971 | const MCAsmInfo *AsmInfo = MCOS->getContext().getAsmInfo(); |
972 | |
973 | auto emitEndOfSequence = [&](uint64_t Address) { |
974 | MCDwarfLineAddr::Emit(MCOS, Params, INT64_MAX, AddrDelta: Address - LastAddress); |
975 | FileNum = 1; |
976 | LastLine = 1; |
977 | Column = 0; |
978 | Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; |
979 | Isa = 0; |
980 | Discriminator = 0; |
981 | LastAddress = InvalidAddress; |
982 | }; |
983 | |
984 | for (const DwarfLineTable::RowSequence &Sequence : InputSequences) { |
985 | const uint64_t SequenceStart = |
986 | Table->Rows[Sequence.FirstIndex].Address.Address; |
987 | |
988 | // Check if we need to mark the end of the sequence. |
989 | if (PrevEndOfSequence != InvalidAddress && LastAddress != InvalidAddress && |
990 | PrevEndOfSequence != SequenceStart) { |
991 | emitEndOfSequence(PrevEndOfSequence); |
992 | } |
993 | |
994 | for (uint32_t RowIndex = Sequence.FirstIndex; |
995 | RowIndex <= Sequence.LastIndex; ++RowIndex) { |
996 | const DWARFDebugLine::Row &Row = Table->Rows[RowIndex]; |
997 | int64_t LineDelta = static_cast<int64_t>(Row.Line) - LastLine; |
998 | const uint64_t Address = Row.Address.Address; |
999 | |
1000 | if (FileNum != Row.File) { |
1001 | FileNum = Row.File; |
1002 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_file); |
1003 | MCOS->emitULEB128IntValue(Value: FileNum); |
1004 | } |
1005 | if (Column != Row.Column) { |
1006 | Column = Row.Column; |
1007 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_column); |
1008 | MCOS->emitULEB128IntValue(Value: Column); |
1009 | } |
1010 | if (Discriminator != Row.Discriminator && |
1011 | MCOS->getContext().getDwarfVersion() >= 4) { |
1012 | Discriminator = Row.Discriminator; |
1013 | unsigned Size = getULEB128Size(Value: Discriminator); |
1014 | MCOS->emitInt8(Value: dwarf::DW_LNS_extended_op); |
1015 | MCOS->emitULEB128IntValue(Value: Size + 1); |
1016 | MCOS->emitInt8(Value: dwarf::DW_LNE_set_discriminator); |
1017 | MCOS->emitULEB128IntValue(Value: Discriminator); |
1018 | } |
1019 | if (Isa != Row.Isa) { |
1020 | Isa = Row.Isa; |
1021 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_isa); |
1022 | MCOS->emitULEB128IntValue(Value: Isa); |
1023 | } |
1024 | if (Row.IsStmt != Flags) { |
1025 | Flags = Row.IsStmt; |
1026 | MCOS->emitInt8(Value: dwarf::DW_LNS_negate_stmt); |
1027 | } |
1028 | if (Row.BasicBlock) |
1029 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_basic_block); |
1030 | if (Row.PrologueEnd) |
1031 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_prologue_end); |
1032 | if (Row.EpilogueBegin) |
1033 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_epilogue_begin); |
1034 | |
1035 | // The end of the sequence is not normal in the middle of the input |
1036 | // sequence, but could happen, e.g. for assembly code. |
1037 | if (Row.EndSequence) { |
1038 | emitEndOfSequence(Address); |
1039 | } else { |
1040 | if (LastAddress == InvalidAddress) |
1041 | emitDwarfSetLineAddrAbs(OS&: *MCOS, Params, LineDelta, Address, |
1042 | PointerSize: AsmInfo->getCodePointerSize()); |
1043 | else |
1044 | MCDwarfLineAddr::Emit(MCOS, Params, LineDelta, AddrDelta: Address - LastAddress); |
1045 | |
1046 | LastAddress = Address; |
1047 | LastLine = Row.Line; |
1048 | } |
1049 | |
1050 | Discriminator = 0; |
1051 | } |
1052 | PrevEndOfSequence = Sequence.EndAddress; |
1053 | } |
1054 | |
1055 | // Finish with the end of the sequence. |
1056 | if (LastAddress != InvalidAddress) |
1057 | emitEndOfSequence(PrevEndOfSequence); |
1058 | } |
1059 | |
1060 | // This function is similar to the one from MCDwarfLineTable, except it handles |
1061 | // end-of-sequence entries differently by utilizing line entries with |
1062 | // DWARF2_FLAG_END_SEQUENCE flag. |
1063 | static inline void emitDwarfLineTable( |
1064 | MCStreamer *MCOS, MCSection *Section, |
1065 | const MCLineSection::MCDwarfLineEntryCollection &LineEntries) { |
1066 | unsigned FileNum = 1; |
1067 | unsigned LastLine = 1; |
1068 | unsigned Column = 0; |
1069 | unsigned Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; |
1070 | unsigned Isa = 0; |
1071 | unsigned Discriminator = 0; |
1072 | MCSymbol *LastLabel = nullptr; |
1073 | const MCAsmInfo *AsmInfo = MCOS->getContext().getAsmInfo(); |
1074 | |
1075 | // Loop through each MCDwarfLineEntry and encode the dwarf line number table. |
1076 | for (const MCDwarfLineEntry &LineEntry : LineEntries) { |
1077 | if (LineEntry.getFlags() & DWARF2_FLAG_END_SEQUENCE) { |
1078 | MCOS->emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, Label: LineEntry.getLabel(), |
1079 | PointerSize: AsmInfo->getCodePointerSize()); |
1080 | FileNum = 1; |
1081 | LastLine = 1; |
1082 | Column = 0; |
1083 | Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; |
1084 | Isa = 0; |
1085 | Discriminator = 0; |
1086 | LastLabel = nullptr; |
1087 | continue; |
1088 | } |
1089 | |
1090 | int64_t LineDelta = static_cast<int64_t>(LineEntry.getLine()) - LastLine; |
1091 | |
1092 | if (FileNum != LineEntry.getFileNum()) { |
1093 | FileNum = LineEntry.getFileNum(); |
1094 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_file); |
1095 | MCOS->emitULEB128IntValue(Value: FileNum); |
1096 | } |
1097 | if (Column != LineEntry.getColumn()) { |
1098 | Column = LineEntry.getColumn(); |
1099 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_column); |
1100 | MCOS->emitULEB128IntValue(Value: Column); |
1101 | } |
1102 | if (Discriminator != LineEntry.getDiscriminator() && |
1103 | MCOS->getContext().getDwarfVersion() >= 2) { |
1104 | Discriminator = LineEntry.getDiscriminator(); |
1105 | unsigned Size = getULEB128Size(Value: Discriminator); |
1106 | MCOS->emitInt8(Value: dwarf::DW_LNS_extended_op); |
1107 | MCOS->emitULEB128IntValue(Value: Size + 1); |
1108 | MCOS->emitInt8(Value: dwarf::DW_LNE_set_discriminator); |
1109 | MCOS->emitULEB128IntValue(Value: Discriminator); |
1110 | } |
1111 | if (Isa != LineEntry.getIsa()) { |
1112 | Isa = LineEntry.getIsa(); |
1113 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_isa); |
1114 | MCOS->emitULEB128IntValue(Value: Isa); |
1115 | } |
1116 | if ((LineEntry.getFlags() ^ Flags) & DWARF2_FLAG_IS_STMT) { |
1117 | Flags = LineEntry.getFlags(); |
1118 | MCOS->emitInt8(Value: dwarf::DW_LNS_negate_stmt); |
1119 | } |
1120 | if (LineEntry.getFlags() & DWARF2_FLAG_BASIC_BLOCK) |
1121 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_basic_block); |
1122 | if (LineEntry.getFlags() & DWARF2_FLAG_PROLOGUE_END) |
1123 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_prologue_end); |
1124 | if (LineEntry.getFlags() & DWARF2_FLAG_EPILOGUE_BEGIN) |
1125 | MCOS->emitInt8(Value: dwarf::DW_LNS_set_epilogue_begin); |
1126 | |
1127 | MCSymbol *Label = LineEntry.getLabel(); |
1128 | |
1129 | // At this point we want to emit/create the sequence to encode the delta |
1130 | // in line numbers and the increment of the address from the previous |
1131 | // Label and the current Label. |
1132 | MCOS->emitDwarfAdvanceLineAddr(LineDelta, LastLabel, Label, |
1133 | PointerSize: AsmInfo->getCodePointerSize()); |
1134 | Discriminator = 0; |
1135 | LastLine = LineEntry.getLine(); |
1136 | LastLabel = Label; |
1137 | } |
1138 | |
1139 | assert(LastLabel == nullptr && "end of sequence expected"); |
1140 | } |
1141 | |
1142 | void DwarfLineTable::emitCU(MCStreamer *MCOS, MCDwarfLineTableParams Params, |
1143 | std::optional<MCDwarfLineStr> &LineStr, |
1144 | BinaryContext &BC) const { |
1145 | if (!RawData.empty()) { |
1146 | assert(MCLineSections.getMCLineEntries().empty() && |
1147 | InputSequences.empty() && |
1148 | "cannot combine raw data with new line entries"); |
1149 | MCOS->emitLabel(Symbol: getLabel()); |
1150 | MCOS->emitBytes(Data: RawData); |
1151 | return; |
1152 | } |
1153 | |
1154 | MCSymbol *LineEndSym = Header.Emit(MCOS, Params, LineStr).second; |
1155 | |
1156 | // Put out the line tables. |
1157 | for (const auto &LineSec : MCLineSections.getMCLineEntries()) |
1158 | emitDwarfLineTable(MCOS, Section: LineSec.first, LineEntries: LineSec.second); |
1159 | |
1160 | // Emit line tables for the original code. |
1161 | emitBinaryDwarfLineTable(MCOS, Params, Table: InputTable, InputSequences); |
1162 | |
1163 | // This is the end of the section, so set the value of the symbol at the end |
1164 | // of this section (that was used in a previous expression). |
1165 | MCOS->emitLabel(Symbol: LineEndSym); |
1166 | } |
1167 | |
1168 | // Helper function to parse .debug_line_str, and populate one we are using. |
1169 | // For functions that we do not modify we output them as raw data. |
1170 | // Re-constructing .debug_line_str so that offsets are correct for those |
1171 | // debug line tables. |
1172 | // Bonus is that when we output a final binary we can re-use .debug_line_str |
1173 | // section. So we don't have to do the SHF_ALLOC trick we did with |
1174 | // .debug_line. |
1175 | static void parseAndPopulateDebugLineStr(BinarySection &LineStrSection, |
1176 | MCDwarfLineStr &LineStr, |
1177 | BinaryContext &BC) { |
1178 | DataExtractor StrData(LineStrSection.getContents(), |
1179 | BC.DwCtx->isLittleEndian(), 0); |
1180 | uint64_t Offset = 0; |
1181 | while (StrData.isValidOffset(offset: Offset)) { |
1182 | const uint64_t StrOffset = Offset; |
1183 | Error Err = Error::success(); |
1184 | const char *CStr = StrData.getCStr(OffsetPtr: &Offset, Err: &Err); |
1185 | if (Err) { |
1186 | BC.errs() << "BOLT-ERROR: could not extract string from .debug_line_str"; |
1187 | continue; |
1188 | } |
1189 | const size_t NewOffset = LineStr.addString(Path: CStr); |
1190 | assert(StrOffset == NewOffset && |
1191 | "New offset in .debug_line_str doesn't match original offset"); |
1192 | (void)StrOffset; |
1193 | (void)NewOffset; |
1194 | } |
1195 | } |
1196 | |
1197 | void DwarfLineTable::emit(BinaryContext &BC, MCStreamer &Streamer) { |
1198 | MCAssembler &Assembler = |
1199 | static_cast<MCObjectStreamer *>(&Streamer)->getAssembler(); |
1200 | |
1201 | MCDwarfLineTableParams Params = Assembler.getDWARFLinetableParams(); |
1202 | |
1203 | auto &LineTables = BC.getDwarfLineTables(); |
1204 | |
1205 | // Bail out early so we don't switch to the debug_line section needlessly and |
1206 | // in doing so create an unnecessary (if empty) section. |
1207 | if (LineTables.empty()) |
1208 | return; |
1209 | // In a v5 non-split line table, put the strings in a separate section. |
1210 | std::optional<MCDwarfLineStr> LineStr; |
1211 | ErrorOr<BinarySection &> LineStrSection = |
1212 | BC.getUniqueSectionByName(SectionName: ".debug_line_str"); |
1213 | |
1214 | // Some versions of GCC output DWARF5 .debug_info, but DWARF4 or lower |
1215 | // .debug_line, so need to check if section exists. |
1216 | if (LineStrSection) { |
1217 | LineStr.emplace(args&: *BC.Ctx); |
1218 | parseAndPopulateDebugLineStr(LineStrSection&: *LineStrSection, LineStr&: *LineStr, BC); |
1219 | } |
1220 | |
1221 | // Switch to the section where the table will be emitted into. |
1222 | Streamer.switchSection(Section: BC.MOFI->getDwarfLineSection()); |
1223 | |
1224 | const uint16_t DwarfVersion = BC.Ctx->getDwarfVersion(); |
1225 | // Handle the rest of the Compile Units. |
1226 | for (auto &CUIDTablePair : LineTables) { |
1227 | Streamer.getContext().setDwarfVersion( |
1228 | CUIDTablePair.second.getDwarfVersion()); |
1229 | CUIDTablePair.second.emitCU(MCOS: &Streamer, Params, LineStr, BC); |
1230 | } |
1231 | |
1232 | // Resetting DWARF version for rest of the flow. |
1233 | BC.Ctx->setDwarfVersion(DwarfVersion); |
1234 | |
1235 | // Still need to write the section out for the ExecutionEngine, and temp in |
1236 | // memory object we are constructing. |
1237 | if (LineStr) |
1238 | LineStr->emitSection(MCOS: &Streamer); |
1239 | } |
1240 | |
1241 | } // namespace bolt |
1242 | } // namespace llvm |
1243 |
Definitions
- replaceLocValbyForm
- findAttributeInfo
- findAttributeInfo
- NULL_ROW
- printLE64
- writeAddressRanges
- DebugRangesSectionWriter
- initSection
- addRanges
- addRanges
- getSectionOffset
- appendToRangeBuffer
- addRanges
- LocListsRangelistsHeader
- getDWARF5Header
- OffsetEntry
- emitWithBase
- addRanges
- finalizeSection
- initSection
- addCURanges
- writeARangesSection
- DebugAddrWriter
- dump
- getIndexFromAddress
- updateAddressBase
- updateAddrBase
- finalize
- updateAddrBase
- UnmodifiedAddressOffsets
- finalize
- init
- LocSectionOffset
- addList
- getBuffer
- finalize
- writeEmptyListDwarf5
- writeLegacyLocList
- writeDWARF5LocList
- addList
- LoclistBaseOffset
- finalizeDWARF5
- finalize
- encodeLE
- addBinaryPatch
- addBytePatch
- addLEPatch
- addUDataPatch
- addLE64Patch
- addLE32Patch
- patchBinary
- initialize
- updateAddressMap
- finalizeSection
- create
- initialize
- addString
- emitDwarfSetLineAddrAbs
- emitBinaryDwarfLineTable
- emitDwarfLineTable
- emitCU
- parseAndPopulateDebugLineStr
Learn to use CMake with our Intro Training
Find out more