| //===- 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/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/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 <memory> | |
| #include <optional> | |
| #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); | |
| } | |
| void forEachDIEInUnit(DWARFUnit &Unit, | |
| function_ref<void(const DWARFDie &)> Callback) { | |
| DWARFDataExtractor DebugInfoData = Unit.getDebugInfoExtractor(); | |
| uint64_t DIEOffset = Unit.getOffset() + Unit.getHeaderSize(); | |
| const uint64_t NextCUOffset = Unit.getNextUnitOffset(); | |
| DWARFDebugInfoEntry DIEEntry; | |
| // ParentIdx is passed as 0 throughout: we visit every DIE but never | |
| // reconstruct the tree, so the parent linkage extractFast would record is | |
| // unused. The single reusable transient entry means no DIE vector is built. | |
| while (DIEOffset < NextCUOffset && | |
| DIEEntry.extractFast(Unit, &DIEOffset, DebugInfoData, NextCUOffset, | |
| /*ParentIdx=*/0)) { | |
| if (!DIEEntry.getAbbreviationDeclarationPtr()) | |
| continue; // Null entry: terminator of a sibling chain. | |
| DWARFDie Die(&Unit, &DIEEntry); | |
| Callback(Die); | |
| } | |
| } | |
| [[maybe_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; | |
| } | |
| /// Return the size of a DWARF v5 .debug_addr contribution header. | |
| static uint32_t getHeaderSize(dwarf::DwarfFormat Format) { | |
| // The initial length is followed by version(2), address size(1), | |
| // and segment size(1). | |
| return dwarf::getUnitLengthFieldByteSize(Format) + sizeof(uint16_t) + | |
| sizeof(uint8_t) + sizeof(uint8_t); | |
| } | |
| DebugRangesSectionWriter::DebugRangesSectionWriter() { | |
| RangesBuffer = std::make_unique<DebugBufferVector>(); | |
| RangesStream = std::make_unique<raw_svector_ostream>(*RangesBuffer); | |
| Kind = RangesWriterKind::DebugRangesWriter; | |
| } | |
| void DebugRangesSectionWriter::initSection() { | |
| // Adds an empty range to the buffer. | |
| writeAddressRanges(*RangesStream, DebugAddressRangesVector{}); | |
| } | |
| 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 = RangesBuffer->size(); | |
| writeAddressRanges(*RangesStream, Ranges); | |
| return EntryOffset; | |
| } | |
| uint64_t DebugRangesSectionWriter::getSectionOffset() { | |
| std::lock_guard<std::mutex> Lock(WriterMutex); | |
| return RangesBuffer->size(); | |
| } | |
| void DebugRangesSectionWriter::appendToRangeBuffer( | |
| const DebugBufferVector &CUBuffer) { | |
| *RangesStream << CUBuffer; | |
| } | |
| uint64_t DebugRangeListsSectionWriter::addRanges( | |
| DebugAddressRangesVector &&Ranges, | |
| std::map<DebugAddressRangesVector, uint64_t> &CachedRanges) { | |
| return addRanges(Ranges); | |
| } | |
| struct LocListsRangelistsHeader { | |
| dwarf::DwarfFormat Format; | |
| 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(Header.Format) - | |
| dwarf::getUnitLengthFieldByteSize(Header.Format); | |
| if (Header.Format == dwarf::DwarfFormat::DWARF64) | |
| support::endian::write(*HeaderStream, dwarf::DW_LENGTH_DWARF64, | |
| llvm::endianness::little); | |
| writeDWARFLengthOrOffset(*HeaderStream, Header.Format, | |
| Header.UnitLength + HeaderSize); | |
| 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; | |
| uint64_t StartOffset; | |
| uint64_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 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; | |
| // In case rnglists or loclists are not sorted. | |
| if (Base > Entry.LowPC) | |
| break; | |
| uint64_t StartOffset = Entry.LowPC - Base; | |
| uint64_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); | |
| } | |
| return true; | |
| } | |
| uint64_t | |
| DebugRangeListsSectionWriter::addRanges(DebugAddressRangesVector &Ranges) { | |
| std::lock_guard<std::mutex> Lock(WriterMutex); | |
| RangeEntries.push_back(CurrentOffset); | |
| 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();) { | |
| if (emitWithBase<DebugAddressRangesVector, dwarf::RnglistEntries, | |
| DebugAddressRange>(*CUBodyStream, Ranges, *AddrWriter, *CU, | |
| I, dwarf::DW_RLE_base_addressx, | |
| dwarf::DW_RLE_offset_pair, | |
| [](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; | |
| } | |
| 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); | |
| const dwarf::DwarfFormat Format = CU->getFormParams().Format; | |
| const uint64_t SizeOfArraySection = | |
| RangeEntries.size() * dwarf::getDwarfOffsetByteSize(Format); | |
| for (uint64_t Offset : RangeEntries) | |
| writeDWARFLengthOrOffset(*CUArrayStream, Format, | |
| Offset + SizeOfArraySection); | |
| std::unique_ptr<DebugBufferVector> Header = | |
| getDWARF5Header({Format, SizeOfArraySection + CUBodyBuffer->size(), 5, 8, | |
| 0, static_cast<uint32_t>(RangeEntries.size())}); | |
| *RangesStream << *Header; | |
| *RangesStream << *CUArrayBuffer; | |
| *RangesStream << *CUBodyBuffer; | |
| } | |
| 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; | |
| assert(CUMap.count(Offset) && "Original CU offset is not found in CU Map"); | |
| const CUInfo &Info = CUMap.find(Offset)->second; | |
| const dwarf::DwarfFormat Format = Info.Format; | |
| const uint8_t DwarfOffsetByteSize = dwarf::getDwarfOffsetByteSize(Format); | |
| // Emit header. | |
| // Header size: version(2) + offset(4/8) + address size(1) + segment(1). | |
| const uint64_t HeaderSize = sizeof(uint16_t) + DwarfOffsetByteSize + | |
| sizeof(uint8_t) + sizeof(uint8_t); | |
| // Size of this set: header (8/12 bytes depending on the DWARF format) + | |
| // padding after header (4/8 bytes) + 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. | |
| const uint64_t Size = HeaderSize + DwarfOffsetByteSize + | |
| 2 * sizeof(uint64_t) * (AddressRanges.size() + 1); | |
| // Header field #1: set size. | |
| if (Format == dwarf::DwarfFormat::DWARF64) | |
| support::endian::write(RangesStream, dwarf::DW_LENGTH_DWARF64, | |
| llvm::endianness::little); | |
| writeDWARFLengthOrOffset(RangesStream, Format, Size); | |
| // Header field #2: version number, 2 as per the specification. | |
| support::endian::write(RangesStream, static_cast<uint16_t>(2), | |
| llvm::endianness::little); | |
| // Header field #3: debug info offset of the correspondent compile unit. | |
| writeDWARFLengthOrOffset(RangesStream, Format, Info.Offset); | |
| // 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/8 bytes. | |
| writeDWARFLengthOrOffset(RangesStream, Format, 0); | |
| writeAddressRanges(RangesStream, AddressRanges, true); | |
| } | |
| } | |
| DebugAddrWriter::DebugAddrWriter(BinaryContext *BC, | |
| const uint8_t AddressByteSize) | |
| : BC(BC), AddressByteSize(AddressByteSize) { | |
| 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); | |
| 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::updateAddrBase(DIEBuilder &DIEBlder, DWARFUnit &CU, | |
| const uint64_t Offset) { | |
| updateAddressBase(DIEBlder, *this, CU, Offset); | |
| } | |
| std::optional<uint64_t> DebugAddrWriter::finalize(const size_t BufferSize) { | |
| if (Map.begin() == Map.end()) | |
| return std::nullopt; | |
| std::vector<IndexAddressPair> SortedMap(Map.indexToAddressBegin(), | |
| Map.indexToAdddessEnd()); | |
| // Sorting address in increasing order of indices. | |
| llvm::sort(SortedMap, llvm::less_first()); | |
| uint32_t Counter = 0; | |
| auto WriteAddress = [&](uint64_t Address) -> void { | |
| ++Counter; | |
| switch (AddressByteSize) { | |
| 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); | |
| } | |
| return std::nullopt; | |
| } | |
| void DebugAddrWriterDwarf5::updateAddrBase(DIEBuilder &DIEBlder, DWARFUnit &CU, | |
| const uint64_t Offset) { | |
| /// Header for DWARF5 has size 8 bytes in DWARF32 or 16 bytes in DWARF64, | |
| /// so we add it to the offset. | |
| updateAddressBase(DIEBlder, *this, CU, Offset + getHeaderSize(Format)); | |
| } | |
| DenseMap<uint64_t, uint64_t> DebugAddrWriter::UnmodifiedAddressOffsets; | |
| std::optional<uint64_t> | |
| DebugAddrWriterDwarf5::finalize(const size_t BufferSize) { | |
| // 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; | |
| // A case where CU has entry in .debug_addr, but we don't modify addresses | |
| // for it. | |
| if (Map.begin() == Map.end()) { | |
| if (!AddrOffsetSectionBase) | |
| return std::nullopt; | |
| // Address base offset is to the first entry. | |
| uint64_t Offset = *AddrOffsetSectionBase - getHeaderSize(Format); | |
| auto Iter = UnmodifiedAddressOffsets.find(Offset); | |
| if (Iter != UnmodifiedAddressOffsets.end()) | |
| return Iter->second; | |
| UnmodifiedAddressOffsets[Offset] = BufferSize; | |
| if (Error Err = AddrTable.extract(AddrData, &Offset, 5, AddressByteSize, | |
| DumpOpts.WarningHandler)) { | |
| DumpOpts.RecoverableErrorHandler(std::move(Err)); | |
| return std::nullopt; | |
| } | |
| uint32_t Index = 0; | |
| for (uint64_t Addr : AddrTable.getAddressEntries()) | |
| Map.insert(Addr, Index++); | |
| } | |
| std::vector<IndexAddressPair> SortedMap(Map.indexToAddressBegin(), | |
| Map.indexToAdddessEnd()); | |
| // Sorting address in increasing order of indices. | |
| llvm::sort(SortedMap, llvm::less_first()); | |
| // Writing out Header | |
| const uint64_t Length = SortedMap.size() * AddressByteSize + 4; | |
| if (Format == dwarf::DwarfFormat::DWARF64) | |
| support::endian::write(*AddressStream, dwarf::DW_LENGTH_DWARF64, Endian); | |
| writeDWARFLengthOrOffset(*AddressStream, Format, Length, Endian); | |
| support::endian::write(*AddressStream, static_cast<uint16_t>(5), Endian); | |
| support::endian::write(*AddressStream, static_cast<uint8_t>(AddressByteSize), | |
| Endian); | |
| support::endian::write(*AddressStream, static_cast<uint8_t>(0), Endian); | |
| uint32_t Counter = 0; | |
| auto writeAddress = [&](uint64_t Address) -> void { | |
| ++Counter; | |
| switch (AddressByteSize) { | |
| 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); | |
| } | |
| return std::nullopt; | |
| } | |
| 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 (DwarfVersion < 5) { | |
| const char Zeroes[16] = {0}; | |
| *LocStream << StringRef(Zeroes, 16); | |
| } | |
| } | |
| 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 = LocBuffer->size(); | |
| 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()); | |
| } | |
| LocStream->write_zeros(16); | |
| LocListDebugInfoPatches.push_back({0xdeadbeee, EntryOffset}); | |
| replaceLocValbyForm(DIEBldr, Die, AttrInfo, AttrInfo.getForm(), EntryOffset); | |
| LocListPatches.push_back({&Die, AttrInfo.getAttribute(), AttrInfo.getForm()}); | |
| } | |
| std::unique_ptr<DebugBufferVector> DebugLocWriter::getBuffer() { | |
| return std::move(LocBuffer); | |
| } | |
| // DWARF 4: 2.6.2 | |
| void DebugLocWriter::finalize(DIEBuilder &DIEBldr, DIE &Die) {} | |
| /// Rebases all pending location list offsets by adding \p Base, | |
| /// updating the corresponding attributes in each patched DIE. | |
| void DebugLocWriter::applyBase(DIEBuilder &DIEBldr, uint64_t Base) { | |
| if (Base == 0) { | |
| LocListPatches.clear(); | |
| return; | |
| } | |
| for (const LocListPatch &LP : LocListPatches) { | |
| if (!LP.Die) | |
| continue; | |
| DIEValue Cur = LP.Die->findAttribute(LP.Attr); | |
| if (!Cur.getType()) | |
| continue; | |
| uint64_t OldVal = Cur.getDIEInteger().getValue(); | |
| if (OldVal == DebugLocWriter::EmptyListOffset) | |
| continue; | |
| DIEBldr.replaceValue(LP.Die, LP.Attr, LP.Form, DIEInteger(OldVal + Base)); | |
| } | |
| LocListPatches.clear(); | |
| } | |
| 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<uint64_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; | |
| } | |
| 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();) { | |
| if (emitWithBase<DebugLocationsVector, dwarf::LoclistEntries, | |
| DebugLocationEntry>(LocBodyStream, LocList, AddrWriter, CU, | |
| I, dwarf::DW_LLE_base_addressx, | |
| dwarf::DW_LLE_offset_pair, | |
| 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; | |
| } | |
| 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); | |
| } | |
| 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( | |
| CU.getFormParams().Format))); | |
| 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 dwarf::DwarfFormat Format = CU.getFormParams().Format; | |
| const uint64_t SizeOfArraySection = | |
| NumberOfEntries * dwarf::getDwarfOffsetByteSize(Format); | |
| // Write out IndexArray | |
| for (uint64_t RelativeOffset : RelativeLocListOffsets) | |
| writeDWARFLengthOrOffset(*LocArrayStream, Format, | |
| SizeOfArraySection + RelativeOffset); | |
| std::unique_ptr<DebugBufferVector> Header = | |
| getDWARF5Header({Format, SizeOfArraySection + LocBodyBuffer->size(), 5, 8, | |
| 0, NumberOfEntries}); | |
| *LocStream << *Header; | |
| *LocStream << *LocArrayBuffer; | |
| *LocStream << *LocBodyBuffer; | |
| if (!isSplitDwarf()) { | |
| const uint64_t LocalBase = getDWARF5RngListLocListHeaderSize(Format); | |
| DIEValue LocListBaseAttrInfo = | |
| Die.findAttribute(dwarf::DW_AT_loclists_base); | |
| if (LocListBaseAttrInfo.getType()) { | |
| DIEBldr.replaceValue(&Die, dwarf::DW_AT_loclists_base, | |
| LocListBaseAttrInfo.getForm(), | |
| DIEInteger(LocalBase)); | |
| } else { | |
| DIEBldr.addValue(&Die, dwarf::DW_AT_loclists_base, | |
| dwarf::DW_FORM_sec_offset, DIEInteger(LocalBase)); | |
| } | |
| } | |
| clearList(RelativeLocListOffsets); | |
| clearList(*LocArrayBuffer); | |
| clearList(*LocBodyBuffer); | |
| } | |
| void DebugLoclistWriter::finalize(DIEBuilder &DIEBldr, DIE &Die) { | |
| if (DwarfVersion >= 5) | |
| finalizeDWARF5(DIEBldr, Die); | |
| } | |
| 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) { | |
| uint64_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 dwarf::DwarfFormat Format = Unit.getFormParams().Format; | |
| const uint8_t DwarfOffsetByteSize = Contr->getDwarfOffsetByteSize(); | |
| StrOffsets.reserve(Contr->Size / DwarfOffsetByteSize); | |
| for (uint64_t Offset = 0; Offset < Contr->Size; | |
| Offset += DwarfOffsetByteSize) { | |
| uint64_t Value = | |
| Format == dwarf::DwarfFormat::DWARF32 | |
| ? support::endian::read32le(StrOffsetsSection.Data.data() + | |
| Contr->Base + Offset) | |
| : support::endian::read64le(StrOffsetsSection.Data.data() + | |
| Contr->Base + Offset); | |
| StrOffsets.push_back(Value); | |
| } | |
| } | |
| void DebugStrOffsetsWriter::updateAddressMap(uint32_t Index, uint64_t Address, | |
| const DWARFUnit &Unit) { | |
| assert(DebugStrOffsetFinalized.count(Unit.getOffset()) == 0 && | |
| "Cannot update address map since debug_str_offsets was already " | |
| "finalized for this CU."); | |
| 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 && !Unit.isDWOUnit()) | |
| return; | |
| std::optional<uint64_t> Val = std::nullopt; | |
| if (AttrVal) { | |
| Val = AttrVal->V.getAsSectionOffset(); | |
| } else { | |
| if (!Unit.isDWOUnit()) | |
| BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: " | |
| "DW_AT_str_offsets_base Value not present\n"; | |
| Val = 0; | |
| } | |
| DIE &Die = *DIEBldr.getUnitDIEbyUnit(Unit); | |
| DIEValue StrListBaseAttrInfo = | |
| Die.findAttribute(dwarf::DW_AT_str_offsets_base); | |
| auto RetVal = ProcessedBaseOffsets.find(*Val); | |
| // Handling reuse 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; | |
| const dwarf::DwarfFormat Format = Unit.getFormParams().Format; | |
| // Writing out the header for each section. | |
| if (Format == dwarf::DwarfFormat::DWARF64) | |
| support::endian::write(*StrOffsetsStream, dwarf::DW_LENGTH_DWARF64, | |
| llvm::endianness::little); | |
| writeDWARFLengthOrOffset(*StrOffsetsStream, Format, | |
| StrOffsets.size() * | |
| dwarf::getDwarfOffsetByteSize(Format) + | |
| sizeof(uint16_t) + sizeof(uint16_t)); | |
| 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 uint64_t Offset : StrOffsets) | |
| writeDWARFLengthOrOffset(*StrOffsetsStream, Format, Offset); | |
| } else { | |
| DIEBldr.replaceValue(&Die, dwarf::DW_AT_str_offsets_base, | |
| StrListBaseAttrInfo.getForm(), | |
| DIEInteger(RetVal->second)); | |
| } | |
| StrOffsetSectionWasModified = false; | |
| assert(DebugStrOffsetFinalized.insert(Unit.getOffset()).second && | |
| "debug_str_offsets was already finalized for this CU."); | |
| clear(); | |
| } | |
| void DebugStrWriter::create() { | |
| StrBuffer = std::make_unique<DebugStrBufferVector>(); | |
| StrStream = std::make_unique<raw_svector_ostream>(*StrBuffer); | |
| } | |
| void DebugStrWriter::initialize() { | |
| StringRef StrSection; | |
| if (IsDWO) | |
| StrSection = DwCtx.getDWARFObj().getStrDWOSection(); | |
| else | |
| StrSection = 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 reuse .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()); | |
| 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(); | |
| const dwarf::DwarfFormat Format = BC.Ctx->getDwarfFormat(); | |
| // Handle the rest of the Compile Units. | |
| for (auto &CUIDTablePair : LineTables) { | |
| Streamer.getContext().setDwarfVersion( | |
| CUIDTablePair.second.getDwarfVersion()); | |
| Streamer.getContext().setDwarfFormat(CUIDTablePair.second.getFormat()); | |
| CUIDTablePair.second.emitCU(&Streamer, Params, LineStr, BC); | |
| } | |
| // Resetting DWARF version for rest of the flow. | |
| BC.Ctx->setDwarfVersion(DwarfVersion); | |
| BC.Ctx->setDwarfFormat(Format); | |
| // 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 |