| //===- bolt/Core/DebugData.cpp - Debugging information handling -----------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements functions and classes for handling debug info. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "bolt/Core/DebugData.h" |
| #include "bolt/Core/BinaryContext.h" |
| #include "bolt/Core/DIEBuilder.h" |
| #include "bolt/Rewrite/RewriteInstance.h" |
| #include "bolt/Utils/Utils.h" |
| #include "llvm/BinaryFormat/Dwarf.h" |
| #include "llvm/CodeGen/DIE.h" |
| #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" |
| #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" |
| #include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" |
| #include "llvm/MC/MCAssembler.h" |
| #include "llvm/MC/MCContext.h" |
| #include "llvm/MC/MCObjectStreamer.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/EndianStream.h" |
| #include "llvm/Support/LEB128.h" |
| #include "llvm/Support/SHA1.h" |
| #include <algorithm> |
| #include <cassert> |
| #include <cstdint> |
| #include <functional> |
| #include <limits> |
| #include <memory> |
| #include <unordered_map> |
| #include <vector> |
| |
| #define DEBUG_TYPE "bolt-debug-info" |
| |
| namespace opts { |
| extern llvm::cl::opt<unsigned> Verbosity; |
| } // namespace opts |
| |
| namespace llvm { |
| class MCSymbol; |
| |
| namespace bolt { |
| |
| static void replaceLocValbyForm(DIEBuilder &DIEBldr, DIE &Die, DIEValue DIEVal, |
| dwarf::Form Format, uint64_t NewVal) { |
| if (Format == dwarf::DW_FORM_loclistx) |
| DIEBldr.replaceValue(&Die, DIEVal.getAttribute(), Format, |
| DIELocList(NewVal)); |
| else |
| DIEBldr.replaceValue(&Die, DIEVal.getAttribute(), Format, |
| DIEInteger(NewVal)); |
| } |
| |
| std::optional<AttrInfo> |
| findAttributeInfo(const DWARFDie DIE, |
| const DWARFAbbreviationDeclaration *AbbrevDecl, |
| uint32_t Index) { |
| const DWARFUnit &U = *DIE.getDwarfUnit(); |
| uint64_t Offset = |
| AbbrevDecl->getAttributeOffsetFromIndex(Index, DIE.getOffset(), U); |
| std::optional<DWARFFormValue> Value = |
| AbbrevDecl->getAttributeValueFromOffset(Index, Offset, U); |
| if (!Value) |
| return std::nullopt; |
| // AttributeSpec |
| const DWARFAbbreviationDeclaration::AttributeSpec *AttrVal = |
| AbbrevDecl->attributes().begin() + Index; |
| uint32_t ValSize = 0; |
| std::optional<int64_t> ValSizeOpt = AttrVal->getByteSize(U); |
| if (ValSizeOpt) { |
| ValSize = static_cast<uint32_t>(*ValSizeOpt); |
| } else { |
| DWARFDataExtractor DebugInfoData = U.getDebugInfoExtractor(); |
| uint64_t NewOffset = Offset; |
| DWARFFormValue::skipValue(Value->getForm(), DebugInfoData, &NewOffset, |
| U.getFormParams()); |
| // This includes entire size of the entry, which might not be just the |
| // encoding part. For example for DW_AT_loc it will include expression |
| // location. |
| ValSize = NewOffset - Offset; |
| } |
| return AttrInfo{*Value, DIE.getAbbreviationDeclarationPtr(), Offset, ValSize}; |
| } |
| |
| std::optional<AttrInfo> findAttributeInfo(const DWARFDie DIE, |
| dwarf::Attribute Attr) { |
| if (!DIE.isValid()) |
| return std::nullopt; |
| const DWARFAbbreviationDeclaration *AbbrevDecl = |
| DIE.getAbbreviationDeclarationPtr(); |
| if (!AbbrevDecl) |
| return std::nullopt; |
| std::optional<uint32_t> Index = AbbrevDecl->findAttributeIndex(Attr); |
| if (!Index) |
| return std::nullopt; |
| return findAttributeInfo(DIE, AbbrevDecl, *Index); |
| } |
| |
| const DebugLineTableRowRef DebugLineTableRowRef::NULL_ROW{0, 0}; |
| |
| LLVM_ATTRIBUTE_UNUSED |
| static void printLE64(const std::string &S) { |
| for (uint32_t I = 0, Size = S.size(); I < Size; ++I) { |
| errs() << Twine::utohexstr(S[I]); |
| errs() << Twine::utohexstr((int8_t)S[I]); |
| } |
| errs() << "\n"; |
| } |
| |
| // Writes address ranges to Writer as pairs of 64-bit (address, size). |
| // If RelativeRange is true, assumes the address range to be written must be of |
| // the form (begin address, range size), otherwise (begin address, end address). |
| // Terminates the list by writing a pair of two zeroes. |
| // Returns the number of written bytes. |
| static uint64_t |
| writeAddressRanges(raw_svector_ostream &Stream, |
| const DebugAddressRangesVector &AddressRanges, |
| const bool WriteRelativeRanges = false) { |
| for (const DebugAddressRange &Range : AddressRanges) { |
| support::endian::write(Stream, Range.LowPC, llvm::endianness::little); |
| support::endian::write( |
| Stream, WriteRelativeRanges ? Range.HighPC - Range.LowPC : Range.HighPC, |
| llvm::endianness::little); |
| } |
| // Finish with 0 entries. |
| support::endian::write(Stream, 0ULL, llvm::endianness::little); |
| support::endian::write(Stream, 0ULL, llvm::endianness::little); |
| return AddressRanges.size() * 16 + 16; |
| } |
| |
| DebugRangesSectionWriter::DebugRangesSectionWriter() { |
| RangesBuffer = std::make_unique<DebugBufferVector>(); |
| RangesStream = std::make_unique<raw_svector_ostream>(*RangesBuffer); |
| |
| // Add an empty range as the first entry; |
| SectionOffset += |
| writeAddressRanges(*RangesStream.get(), DebugAddressRangesVector{}); |
| Kind = RangesWriterKind::DebugRangesWriter; |
| } |
| |
| uint64_t DebugRangesSectionWriter::addRanges( |
| DebugAddressRangesVector &&Ranges, |
| std::map<DebugAddressRangesVector, uint64_t> &CachedRanges) { |
| if (Ranges.empty()) |
| return getEmptyRangesOffset(); |
| |
| const auto RI = CachedRanges.find(Ranges); |
| if (RI != CachedRanges.end()) |
| return RI->second; |
| |
| const uint64_t EntryOffset = addRanges(Ranges); |
| CachedRanges.emplace(std::move(Ranges), EntryOffset); |
| |
| return EntryOffset; |
| } |
| |
| uint64_t DebugRangesSectionWriter::addRanges(DebugAddressRangesVector &Ranges) { |
| if (Ranges.empty()) |
| return getEmptyRangesOffset(); |
| |
| // Reading the SectionOffset and updating it should be atomic to guarantee |
| // unique and correct offsets in patches. |
| std::lock_guard<std::mutex> Lock(WriterMutex); |
| const uint32_t EntryOffset = SectionOffset; |
| SectionOffset += writeAddressRanges(*RangesStream.get(), Ranges); |
| |
| return EntryOffset; |
| } |
| |
| uint64_t DebugRangesSectionWriter::getSectionOffset() { |
| std::lock_guard<std::mutex> Lock(WriterMutex); |
| return SectionOffset; |
| } |
| |
| DebugAddrWriter *DebugRangeListsSectionWriter::AddrWriter = nullptr; |
| |
| uint64_t DebugRangeListsSectionWriter::addRanges( |
| DebugAddressRangesVector &&Ranges, |
| std::map<DebugAddressRangesVector, uint64_t> &CachedRanges) { |
| return addRanges(Ranges); |
| } |
| |
| struct LocListsRangelistsHeader { |
| UnitLengthType UnitLength; // Size of loclist entries section, not including |
| // size of header. |
| VersionType Version; |
| AddressSizeType AddressSize; |
| SegmentSelectorType SegmentSelector; |
| OffsetEntryCountType OffsetEntryCount; |
| }; |
| |
| static std::unique_ptr<DebugBufferVector> |
| getDWARF5Header(const LocListsRangelistsHeader &Header) { |
| std::unique_ptr<DebugBufferVector> HeaderBuffer = |
| std::make_unique<DebugBufferVector>(); |
| std::unique_ptr<raw_svector_ostream> HeaderStream = |
| std::make_unique<raw_svector_ostream>(*HeaderBuffer); |
| |
| // 7.29 length of the set of entries for this compilation unit, not including |
| // the length field itself |
| const uint32_t HeaderSize = |
| getDWARF5RngListLocListHeaderSize() - sizeof(UnitLengthType); |
| |
| support::endian::write(*HeaderStream, Header.UnitLength + HeaderSize, |
| llvm::endianness::little); |
| support::endian::write(*HeaderStream, Header.Version, |
| llvm::endianness::little); |
| support::endian::write(*HeaderStream, Header.AddressSize, |
| llvm::endianness::little); |
| support::endian::write(*HeaderStream, Header.SegmentSelector, |
| llvm::endianness::little); |
| support::endian::write(*HeaderStream, Header.OffsetEntryCount, |
| llvm::endianness::little); |
| return HeaderBuffer; |
| } |
| |
| struct OffsetEntry { |
| uint32_t Index; |
| uint32_t StartOffset; |
| uint32_t EndOffset; |
| }; |
| template <typename DebugVector, typename ListEntry, typename DebugAddressEntry> |
| static bool emitWithBase(raw_ostream &OS, const DebugVector &Entries, |
| DebugAddrWriter &AddrWriter, DWARFUnit &CU, |
| uint32_t &Index, const ListEntry BaseAddressx, |
| const ListEntry OffsetPair, const ListEntry EndOfList, |
| const std::function<void(uint32_t)> &Func) { |
| if (Entries.size() < 2) |
| return false; |
| uint64_t Base = Entries[Index].LowPC; |
| std::vector<OffsetEntry> Offsets; |
| uint8_t TempBuffer[64]; |
| while (Index < Entries.size()) { |
| const DebugAddressEntry &Entry = Entries[Index]; |
| if (Entry.LowPC == 0) |
| break; |
| assert(Base <= Entry.LowPC && "Entry base is higher than low PC"); |
| uint32_t StartOffset = Entry.LowPC - Base; |
| uint32_t EndOffset = Entry.HighPC - Base; |
| if (encodeULEB128(EndOffset, TempBuffer) > 2) |
| break; |
| Offsets.push_back({Index, StartOffset, EndOffset}); |
| ++Index; |
| } |
| |
| if (Offsets.size() < 2) { |
| Index -= Offsets.size(); |
| return false; |
| } |
| |
| support::endian::write(OS, static_cast<uint8_t>(BaseAddressx), |
| llvm::endianness::little); |
| uint32_t BaseIndex = AddrWriter.getIndexFromAddress(Base, CU); |
| encodeULEB128(BaseIndex, OS); |
| for (auto &OffsetEntry : Offsets) { |
| support::endian::write(OS, static_cast<uint8_t>(OffsetPair), |
| llvm::endianness::little); |
| encodeULEB128(OffsetEntry.StartOffset, OS); |
| encodeULEB128(OffsetEntry.EndOffset, OS); |
| Func(OffsetEntry.Index); |
| } |
| support::endian::write(OS, static_cast<uint8_t>(EndOfList), |
| llvm::endianness::little); |
| return true; |
| } |
| |
| uint64_t |
| DebugRangeListsSectionWriter::addRanges(DebugAddressRangesVector &Ranges) { |
| std::lock_guard<std::mutex> Lock(WriterMutex); |
| |
| RangeEntries.push_back(CurrentOffset); |
| bool WrittenStartxLength = false; |
| std::sort( |
| Ranges.begin(), Ranges.end(), |
| [](const DebugAddressRange &R1, const DebugAddressRange &R2) -> bool { |
| return R1.LowPC < R2.LowPC; |
| }); |
| for (unsigned I = 0; I < Ranges.size();) { |
| WrittenStartxLength = false; |
| if (emitWithBase<DebugAddressRangesVector, dwarf::RnglistEntries, |
| DebugAddressRange>( |
| *CUBodyStream, Ranges, *AddrWriter, *CU, I, |
| dwarf::DW_RLE_base_addressx, dwarf::DW_RLE_offset_pair, |
| dwarf::DW_RLE_end_of_list, [](uint32_t Index) -> void {})) |
| continue; |
| |
| const DebugAddressRange &Range = Ranges[I]; |
| support::endian::write(*CUBodyStream, |
| static_cast<uint8_t>(dwarf::DW_RLE_startx_length), |
| llvm::endianness::little); |
| uint32_t Index = AddrWriter->getIndexFromAddress(Range.LowPC, *CU); |
| encodeULEB128(Index, *CUBodyStream); |
| encodeULEB128(Range.HighPC - Range.LowPC, *CUBodyStream); |
| ++I; |
| WrittenStartxLength = true; |
| } |
| if (WrittenStartxLength) |
| support::endian::write(*CUBodyStream, |
| static_cast<uint8_t>(dwarf::DW_RLE_end_of_list), |
| llvm::endianness::little); |
| CurrentOffset = CUBodyBuffer->size(); |
| return RangeEntries.size() - 1; |
| } |
| |
| void DebugRangeListsSectionWriter::finalizeSection() { |
| std::unique_ptr<DebugBufferVector> CUArrayBuffer = |
| std::make_unique<DebugBufferVector>(); |
| std::unique_ptr<raw_svector_ostream> CUArrayStream = |
| std::make_unique<raw_svector_ostream>(*CUArrayBuffer); |
| constexpr uint32_t SizeOfArrayEntry = 4; |
| const uint32_t SizeOfArraySection = RangeEntries.size() * SizeOfArrayEntry; |
| for (uint32_t Offset : RangeEntries) |
| support::endian::write(*CUArrayStream, Offset + SizeOfArraySection, |
| llvm::endianness::little); |
| |
| std::unique_ptr<DebugBufferVector> Header = getDWARF5Header( |
| {static_cast<uint32_t>(SizeOfArraySection + CUBodyBuffer.get()->size()), |
| 5, 8, 0, static_cast<uint32_t>(RangeEntries.size())}); |
| *RangesStream << *Header; |
| *RangesStream << *CUArrayBuffer; |
| *RangesStream << *CUBodyBuffer; |
| SectionOffset = RangesBuffer->size(); |
| } |
| |
| void DebugRangeListsSectionWriter::initSection(DWARFUnit &Unit) { |
| CUBodyBuffer = std::make_unique<DebugBufferVector>(); |
| CUBodyStream = std::make_unique<raw_svector_ostream>(*CUBodyBuffer); |
| RangeEntries.clear(); |
| CurrentOffset = 0; |
| CU = &Unit; |
| } |
| |
| void DebugARangesSectionWriter::addCURanges(uint64_t CUOffset, |
| DebugAddressRangesVector &&Ranges) { |
| std::lock_guard<std::mutex> Lock(CUAddressRangesMutex); |
| CUAddressRanges.emplace(CUOffset, std::move(Ranges)); |
| } |
| |
| void DebugARangesSectionWriter::writeARangesSection( |
| raw_svector_ostream &RangesStream, const CUOffsetMap &CUMap) const { |
| // For reference on the format of the .debug_aranges section, see the DWARF4 |
| // specification, section 6.1.4 Lookup by Address |
| // http://www.dwarfstd.org/doc/DWARF4.pdf |
| for (const auto &CUOffsetAddressRangesPair : CUAddressRanges) { |
| const uint64_t Offset = CUOffsetAddressRangesPair.first; |
| const DebugAddressRangesVector &AddressRanges = |
| CUOffsetAddressRangesPair.second; |
| |
| // Emit header. |
| |
| // Size of this set: 8 (size of the header) + 4 (padding after header) |
| // + 2*sizeof(uint64_t) bytes for each of the ranges, plus an extra |
| // pair of uint64_t's for the terminating, zero-length range. |
| // Does not include size field itself. |
| uint32_t Size = 8 + 4 + 2 * sizeof(uint64_t) * (AddressRanges.size() + 1); |
| |
| // Header field #1: set size. |
| support::endian::write(RangesStream, Size, llvm::endianness::little); |
| |
| // Header field #2: version number, 2 as per the specification. |
| support::endian::write(RangesStream, static_cast<uint16_t>(2), |
| llvm::endianness::little); |
| |
| assert(CUMap.count(Offset) && "Original CU offset is not found in CU Map"); |
| // Header field #3: debug info offset of the correspondent compile unit. |
| support::endian::write( |
| RangesStream, static_cast<uint32_t>(CUMap.find(Offset)->second.Offset), |
| llvm::endianness::little); |
| |
| // Header field #4: address size. |
| // 8 since we only write ELF64 binaries for now. |
| RangesStream << char(8); |
| |
| // Header field #5: segment size of target architecture. |
| RangesStream << char(0); |
| |
| // Padding before address table - 4 bytes in the 64-bit-pointer case. |
| support::endian::write(RangesStream, static_cast<uint32_t>(0), |
| llvm::endianness::little); |
| |
| writeAddressRanges(RangesStream, AddressRanges, true); |
| } |
| } |
| |
| DebugAddrWriter::DebugAddrWriter(BinaryContext *BC) : BC(BC) { |
| Buffer = std::make_unique<AddressSectionBuffer>(); |
| AddressStream = std::make_unique<raw_svector_ostream>(*Buffer); |
| } |
| |
| void DebugAddrWriter::AddressForDWOCU::dump() { |
| std::vector<IndexAddressPair> SortedMap(indexToAddressBegin(), |
| indexToAdddessEnd()); |
| // Sorting address in increasing order of indices. |
| llvm::sort(SortedMap, llvm::less_first()); |
| for (auto &Pair : SortedMap) |
| dbgs() << Twine::utohexstr(Pair.second) << "\t" << Pair.first << "\n"; |
| } |
| uint32_t DebugAddrWriter::getIndexFromAddress(uint64_t Address, DWARFUnit &CU) { |
| std::lock_guard<std::mutex> Lock(WriterMutex); |
| const uint64_t CUID = getCUID(CU); |
| if (!AddressMaps.count(CUID)) |
| AddressMaps[CUID] = AddressForDWOCU(); |
| |
| AddressForDWOCU &Map = AddressMaps[CUID]; |
| auto Entry = Map.find(Address); |
| if (Entry == Map.end()) { |
| auto Index = Map.getNextIndex(); |
| Entry = Map.insert(Address, Index).first; |
| } |
| return Entry->second; |
| } |
| |
| static void updateAddressBase(DIEBuilder &DIEBlder, DebugAddrWriter &AddrWriter, |
| DWARFUnit &CU, const uint64_t Offset) { |
| DIE *Die = DIEBlder.getUnitDIEbyUnit(CU); |
| DIEValue GnuAddrBaseAttrInfo = Die->findAttribute(dwarf::DW_AT_GNU_addr_base); |
| DIEValue AddrBaseAttrInfo = Die->findAttribute(dwarf::DW_AT_addr_base); |
| dwarf::Form BaseAttrForm; |
| dwarf::Attribute BaseAttr; |
| // For cases where Skeleton CU does not have DW_AT_GNU_addr_base |
| if (!GnuAddrBaseAttrInfo && CU.getVersion() < 5) |
| return; |
| |
| if (GnuAddrBaseAttrInfo) { |
| BaseAttrForm = GnuAddrBaseAttrInfo.getForm(); |
| BaseAttr = GnuAddrBaseAttrInfo.getAttribute(); |
| } |
| |
| if (AddrBaseAttrInfo) { |
| BaseAttrForm = AddrBaseAttrInfo.getForm(); |
| BaseAttr = AddrBaseAttrInfo.getAttribute(); |
| } |
| |
| if (GnuAddrBaseAttrInfo || AddrBaseAttrInfo) { |
| DIEBlder.replaceValue(Die, BaseAttr, BaseAttrForm, DIEInteger(Offset)); |
| } else if (CU.getVersion() >= 5) { |
| // A case where we were not using .debug_addr section, but after update |
| // now using it. |
| DIEBlder.addValue(Die, dwarf::DW_AT_addr_base, dwarf::DW_FORM_sec_offset, |
| DIEInteger(Offset)); |
| } |
| } |
| |
| void DebugAddrWriter::update(DIEBuilder &DIEBlder, DWARFUnit &CU) { |
| // Handling the case where debug information is a mix of Debug fission and |
| // monolithic. |
| if (!CU.getDWOId()) |
| return; |
| const uint64_t CUID = getCUID(CU); |
| auto AM = AddressMaps.find(CUID); |
| // Adding to map even if it did not contribute to .debug_addr. |
| // The Skeleton CU might still have DW_AT_GNU_addr_base. |
| uint64_t Offset = Buffer->size(); |
| // If does not exist this CUs DWO section didn't contribute to .debug_addr. |
| if (AM == AddressMaps.end()) |
| return; |
| std::vector<IndexAddressPair> SortedMap(AM->second.indexToAddressBegin(), |
| AM->second.indexToAdddessEnd()); |
| // Sorting address in increasing order of indices. |
| llvm::sort(SortedMap, llvm::less_first()); |
| |
| uint8_t AddrSize = CU.getAddressByteSize(); |
| uint32_t Counter = 0; |
| auto WriteAddress = [&](uint64_t Address) -> void { |
| ++Counter; |
| switch (AddrSize) { |
| default: |
| assert(false && "Address Size is invalid."); |
| break; |
| case 4: |
| support::endian::write(*AddressStream, static_cast<uint32_t>(Address), |
| llvm::endianness::little); |
| break; |
| case 8: |
| support::endian::write(*AddressStream, Address, llvm::endianness::little); |
| break; |
| } |
| }; |
| |
| for (const IndexAddressPair &Val : SortedMap) { |
| while (Val.first > Counter) |
| WriteAddress(0); |
| WriteAddress(Val.second); |
| } |
| updateAddressBase(DIEBlder, *this, CU, Offset); |
| } |
| |
| void DebugAddrWriterDwarf5::update(DIEBuilder &DIEBlder, DWARFUnit &CU) { |
| // Need to layout all sections within .debug_addr |
| // Within each section sort Address by index. |
| const endianness Endian = BC->DwCtx->isLittleEndian() |
| ? llvm::endianness::little |
| : llvm::endianness::big; |
| const DWARFSection &AddrSec = BC->DwCtx->getDWARFObj().getAddrSection(); |
| DWARFDataExtractor AddrData(BC->DwCtx->getDWARFObj(), AddrSec, |
| Endian == llvm::endianness::little, 0); |
| DWARFDebugAddrTable AddrTable; |
| DIDumpOptions DumpOpts; |
| constexpr uint32_t HeaderSize = 8; |
| const uint64_t CUID = getCUID(CU); |
| const uint8_t AddrSize = CU.getAddressByteSize(); |
| auto AMIter = AddressMaps.find(CUID); |
| // A case where CU has entry in .debug_addr, but we don't modify addresses |
| // for it. |
| if (AMIter == AddressMaps.end()) { |
| AMIter = AddressMaps.insert({CUID, AddressForDWOCU()}).first; |
| std::optional<uint64_t> BaseOffset = CU.getAddrOffsetSectionBase(); |
| if (!BaseOffset) |
| return; |
| // Address base offset is to the first entry. |
| // The size of header is 8 bytes. |
| uint64_t Offset = *BaseOffset - HeaderSize; |
| auto Iter = UnmodifiedAddressOffsets.find(Offset); |
| if (Iter != UnmodifiedAddressOffsets.end()) { |
| updateAddressBase(DIEBlder, *this, CU, Iter->getSecond()); |
| return; |
| } |
| UnmodifiedAddressOffsets[Offset] = Buffer->size() + HeaderSize; |
| if (Error Err = AddrTable.extract(AddrData, &Offset, 5, AddrSize, |
| DumpOpts.WarningHandler)) { |
| DumpOpts.RecoverableErrorHandler(std::move(Err)); |
| return; |
| } |
| |
| uint32_t Index = 0; |
| for (uint64_t Addr : AddrTable.getAddressEntries()) |
| AMIter->second.insert(Addr, Index++); |
| } |
| |
| updateAddressBase(DIEBlder, *this, CU, Buffer->size() + HeaderSize); |
| |
| std::vector<IndexAddressPair> SortedMap(AMIter->second.indexToAddressBegin(), |
| AMIter->second.indexToAdddessEnd()); |
| // Sorting address in increasing order of indices. |
| llvm::sort(SortedMap, llvm::less_first()); |
| // Writing out Header |
| const uint32_t Length = SortedMap.size() * AddrSize + 4; |
| support::endian::write(*AddressStream, Length, Endian); |
| support::endian::write(*AddressStream, static_cast<uint16_t>(5), Endian); |
| support::endian::write(*AddressStream, static_cast<uint8_t>(AddrSize), |
| Endian); |
| support::endian::write(*AddressStream, static_cast<uint8_t>(0), Endian); |
| |
| uint32_t Counter = 0; |
| auto writeAddress = [&](uint64_t Address) -> void { |
| ++Counter; |
| switch (AddrSize) { |
| default: |
| llvm_unreachable("Address Size is invalid."); |
| break; |
| case 4: |
| support::endian::write(*AddressStream, static_cast<uint32_t>(Address), |
| Endian); |
| break; |
| case 8: |
| support::endian::write(*AddressStream, Address, Endian); |
| break; |
| } |
| }; |
| |
| for (const IndexAddressPair &Val : SortedMap) { |
| while (Val.first > Counter) |
| writeAddress(0); |
| writeAddress(Val.second); |
| } |
| } |
| |
| void DebugLocWriter::init() { |
| LocBuffer = std::make_unique<DebugBufferVector>(); |
| LocStream = std::make_unique<raw_svector_ostream>(*LocBuffer); |
| // Writing out empty location list to which all references to empty location |
| // lists will point. |
| if (!LocSectionOffset && DwarfVersion < 5) { |
| const char Zeroes[16] = {0}; |
| *LocStream << StringRef(Zeroes, 16); |
| LocSectionOffset += 16; |
| } |
| } |
| |
| uint32_t DebugLocWriter::LocSectionOffset = 0; |
| void DebugLocWriter::addList(DIEBuilder &DIEBldr, DIE &Die, DIEValue &AttrInfo, |
| DebugLocationsVector &LocList) { |
| if (LocList.empty()) { |
| replaceLocValbyForm(DIEBldr, Die, AttrInfo, AttrInfo.getForm(), |
| DebugLocWriter::EmptyListOffset); |
| return; |
| } |
| // Since there is a separate DebugLocWriter for each thread, |
| // we don't need a lock to read the SectionOffset and update it. |
| const uint32_t EntryOffset = LocSectionOffset; |
| |
| for (const DebugLocationEntry &Entry : LocList) { |
| support::endian::write(*LocStream, static_cast<uint64_t>(Entry.LowPC), |
| llvm::endianness::little); |
| support::endian::write(*LocStream, static_cast<uint64_t>(Entry.HighPC), |
| llvm::endianness::little); |
| support::endian::write(*LocStream, static_cast<uint16_t>(Entry.Expr.size()), |
| llvm::endianness::little); |
| *LocStream << StringRef(reinterpret_cast<const char *>(Entry.Expr.data()), |
| Entry.Expr.size()); |
| LocSectionOffset += 2 * 8 + 2 + Entry.Expr.size(); |
| } |
| LocStream->write_zeros(16); |
| LocSectionOffset += 16; |
| LocListDebugInfoPatches.push_back({0xdeadbeee, EntryOffset}); // never seen |
| // use |
| replaceLocValbyForm(DIEBldr, Die, AttrInfo, AttrInfo.getForm(), EntryOffset); |
| } |
| |
| std::unique_ptr<DebugBufferVector> DebugLocWriter::getBuffer() { |
| return std::move(LocBuffer); |
| } |
| |
| // DWARF 4: 2.6.2 |
| void DebugLocWriter::finalize(DIEBuilder &DIEBldr, DIE &Die) {} |
| |
| static void writeEmptyListDwarf5(raw_svector_ostream &Stream) { |
| support::endian::write(Stream, static_cast<uint32_t>(4), |
| llvm::endianness::little); |
| support::endian::write(Stream, static_cast<uint8_t>(dwarf::DW_LLE_start_end), |
| llvm::endianness::little); |
| |
| const char Zeroes[16] = {0}; |
| Stream << StringRef(Zeroes, 16); |
| encodeULEB128(0, Stream); |
| support::endian::write(Stream, |
| static_cast<uint8_t>(dwarf::DW_LLE_end_of_list), |
| llvm::endianness::little); |
| } |
| |
| static void writeLegacyLocList(DIEValue &AttrInfo, |
| DebugLocationsVector &LocList, |
| DIEBuilder &DIEBldr, DIE &Die, |
| DebugAddrWriter &AddrWriter, |
| DebugBufferVector &LocBuffer, DWARFUnit &CU, |
| raw_svector_ostream &LocStream) { |
| if (LocList.empty()) { |
| replaceLocValbyForm(DIEBldr, Die, AttrInfo, AttrInfo.getForm(), |
| DebugLocWriter::EmptyListOffset); |
| return; |
| } |
| |
| const uint32_t EntryOffset = LocBuffer.size(); |
| for (const DebugLocationEntry &Entry : LocList) { |
| support::endian::write(LocStream, |
| static_cast<uint8_t>(dwarf::DW_LLE_startx_length), |
| llvm::endianness::little); |
| const uint32_t Index = AddrWriter.getIndexFromAddress(Entry.LowPC, CU); |
| encodeULEB128(Index, LocStream); |
| |
| support::endian::write(LocStream, |
| static_cast<uint32_t>(Entry.HighPC - Entry.LowPC), |
| llvm::endianness::little); |
| support::endian::write(LocStream, static_cast<uint16_t>(Entry.Expr.size()), |
| llvm::endianness::little); |
| LocStream << StringRef(reinterpret_cast<const char *>(Entry.Expr.data()), |
| Entry.Expr.size()); |
| } |
| support::endian::write(LocStream, |
| static_cast<uint8_t>(dwarf::DW_LLE_end_of_list), |
| llvm::endianness::little); |
| replaceLocValbyForm(DIEBldr, Die, AttrInfo, AttrInfo.getForm(), EntryOffset); |
| } |
| |
| static void writeDWARF5LocList(uint32_t &NumberOfEntries, DIEValue &AttrInfo, |
| DebugLocationsVector &LocList, DIE &Die, |
| DIEBuilder &DIEBldr, DebugAddrWriter &AddrWriter, |
| DebugBufferVector &LocBodyBuffer, |
| std::vector<uint32_t> &RelativeLocListOffsets, |
| DWARFUnit &CU, |
| raw_svector_ostream &LocBodyStream) { |
| |
| replaceLocValbyForm(DIEBldr, Die, AttrInfo, dwarf::DW_FORM_loclistx, |
| NumberOfEntries); |
| |
| RelativeLocListOffsets.push_back(LocBodyBuffer.size()); |
| ++NumberOfEntries; |
| if (LocList.empty()) { |
| writeEmptyListDwarf5(LocBodyStream); |
| return; |
| } |
| |
| std::vector<uint64_t> OffsetsArray; |
| bool WrittenStartxLength = false; |
| auto writeExpression = [&](uint32_t Index) -> void { |
| const DebugLocationEntry &Entry = LocList[Index]; |
| encodeULEB128(Entry.Expr.size(), LocBodyStream); |
| LocBodyStream << StringRef( |
| reinterpret_cast<const char *>(Entry.Expr.data()), Entry.Expr.size()); |
| }; |
| for (unsigned I = 0; I < LocList.size();) { |
| WrittenStartxLength = false; |
| if (emitWithBase<DebugLocationsVector, dwarf::LoclistEntries, |
| DebugLocationEntry>( |
| LocBodyStream, LocList, AddrWriter, CU, I, |
| dwarf::DW_LLE_base_addressx, dwarf::DW_LLE_offset_pair, |
| dwarf::DW_LLE_end_of_list, writeExpression)) |
| continue; |
| |
| const DebugLocationEntry &Entry = LocList[I]; |
| support::endian::write(LocBodyStream, |
| static_cast<uint8_t>(dwarf::DW_LLE_startx_length), |
| llvm::endianness::little); |
| const uint32_t Index = AddrWriter.getIndexFromAddress(Entry.LowPC, CU); |
| encodeULEB128(Index, LocBodyStream); |
| encodeULEB128(Entry.HighPC - Entry.LowPC, LocBodyStream); |
| writeExpression(I); |
| ++I; |
| WrittenStartxLength = true; |
| } |
| |
| if (WrittenStartxLength) |
| support::endian::write(LocBodyStream, |
| static_cast<uint8_t>(dwarf::DW_LLE_end_of_list), |
| llvm::endianness::little); |
| } |
| |
| void DebugLoclistWriter::addList(DIEBuilder &DIEBldr, DIE &Die, |
| DIEValue &AttrInfo, |
| DebugLocationsVector &LocList) { |
| if (DwarfVersion < 5) |
| writeLegacyLocList(AttrInfo, LocList, DIEBldr, Die, *AddrWriter, *LocBuffer, |
| CU, *LocStream); |
| else |
| writeDWARF5LocList(NumberOfEntries, AttrInfo, LocList, Die, DIEBldr, |
| *AddrWriter, *LocBodyBuffer, RelativeLocListOffsets, CU, |
| *LocBodyStream); |
| } |
| |
| uint32_t DebugLoclistWriter::LoclistBaseOffset = 0; |
| void DebugLoclistWriter::finalizeDWARF5(DIEBuilder &DIEBldr, DIE &Die) { |
| if (LocBodyBuffer->empty()) { |
| DIEValue LocListBaseAttrInfo = |
| Die.findAttribute(dwarf::DW_AT_loclists_base); |
| // Pointing to first one, because it doesn't matter. There are no uses of it |
| // in this CU. |
| if (!isSplitDwarf() && LocListBaseAttrInfo.getType()) |
| DIEBldr.replaceValue(&Die, dwarf::DW_AT_loclists_base, |
| LocListBaseAttrInfo.getForm(), |
| DIEInteger(getDWARF5RngListLocListHeaderSize())); |
| return; |
| } |
| |
| std::unique_ptr<DebugBufferVector> LocArrayBuffer = |
| std::make_unique<DebugBufferVector>(); |
| std::unique_ptr<raw_svector_ostream> LocArrayStream = |
| std::make_unique<raw_svector_ostream>(*LocArrayBuffer); |
| |
| const uint32_t SizeOfArraySection = NumberOfEntries * sizeof(uint32_t); |
| // Write out IndexArray |
| for (uint32_t RelativeOffset : RelativeLocListOffsets) |
| support::endian::write( |
| *LocArrayStream, |
| static_cast<uint32_t>(SizeOfArraySection + RelativeOffset), |
| llvm::endianness::little); |
| |
| std::unique_ptr<DebugBufferVector> Header = getDWARF5Header( |
| {static_cast<uint32_t>(SizeOfArraySection + LocBodyBuffer.get()->size()), |
| 5, 8, 0, NumberOfEntries}); |
| *LocStream << *Header; |
| *LocStream << *LocArrayBuffer; |
| *LocStream << *LocBodyBuffer; |
| |
| if (!isSplitDwarf()) { |
| DIEValue LocListBaseAttrInfo = |
| Die.findAttribute(dwarf::DW_AT_loclists_base); |
| if (LocListBaseAttrInfo.getType()) { |
| DIEBldr.replaceValue( |
| &Die, dwarf::DW_AT_loclists_base, LocListBaseAttrInfo.getForm(), |
| DIEInteger(LoclistBaseOffset + getDWARF5RngListLocListHeaderSize())); |
| } else { |
| DIEBldr.addValue(&Die, dwarf::DW_AT_loclists_base, |
| dwarf::DW_FORM_sec_offset, |
| DIEInteger(LoclistBaseOffset + Header->size())); |
| } |
| LoclistBaseOffset += LocBuffer->size(); |
| } |
| clearList(RelativeLocListOffsets); |
| clearList(*LocArrayBuffer); |
| clearList(*LocBodyBuffer); |
| } |
| |
| void DebugLoclistWriter::finalize(DIEBuilder &DIEBldr, DIE &Die) { |
| if (DwarfVersion >= 5) |
| finalizeDWARF5(DIEBldr, Die); |
| } |
| |
| DebugAddrWriter *DebugLoclistWriter::AddrWriter = nullptr; |
| |
| static std::string encodeLE(size_t ByteSize, uint64_t NewValue) { |
| std::string LE64(ByteSize, 0); |
| for (size_t I = 0; I < ByteSize; ++I) { |
| LE64[I] = NewValue & 0xff; |
| NewValue >>= 8; |
| } |
| return LE64; |
| } |
| |
| void SimpleBinaryPatcher::addBinaryPatch(uint64_t Offset, |
| std::string &&NewValue, |
| uint32_t OldValueSize) { |
| Patches.emplace_back(Offset, std::move(NewValue)); |
| } |
| |
| void SimpleBinaryPatcher::addBytePatch(uint64_t Offset, uint8_t Value) { |
| auto Str = std::string(1, Value); |
| Patches.emplace_back(Offset, std::move(Str)); |
| } |
| |
| void SimpleBinaryPatcher::addLEPatch(uint64_t Offset, uint64_t NewValue, |
| size_t ByteSize) { |
| Patches.emplace_back(Offset, encodeLE(ByteSize, NewValue)); |
| } |
| |
| void SimpleBinaryPatcher::addUDataPatch(uint64_t Offset, uint64_t Value, |
| uint32_t OldValueSize) { |
| std::string Buff; |
| raw_string_ostream OS(Buff); |
| encodeULEB128(Value, OS, OldValueSize); |
| |
| Patches.emplace_back(Offset, std::move(Buff)); |
| } |
| |
| void SimpleBinaryPatcher::addLE64Patch(uint64_t Offset, uint64_t NewValue) { |
| addLEPatch(Offset, NewValue, 8); |
| } |
| |
| void SimpleBinaryPatcher::addLE32Patch(uint64_t Offset, uint32_t NewValue, |
| uint32_t OldValueSize) { |
| addLEPatch(Offset, NewValue, 4); |
| } |
| |
| std::string SimpleBinaryPatcher::patchBinary(StringRef BinaryContents) { |
| std::string BinaryContentsStr = std::string(BinaryContents); |
| for (const auto &Patch : Patches) { |
| uint32_t Offset = Patch.first; |
| const std::string &ByteSequence = Patch.second; |
| assert(Offset + ByteSequence.size() <= BinaryContents.size() && |
| "Applied patch runs over binary size."); |
| for (uint64_t I = 0, Size = ByteSequence.size(); I < Size; ++I) { |
| BinaryContentsStr[Offset + I] = ByteSequence[I]; |
| } |
| } |
| return BinaryContentsStr; |
| } |
| |
| void DebugStrOffsetsWriter::initialize(DWARFUnit &Unit) { |
| if (Unit.getVersion() < 5) |
| return; |
| const DWARFSection &StrOffsetsSection = Unit.getStringOffsetSection(); |
| const std::optional<StrOffsetsContributionDescriptor> &Contr = |
| Unit.getStringOffsetsTableContribution(); |
| if (!Contr) |
| return; |
| const uint8_t DwarfOffsetByteSize = Contr->getDwarfOffsetByteSize(); |
| assert(DwarfOffsetByteSize == 4 && |
| "Dwarf String Offsets Byte Size is not supported."); |
| StrOffsets.reserve(Contr->Size); |
| for (uint64_t Offset = 0; Offset < Contr->Size; Offset += DwarfOffsetByteSize) |
| StrOffsets.push_back(support::endian::read32le( |
| StrOffsetsSection.Data.data() + Contr->Base + Offset)); |
| } |
| |
| void DebugStrOffsetsWriter::updateAddressMap(uint32_t Index, uint32_t Address) { |
| IndexToAddressMap[Index] = Address; |
| StrOffsetSectionWasModified = true; |
| } |
| |
| void DebugStrOffsetsWriter::finalizeSection(DWARFUnit &Unit, |
| DIEBuilder &DIEBldr) { |
| std::optional<AttrInfo> AttrVal = |
| findAttributeInfo(Unit.getUnitDIE(), dwarf::DW_AT_str_offsets_base); |
| if (!AttrVal) |
| return; |
| std::optional<uint64_t> Val = AttrVal->V.getAsSectionOffset(); |
| assert(Val && "DW_AT_str_offsets_base Value not present."); |
| DIE &Die = *DIEBldr.getUnitDIEbyUnit(Unit); |
| DIEValue StrListBaseAttrInfo = |
| Die.findAttribute(dwarf::DW_AT_str_offsets_base); |
| auto RetVal = ProcessedBaseOffsets.find(*Val); |
| // Handling re-use of str-offsets section. |
| if (RetVal == ProcessedBaseOffsets.end() || StrOffsetSectionWasModified) { |
| initialize(Unit); |
| // Update String Offsets that were modified. |
| for (const auto &Entry : IndexToAddressMap) |
| StrOffsets[Entry.first] = Entry.second; |
| // Writing out the header for each section. |
| support::endian::write(*StrOffsetsStream, |
| static_cast<uint32_t>(StrOffsets.size() * 4 + 4), |
| llvm::endianness::little); |
| support::endian::write(*StrOffsetsStream, static_cast<uint16_t>(5), |
| llvm::endianness::little); |
| support::endian::write(*StrOffsetsStream, static_cast<uint16_t>(0), |
| llvm::endianness::little); |
| |
| uint64_t BaseOffset = StrOffsetsBuffer->size(); |
| ProcessedBaseOffsets[*Val] = BaseOffset; |
| if (StrListBaseAttrInfo.getType()) |
| DIEBldr.replaceValue(&Die, dwarf::DW_AT_str_offsets_base, |
| StrListBaseAttrInfo.getForm(), |
| DIEInteger(BaseOffset)); |
| for (const uint32_t Offset : StrOffsets) |
| support::endian::write(*StrOffsetsStream, Offset, |
| llvm::endianness::little); |
| } else { |
| DIEBldr.replaceValue(&Die, dwarf::DW_AT_str_offsets_base, |
| StrListBaseAttrInfo.getForm(), |
| DIEInteger(RetVal->second)); |
| } |
| |
| StrOffsetSectionWasModified = false; |
| IndexToAddressMap.clear(); |
| StrOffsets.clear(); |
| } |
| |
| void DebugStrWriter::create() { |
| StrBuffer = std::make_unique<DebugStrBufferVector>(); |
| StrStream = std::make_unique<raw_svector_ostream>(*StrBuffer); |
| } |
| |
| void DebugStrWriter::initialize() { |
| auto StrSection = BC.DwCtx->getDWARFObj().getStrSection(); |
| (*StrStream) << StrSection; |
| } |
| |
| uint32_t DebugStrWriter::addString(StringRef Str) { |
| std::lock_guard<std::mutex> Lock(WriterMutex); |
| if (StrBuffer->empty()) |
| initialize(); |
| auto Offset = StrBuffer->size(); |
| (*StrStream) << Str; |
| StrStream->write_zeros(1); |
| return Offset; |
| } |
| |
| static void emitDwarfSetLineAddrAbs(MCStreamer &OS, |
| MCDwarfLineTableParams Params, |
| int64_t LineDelta, uint64_t Address, |
| int PointerSize) { |
| // emit the sequence to set the address |
| OS.emitIntValue(dwarf::DW_LNS_extended_op, 1); |
| OS.emitULEB128IntValue(PointerSize + 1); |
| OS.emitIntValue(dwarf::DW_LNE_set_address, 1); |
| OS.emitIntValue(Address, PointerSize); |
| |
| // emit the sequence for the LineDelta (from 1) and a zero address delta. |
| MCDwarfLineAddr::Emit(&OS, Params, LineDelta, 0); |
| } |
| |
| static inline void emitBinaryDwarfLineTable( |
| MCStreamer *MCOS, MCDwarfLineTableParams Params, |
| const DWARFDebugLine::LineTable *Table, |
| const std::vector<DwarfLineTable::RowSequence> &InputSequences) { |
| if (InputSequences.empty()) |
| return; |
| |
| constexpr uint64_t InvalidAddress = UINT64_MAX; |
| unsigned FileNum = 1; |
| unsigned LastLine = 1; |
| unsigned Column = 0; |
| unsigned Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; |
| unsigned Isa = 0; |
| unsigned Discriminator = 0; |
| uint64_t LastAddress = InvalidAddress; |
| uint64_t PrevEndOfSequence = InvalidAddress; |
| const MCAsmInfo *AsmInfo = MCOS->getContext().getAsmInfo(); |
| |
| auto emitEndOfSequence = [&](uint64_t Address) { |
| MCDwarfLineAddr::Emit(MCOS, Params, INT64_MAX, Address - LastAddress); |
| FileNum = 1; |
| LastLine = 1; |
| Column = 0; |
| Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; |
| Isa = 0; |
| Discriminator = 0; |
| LastAddress = InvalidAddress; |
| }; |
| |
| for (const DwarfLineTable::RowSequence &Sequence : InputSequences) { |
| const uint64_t SequenceStart = |
| Table->Rows[Sequence.FirstIndex].Address.Address; |
| |
| // Check if we need to mark the end of the sequence. |
| if (PrevEndOfSequence != InvalidAddress && LastAddress != InvalidAddress && |
| PrevEndOfSequence != SequenceStart) { |
| emitEndOfSequence(PrevEndOfSequence); |
| } |
| |
| for (uint32_t RowIndex = Sequence.FirstIndex; |
| RowIndex <= Sequence.LastIndex; ++RowIndex) { |
| const DWARFDebugLine::Row &Row = Table->Rows[RowIndex]; |
| int64_t LineDelta = static_cast<int64_t>(Row.Line) - LastLine; |
| const uint64_t Address = Row.Address.Address; |
| |
| if (FileNum != Row.File) { |
| FileNum = Row.File; |
| MCOS->emitInt8(dwarf::DW_LNS_set_file); |
| MCOS->emitULEB128IntValue(FileNum); |
| } |
| if (Column != Row.Column) { |
| Column = Row.Column; |
| MCOS->emitInt8(dwarf::DW_LNS_set_column); |
| MCOS->emitULEB128IntValue(Column); |
| } |
| if (Discriminator != Row.Discriminator && |
| MCOS->getContext().getDwarfVersion() >= 4) { |
| Discriminator = Row.Discriminator; |
| unsigned Size = getULEB128Size(Discriminator); |
| MCOS->emitInt8(dwarf::DW_LNS_extended_op); |
| MCOS->emitULEB128IntValue(Size + 1); |
| MCOS->emitInt8(dwarf::DW_LNE_set_discriminator); |
| MCOS->emitULEB128IntValue(Discriminator); |
| } |
| if (Isa != Row.Isa) { |
| Isa = Row.Isa; |
| MCOS->emitInt8(dwarf::DW_LNS_set_isa); |
| MCOS->emitULEB128IntValue(Isa); |
| } |
| if (Row.IsStmt != Flags) { |
| Flags = Row.IsStmt; |
| MCOS->emitInt8(dwarf::DW_LNS_negate_stmt); |
| } |
| if (Row.BasicBlock) |
| MCOS->emitInt8(dwarf::DW_LNS_set_basic_block); |
| if (Row.PrologueEnd) |
| MCOS->emitInt8(dwarf::DW_LNS_set_prologue_end); |
| if (Row.EpilogueBegin) |
| MCOS->emitInt8(dwarf::DW_LNS_set_epilogue_begin); |
| |
| // The end of the sequence is not normal in the middle of the input |
| // sequence, but could happen, e.g. for assembly code. |
| if (Row.EndSequence) { |
| emitEndOfSequence(Address); |
| } else { |
| if (LastAddress == InvalidAddress) |
| emitDwarfSetLineAddrAbs(*MCOS, Params, LineDelta, Address, |
| AsmInfo->getCodePointerSize()); |
| else |
| MCDwarfLineAddr::Emit(MCOS, Params, LineDelta, Address - LastAddress); |
| |
| LastAddress = Address; |
| LastLine = Row.Line; |
| } |
| |
| Discriminator = 0; |
| } |
| PrevEndOfSequence = Sequence.EndAddress; |
| } |
| |
| // Finish with the end of the sequence. |
| if (LastAddress != InvalidAddress) |
| emitEndOfSequence(PrevEndOfSequence); |
| } |
| |
| // This function is similar to the one from MCDwarfLineTable, except it handles |
| // end-of-sequence entries differently by utilizing line entries with |
| // DWARF2_FLAG_END_SEQUENCE flag. |
| static inline void emitDwarfLineTable( |
| MCStreamer *MCOS, MCSection *Section, |
| const MCLineSection::MCDwarfLineEntryCollection &LineEntries) { |
| unsigned FileNum = 1; |
| unsigned LastLine = 1; |
| unsigned Column = 0; |
| unsigned Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; |
| unsigned Isa = 0; |
| unsigned Discriminator = 0; |
| MCSymbol *LastLabel = nullptr; |
| const MCAsmInfo *AsmInfo = MCOS->getContext().getAsmInfo(); |
| |
| // Loop through each MCDwarfLineEntry and encode the dwarf line number table. |
| for (const MCDwarfLineEntry &LineEntry : LineEntries) { |
| if (LineEntry.getFlags() & DWARF2_FLAG_END_SEQUENCE) { |
| MCOS->emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, LineEntry.getLabel(), |
| AsmInfo->getCodePointerSize()); |
| FileNum = 1; |
| LastLine = 1; |
| Column = 0; |
| Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; |
| Isa = 0; |
| Discriminator = 0; |
| LastLabel = nullptr; |
| continue; |
| } |
| |
| int64_t LineDelta = static_cast<int64_t>(LineEntry.getLine()) - LastLine; |
| |
| if (FileNum != LineEntry.getFileNum()) { |
| FileNum = LineEntry.getFileNum(); |
| MCOS->emitInt8(dwarf::DW_LNS_set_file); |
| MCOS->emitULEB128IntValue(FileNum); |
| } |
| if (Column != LineEntry.getColumn()) { |
| Column = LineEntry.getColumn(); |
| MCOS->emitInt8(dwarf::DW_LNS_set_column); |
| MCOS->emitULEB128IntValue(Column); |
| } |
| if (Discriminator != LineEntry.getDiscriminator() && |
| MCOS->getContext().getDwarfVersion() >= 2) { |
| Discriminator = LineEntry.getDiscriminator(); |
| unsigned Size = getULEB128Size(Discriminator); |
| MCOS->emitInt8(dwarf::DW_LNS_extended_op); |
| MCOS->emitULEB128IntValue(Size + 1); |
| MCOS->emitInt8(dwarf::DW_LNE_set_discriminator); |
| MCOS->emitULEB128IntValue(Discriminator); |
| } |
| if (Isa != LineEntry.getIsa()) { |
| Isa = LineEntry.getIsa(); |
| MCOS->emitInt8(dwarf::DW_LNS_set_isa); |
| MCOS->emitULEB128IntValue(Isa); |
| } |
| if ((LineEntry.getFlags() ^ Flags) & DWARF2_FLAG_IS_STMT) { |
| Flags = LineEntry.getFlags(); |
| MCOS->emitInt8(dwarf::DW_LNS_negate_stmt); |
| } |
| if (LineEntry.getFlags() & DWARF2_FLAG_BASIC_BLOCK) |
| MCOS->emitInt8(dwarf::DW_LNS_set_basic_block); |
| if (LineEntry.getFlags() & DWARF2_FLAG_PROLOGUE_END) |
| MCOS->emitInt8(dwarf::DW_LNS_set_prologue_end); |
| if (LineEntry.getFlags() & DWARF2_FLAG_EPILOGUE_BEGIN) |
| MCOS->emitInt8(dwarf::DW_LNS_set_epilogue_begin); |
| |
| MCSymbol *Label = LineEntry.getLabel(); |
| |
| // At this point we want to emit/create the sequence to encode the delta |
| // in line numbers and the increment of the address from the previous |
| // Label and the current Label. |
| MCOS->emitDwarfAdvanceLineAddr(LineDelta, LastLabel, Label, |
| AsmInfo->getCodePointerSize()); |
| Discriminator = 0; |
| LastLine = LineEntry.getLine(); |
| LastLabel = Label; |
| } |
| |
| assert(LastLabel == nullptr && "end of sequence expected"); |
| } |
| |
| void DwarfLineTable::emitCU(MCStreamer *MCOS, MCDwarfLineTableParams Params, |
| std::optional<MCDwarfLineStr> &LineStr, |
| BinaryContext &BC) const { |
| if (!RawData.empty()) { |
| assert(MCLineSections.getMCLineEntries().empty() && |
| InputSequences.empty() && |
| "cannot combine raw data with new line entries"); |
| MCOS->emitLabel(getLabel()); |
| MCOS->emitBytes(RawData); |
| return; |
| } |
| |
| MCSymbol *LineEndSym = Header.Emit(MCOS, Params, LineStr).second; |
| |
| // Put out the line tables. |
| for (const auto &LineSec : MCLineSections.getMCLineEntries()) |
| emitDwarfLineTable(MCOS, LineSec.first, LineSec.second); |
| |
| // Emit line tables for the original code. |
| emitBinaryDwarfLineTable(MCOS, Params, InputTable, InputSequences); |
| |
| // This is the end of the section, so set the value of the symbol at the end |
| // of this section (that was used in a previous expression). |
| MCOS->emitLabel(LineEndSym); |
| } |
| |
| // Helper function to parse .debug_line_str, and populate one we are using. |
| // For functions that we do not modify we output them as raw data. |
| // Re-constructing .debug_line_str so that offsets are correct for those |
| // debug line tables. |
| // Bonus is that when we output a final binary we can re-use .debug_line_str |
| // section. So we don't have to do the SHF_ALLOC trick we did with |
| // .debug_line. |
| static void parseAndPopulateDebugLineStr(BinarySection &LineStrSection, |
| MCDwarfLineStr &LineStr, |
| BinaryContext &BC) { |
| DataExtractor StrData(LineStrSection.getContents(), |
| BC.DwCtx->isLittleEndian(), 0); |
| uint64_t Offset = 0; |
| while (StrData.isValidOffset(Offset)) { |
| const uint64_t StrOffset = Offset; |
| Error Err = Error::success(); |
| const char *CStr = StrData.getCStr(&Offset, &Err); |
| if (Err) { |
| BC.errs() << "BOLT-ERROR: could not extract string from .debug_line_str"; |
| continue; |
| } |
| const size_t NewOffset = LineStr.addString(CStr); |
| assert(StrOffset == NewOffset && |
| "New offset in .debug_line_str doesn't match original offset"); |
| (void)StrOffset; |
| (void)NewOffset; |
| } |
| } |
| |
| void DwarfLineTable::emit(BinaryContext &BC, MCStreamer &Streamer) { |
| MCAssembler &Assembler = |
| static_cast<MCObjectStreamer *>(&Streamer)->getAssembler(); |
| |
| MCDwarfLineTableParams Params = Assembler.getDWARFLinetableParams(); |
| |
| auto &LineTables = BC.getDwarfLineTables(); |
| |
| // Bail out early so we don't switch to the debug_line section needlessly and |
| // in doing so create an unnecessary (if empty) section. |
| if (LineTables.empty()) |
| return; |
| // In a v5 non-split line table, put the strings in a separate section. |
| std::optional<MCDwarfLineStr> LineStr; |
| ErrorOr<BinarySection &> LineStrSection = |
| BC.getUniqueSectionByName(".debug_line_str"); |
| |
| // Some versions of GCC output DWARF5 .debug_info, but DWARF4 or lower |
| // .debug_line, so need to check if section exists. |
| if (LineStrSection) { |
| LineStr.emplace(*BC.Ctx); |
| parseAndPopulateDebugLineStr(*LineStrSection, *LineStr, BC); |
| } |
| |
| // Switch to the section where the table will be emitted into. |
| Streamer.switchSection(BC.MOFI->getDwarfLineSection()); |
| |
| const uint16_t DwarfVersion = BC.Ctx->getDwarfVersion(); |
| // Handle the rest of the Compile Units. |
| for (auto &CUIDTablePair : LineTables) { |
| Streamer.getContext().setDwarfVersion( |
| CUIDTablePair.second.getDwarfVersion()); |
| CUIDTablePair.second.emitCU(&Streamer, Params, LineStr, BC); |
| } |
| |
| // Resetting DWARF version for rest of the flow. |
| BC.Ctx->setDwarfVersion(DwarfVersion); |
| |
| // Still need to write the section out for the ExecutionEngine, and temp in |
| // memory object we are constructing. |
| if (LineStr) |
| LineStr->emitSection(&Streamer); |
| } |
| |
| } // namespace bolt |
| } // namespace llvm |