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