| //===- bolt/Rewrite/DWARFRewriter.cpp -------------------------------------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "bolt/Rewrite/DWARFRewriter.h" | |
| #include "bolt/Core/BinaryContext.h" | |
| #include "bolt/Core/BinaryFunction.h" | |
| #include "bolt/Core/DIEBuilder.h" | |
| #include "bolt/Core/DebugData.h" | |
| #include "bolt/Core/DynoStats.h" | |
| #include "bolt/Core/ParallelUtilities.h" | |
| #include "bolt/Rewrite/RewriteInstance.h" | |
| #include "llvm/ADT/DenseSet.h" | |
| #include "llvm/ADT/EquivalenceClasses.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/BinaryFormat/Dwarf.h" | |
| #include "llvm/CodeGen/AsmPrinter.h" | |
| #include "llvm/CodeGen/DIE.h" | |
| #include "llvm/DWARFLinker/Classic/DWARFStreamer.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFContext.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFUnit.h" | |
| #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h" | |
| #include "llvm/MC/MCAsmBackend.h" | |
| #include "llvm/MC/MCAssembler.h" | |
| #include "llvm/MC/MCObjectWriter.h" | |
| #include "llvm/MC/MCStreamer.h" | |
| #include "llvm/MC/MCTargetOptionsCommandFlags.h" | |
| #include "llvm/Object/ObjectFile.h" | |
| #include "llvm/Support/Casting.h" | |
| #include "llvm/Support/CommandLine.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/Endian.h" | |
| #include "llvm/Support/Error.h" | |
| #include "llvm/Support/FileSystem.h" | |
| #include "llvm/Support/LEB128.h" | |
| #include "llvm/Support/ThreadPool.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <algorithm> | |
| #include <cassert> | |
| #include <condition_variable> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <functional> | |
| #include <iterator> | |
| #include <limits> | |
| #include <memory> | |
| #include <mutex> | |
| #include <optional> | |
| #include <queue> | |
| #include <string> | |
| #include <unordered_map> | |
| #include <utility> | |
| #include <vector> | |
| #undef DEBUG_TYPE | |
| #define DEBUG_TYPE "bolt" | |
| static mc::RegisterMCTargetOptionsFlags MOF; | |
| static void printDie(const DWARFDie &DIE) { | |
| DIDumpOptions DumpOpts; | |
| DumpOpts.ShowForm = true; | |
| DumpOpts.Verbose = true; | |
| DumpOpts.ChildRecurseDepth = 0; | |
| DumpOpts.ShowChildren = false; | |
| DIE.dump(dbgs(), 0, DumpOpts); | |
| } | |
| /// Lazily parse DWARF DIE and print it out. | |
| [[maybe_unused]] | |
| static void printDie(DWARFUnit &DU, uint64_t DIEOffset) { | |
| uint64_t OriginalOffsets = DIEOffset; | |
| uint64_t NextCUOffset = DU.getNextUnitOffset(); | |
| DWARFDataExtractor DebugInfoData = DU.getDebugInfoExtractor(); | |
| DWARFDebugInfoEntry DIEEntry; | |
| if (DIEEntry.extractFast(DU, &DIEOffset, DebugInfoData, NextCUOffset, 0)) { | |
| if (DIEEntry.getAbbreviationDeclarationPtr()) { | |
| DWARFDie DDie(&DU, &DIEEntry); | |
| printDie(DDie); | |
| } else { | |
| dbgs() << "Failed to extract abbreviation for" | |
| << Twine::utohexstr(OriginalOffsets) << "\n"; | |
| } | |
| } else { | |
| dbgs() << "Failed to extract DIE for " << Twine::utohexstr(OriginalOffsets) | |
| << " \n"; | |
| } | |
| } | |
| using namespace bolt; | |
| llvm::ThreadPoolInterface & | |
| DWARFRewriter::getOrCreateDebugInfoThreadPool(unsigned ThreadsCount) { | |
| if (opts::NoThreads || ThreadsCount == 0) | |
| ThreadsCount = 1; | |
| if (DebugInfoThreadPool) | |
| return *DebugInfoThreadPool; | |
| DebugInfoThreadPool = std::make_unique<DefaultThreadPool>( | |
| llvm::hardware_concurrency(ThreadsCount)); | |
| return *DebugInfoThreadPool; | |
| } | |
| /// Take a set of DWARF address ranges corresponding to the input binary and | |
| /// translate them to a set of address ranges in the output binary. | |
| static DebugAddressRangesVector | |
| translateInputToOutputRanges(const BinaryFunction &BF, | |
| const DWARFAddressRangesVector &InputRanges) { | |
| DebugAddressRangesVector OutputRanges; | |
| // If the function hasn't changed return the same ranges. | |
| if (!BF.isEmitted()) { | |
| OutputRanges.resize(InputRanges.size()); | |
| llvm::transform(InputRanges, OutputRanges.begin(), | |
| [](const DWARFAddressRange &Range) { | |
| return DebugAddressRange(Range.LowPC, Range.HighPC); | |
| }); | |
| return OutputRanges; | |
| } | |
| for (const DWARFAddressRange &Range : InputRanges) | |
| llvm::append_range(OutputRanges, BF.translateInputToOutputRange( | |
| {Range.LowPC, Range.HighPC})); | |
| // Post-processing pass to sort and merge ranges. | |
| llvm::sort(OutputRanges); | |
| DebugAddressRangesVector MergedRanges; | |
| uint64_t PrevHighPC = 0; | |
| for (const DebugAddressRange &Range : OutputRanges) { | |
| if (Range.LowPC <= PrevHighPC) { | |
| MergedRanges.back().HighPC = | |
| std::max(MergedRanges.back().HighPC, Range.HighPC); | |
| } else { | |
| MergedRanges.emplace_back(Range.LowPC, Range.HighPC); | |
| } | |
| PrevHighPC = MergedRanges.back().HighPC; | |
| } | |
| return MergedRanges; | |
| } | |
| /// Similar to translateInputToOutputRanges() but operates on location lists. | |
| static DebugLocationsVector | |
| translateInputToOutputLocationList(const BinaryFunction &BF, | |
| const DebugLocationsVector &InputLL) { | |
| DebugLocationsVector OutputLL; | |
| // If the function hasn't changed - there's nothing to update. | |
| if (!BF.isEmitted()) | |
| return InputLL; | |
| for (const DebugLocationEntry &Entry : InputLL) { | |
| DebugAddressRangesVector OutRanges = | |
| BF.translateInputToOutputRange({Entry.LowPC, Entry.HighPC}); | |
| if (!OutRanges.empty() && !OutputLL.empty()) { | |
| if (OutRanges.front().LowPC == OutputLL.back().HighPC && | |
| Entry.Expr == OutputLL.back().Expr) { | |
| OutputLL.back().HighPC = | |
| std::max(OutputLL.back().HighPC, OutRanges.front().HighPC); | |
| OutRanges.erase(OutRanges.begin()); | |
| } | |
| } | |
| llvm::transform(OutRanges, std::back_inserter(OutputLL), | |
| [&Entry](const DebugAddressRange &R) { | |
| return DebugLocationEntry{R.LowPC, R.HighPC, Entry.Expr}; | |
| }); | |
| } | |
| // Sort and merge adjacent entries with identical locations. | |
| llvm::stable_sort( | |
| OutputLL, [](const DebugLocationEntry &A, const DebugLocationEntry &B) { | |
| return A.LowPC < B.LowPC; | |
| }); | |
| DebugLocationsVector MergedLL; | |
| uint64_t PrevHighPC = 0; | |
| const SmallVectorImpl<uint8_t> *PrevExpr = nullptr; | |
| for (const DebugLocationEntry &Entry : OutputLL) { | |
| if (Entry.LowPC <= PrevHighPC && *PrevExpr == Entry.Expr) { | |
| MergedLL.back().HighPC = std::max(Entry.HighPC, MergedLL.back().HighPC); | |
| } else { | |
| const uint64_t Begin = std::max(Entry.LowPC, PrevHighPC); | |
| const uint64_t End = std::max(Begin, Entry.HighPC); | |
| MergedLL.emplace_back(DebugLocationEntry{Begin, End, Entry.Expr}); | |
| } | |
| PrevHighPC = MergedLL.back().HighPC; | |
| PrevExpr = &MergedLL.back().Expr; | |
| } | |
| return MergedLL; | |
| } | |
| using namespace dwarf_linker; | |
| using namespace dwarf_linker::classic; | |
| namespace llvm { | |
| namespace bolt { | |
| /// Emits debug information into .debug_info or .debug_types section. | |
| class DIEStreamer : public DwarfStreamer { | |
| DIEBuilder *DIEBldr; | |
| GDBIndex &GDBIndexSection; | |
| private: | |
| /// Emit the compilation unit header for \p Unit in the debug_info | |
| /// section. | |
| /// | |
| /// A Dwarf 4 section header is encoded as: | |
| /// uint32_t Unit length (omitting this field) | |
| /// uint16_t Version | |
| /// uint32_t Abbreviation table offset | |
| /// uint8_t Address size | |
| /// Leading to a total of 11 bytes. | |
| /// | |
| /// A Dwarf 5 section header is encoded as: | |
| /// uint32_t Unit length (omitting this field) | |
| /// uint16_t Version | |
| /// uint8_t Unit type | |
| /// uint8_t Address size | |
| /// uint32_t Abbreviation table offset | |
| /// Leading to a total of 12 bytes. | |
| void emitCompileUnitHeader(DWARFUnit &Unit, DIE &UnitDIE, | |
| unsigned DwarfVersion) { | |
| AsmPrinter &Asm = getAsmPrinter(); | |
| switchToDebugInfoSection(DwarfVersion); | |
| emitCommonHeader(Unit, UnitDIE, DwarfVersion); | |
| if (DwarfVersion >= 5 && | |
| Unit.getUnitType() != dwarf::UnitType::DW_UT_compile) { | |
| std::optional<uint64_t> DWOId = Unit.getDWOId(); | |
| assert(DWOId && | |
| "DWOId does not exist and this is not a DW_UT_compile Unit"); | |
| Asm.emitInt64(*DWOId); | |
| } | |
| } | |
| void emitCommonHeader(DWARFUnit &Unit, DIE &UnitDIE, uint16_t Version) { | |
| dwarf::UnitType UT = dwarf::UnitType(Unit.getUnitType()); | |
| llvm::AsmPrinter &Asm = getAsmPrinter(); | |
| // Emit size of content not including length itself | |
| Asm.emitDwarfUnitLength(Unit.getHeaderSize() + UnitDIE.getSize() - | |
| Asm.getUnitLengthFieldByteSize(), | |
| "Length of Unit"); | |
| Asm.emitInt16(Version); | |
| // DWARF v5 reorders the address size and adds a unit type. | |
| if (Version >= 5) { | |
| Asm.emitInt8(UT); | |
| Asm.emitInt8(Asm.MAI.getCodePointerSize()); | |
| } | |
| Asm.emitDwarfLengthOrOffset(0); | |
| if (Version <= 4) { | |
| Asm.emitInt8(Asm.MAI.getCodePointerSize()); | |
| } | |
| } | |
| void emitTypeUnitHeader(DWARFUnit &Unit, DIE &UnitDIE, | |
| unsigned DwarfVersion) { | |
| AsmPrinter &Asm = getAsmPrinter(); | |
| const uint64_t TypeSignature = cast<DWARFTypeUnit>(Unit).getTypeHash(); | |
| DIE *TypeDIE = DIEBldr->getTypeDIE(Unit); | |
| const DIEBuilder::DWARFUnitInfo &UI = DIEBldr->getUnitInfoByDwarfUnit(Unit); | |
| GDBIndexSection.addGDBTypeUnitEntry( | |
| {UI.UnitOffset, TypeSignature, TypeDIE->getOffset()}); | |
| if (Unit.getVersion() < 5) { | |
| // Switch the section to .debug_types section. | |
| std::unique_ptr<MCStreamer> &MS = Asm.OutStreamer; | |
| llvm::MCContext &MC = Asm.OutContext; | |
| const llvm::MCObjectFileInfo *MOFI = MC.getObjectFileInfo(); | |
| MS->switchSection(MOFI->getDwarfTypesSection(0)); | |
| MC.setDwarfVersion(DwarfVersion); | |
| } else | |
| switchToDebugInfoSection(DwarfVersion); | |
| emitCommonHeader(Unit, UnitDIE, DwarfVersion); | |
| Asm.OutStreamer->emitIntValue(TypeSignature, sizeof(TypeSignature)); | |
| Asm.emitDwarfLengthOrOffset(TypeDIE ? TypeDIE->getOffset() : 0); | |
| } | |
| void emitUnitHeader(DWARFUnit &Unit, DIE &UnitDIE) { | |
| if (Unit.isTypeUnit()) | |
| emitTypeUnitHeader(Unit, UnitDIE, Unit.getVersion()); | |
| else | |
| emitCompileUnitHeader(Unit, UnitDIE, Unit.getVersion()); | |
| } | |
| void emitDIE(DIE &Die) override { | |
| AsmPrinter &Asm = getAsmPrinter(); | |
| Asm.emitDwarfDIE(Die); | |
| } | |
| public: | |
| DIEStreamer(DIEBuilder *DIEBldr, GDBIndex &GDBIndexSection, | |
| DWARFLinkerBase::OutputFileType OutFileType, | |
| raw_pwrite_stream &OutFile, | |
| DWARFLinkerBase::MessageHandlerTy Warning) | |
| : DwarfStreamer(OutFileType, OutFile, Warning), DIEBldr(DIEBldr), | |
| GDBIndexSection(GDBIndexSection) {}; | |
| using DwarfStreamer::emitCompileUnitHeader; | |
| void emitUnit(DWARFUnit &Unit, DIE &UnitDIE) { | |
| emitUnitHeader(Unit, UnitDIE); | |
| emitDIE(UnitDIE); | |
| } | |
| }; | |
| /// Finds attributes FormValue and Offset. | |
| /// | |
| /// \param DIE die to look up in. | |
| /// \param Attrs finds the first attribute that matches and extracts it. | |
| /// \return an optional AttrInfo with DWARFFormValue and Offset. | |
| std::optional<AttrInfo> findAttributeInfo(const DWARFDie DIE, | |
| std::vector<dwarf::Attribute> Attrs) { | |
| for (dwarf::Attribute &Attr : Attrs) | |
| if (std::optional<AttrInfo> Info = findAttributeInfo(DIE, Attr)) | |
| return Info; | |
| return std::nullopt; | |
| } | |
| } // namespace bolt | |
| } // namespace llvm | |
| using namespace llvm; | |
| using namespace llvm::support::endian; | |
| using namespace object; | |
| using namespace bolt; | |
| namespace opts { | |
| extern cl::OptionCategory BoltCategory; | |
| extern cl::opt<unsigned> Verbosity; | |
| extern cl::opt<std::string> OutputFilename; | |
| static cl::opt<bool> KeepARanges( | |
| "keep-aranges", | |
| cl::desc( | |
| "keep or generate .debug_aranges section if .gdb_index is written"), | |
| cl::Hidden, cl::cat(BoltCategory)); | |
| static cl::opt<unsigned> | |
| DebugThreadCount("debug-thread-count", | |
| cl::desc("specifies thread count for the multithreading " | |
| "for updating DWARF debug info"), | |
| cl::init(32), cl::cat(BoltCategory)); | |
| static cl::opt<bool> | |
| PreserveOrder("dwarf-preserve-order", | |
| cl::desc("Update debug information in deterministic order"), | |
| cl::init(true), cl::cat(BoltCategory)); | |
| static cl::opt<std::string> DwarfOutputPath( | |
| "dwarf-output-path", | |
| cl::desc("Path to where .dwo files will be written out to."), cl::init(""), | |
| cl::cat(BoltCategory)); | |
| static cl::opt<bool> CreateDebugNames( | |
| "create-debug-names-section", | |
| cl::desc("Creates .debug_names section, if the input binary doesn't have " | |
| "it already, for DWARF5 CU/TUs."), | |
| cl::init(false), cl::cat(BoltCategory)); | |
| static cl::opt<bool> | |
| DebugSkeletonCu("debug-skeleton-cu", | |
| cl::desc("prints out offsets for abbrev and debug_info of " | |
| "Skeleton CUs that get patched."), | |
| cl::Hidden, cl::init(false), cl::cat(BoltCategory)); | |
| static cl::opt<unsigned> BatchSize( | |
| "cu-processing-batch-size", | |
| cl::desc( | |
| "Specifies the size of batches for processing CUs. Higher number has " | |
| "better performance, but more memory usage. Default value is 1."), | |
| cl::Hidden, cl::init(1), cl::cat(BoltCategory)); | |
| static cl::opt<bool> AlwaysConvertToRanges( | |
| "always-convert-to-ranges", | |
| cl::desc("This option is for testing purposes only. It forces BOLT to " | |
| "convert low_pc/high_pc to ranges always."), | |
| cl::ReallyHidden, cl::init(false), cl::cat(BoltCategory)); | |
| extern cl::opt<std::string> CompDirOverride; | |
| } // namespace opts | |
| /// True if \p Form belongs to DWARF's address class. A DW_AT_high_pc using one | |
| /// of these holds the end address; any other form holds an offset from | |
| /// DW_AT_low_pc. | |
| static bool isAddressForm(dwarf::Form Form) { | |
| switch (Form) { | |
| case dwarf::DW_FORM_addr: | |
| case dwarf::DW_FORM_addrx: | |
| case dwarf::DW_FORM_addrx1: | |
| case dwarf::DW_FORM_addrx2: | |
| case dwarf::DW_FORM_addrx3: | |
| case dwarf::DW_FORM_addrx4: | |
| case dwarf::DW_FORM_GNU_addr_index: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| /// True if \p Form stores an index into .debug_addr rather than the address | |
| /// itself. DW_FORM_LLVM_addrx_offset is deliberately excluded: it packs an | |
| /// index and an offset into a single value and needs its own handling. | |
| static bool isIndexedAddressForm(dwarf::Form Form) { | |
| switch (Form) { | |
| case dwarf::DW_FORM_addrx: | |
| case dwarf::DW_FORM_addrx1: | |
| case dwarf::DW_FORM_addrx2: | |
| case dwarf::DW_FORM_addrx3: | |
| case dwarf::DW_FORM_addrx4: | |
| case dwarf::DW_FORM_GNU_addr_index: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| /// If DW_AT_low_pc exists sets LowPC and returns true. | |
| static bool getLowPC(const DIE &Die, const DWARFUnit &DU, uint64_t &LowPC, | |
| uint64_t &SectionIndex) { | |
| DIEValue DvalLowPc = Die.findAttribute(dwarf::DW_AT_low_pc); | |
| if (!DvalLowPc) | |
| return false; | |
| dwarf::Form Form = DvalLowPc.getForm(); | |
| bool AddrOffset = Form == dwarf::DW_FORM_LLVM_addrx_offset; | |
| uint64_t LowPcValue = DvalLowPc.getDIEInteger().getValue(); | |
| if (isIndexedAddressForm(Form) || AddrOffset) { | |
| uint32_t Index = AddrOffset ? (LowPcValue >> 32) : LowPcValue; | |
| std::optional<object::SectionedAddress> SA = | |
| DU.getAddrOffsetSectionItem(Index); | |
| if (!SA) | |
| return false; | |
| if (AddrOffset) | |
| SA->Address += (LowPcValue & 0xffffffff); | |
| LowPC = SA->Address; | |
| SectionIndex = SA->SectionIndex; | |
| } else { | |
| LowPC = LowPcValue; | |
| SectionIndex = 0; | |
| } | |
| return true; | |
| } | |
| /// If DW_AT_high_pc exists sets HighPC and returns true. | |
| static bool getHighPC(const DIE &Die, const DWARFUnit &DU, const uint64_t LowPC, | |
| uint64_t &HighPC) { | |
| DIEValue DvalHighPc = Die.findAttribute(dwarf::DW_AT_high_pc); | |
| if (!DvalHighPc) | |
| return false; | |
| const dwarf::Form Form = DvalHighPc.getForm(); | |
| const uint64_t Value = DvalHighPc.getDIEInteger().getValue(); | |
| if (!isAddressForm(Form)) { | |
| // Class constant: an offset from DW_AT_low_pc. | |
| HighPC = LowPC + Value; | |
| return true; | |
| } | |
| if (!isIndexedAddressForm(Form)) { | |
| HighPC = Value; | |
| return true; | |
| } | |
| std::optional<object::SectionedAddress> SA = | |
| DU.getAddrOffsetSectionItem(Value); | |
| if (!SA) | |
| return false; | |
| HighPC = SA->Address; | |
| return true; | |
| } | |
| /// If DW_AT_low_pc and DW_AT_high_pc exist sets LowPC and HighPC and returns | |
| /// true. | |
| static bool getLowAndHighPC(const DIE &Die, const DWARFUnit &DU, | |
| uint64_t &LowPC, uint64_t &HighPC, | |
| uint64_t &SectionIndex) { | |
| uint64_t TempLowPC = LowPC; | |
| uint64_t TempHighPC = HighPC; | |
| uint64_t TempSectionIndex = SectionIndex; | |
| if (getLowPC(Die, DU, TempLowPC, TempSectionIndex) && | |
| getHighPC(Die, DU, TempLowPC, TempHighPC)) { | |
| LowPC = TempLowPC; | |
| HighPC = TempHighPC; | |
| SectionIndex = TempSectionIndex; | |
| return true; | |
| } | |
| return false; | |
| } | |
| static Expected<llvm::DWARFAddressRangesVector> | |
| getDIEAddressRanges(const DIE &Die, DWARFUnit &DU) { | |
| uint64_t LowPC, HighPC, Index; | |
| if (getLowAndHighPC(Die, DU, LowPC, HighPC, Index)) | |
| return DWARFAddressRangesVector{{LowPC, HighPC, Index}}; | |
| if (DIEValue Dval = Die.findAttribute(dwarf::DW_AT_ranges)) { | |
| if (Dval.getForm() == dwarf::DW_FORM_rnglistx) | |
| return DU.findRnglistFromIndex(Dval.getDIEInteger().getValue()); | |
| return DU.findRnglistFromOffset(Dval.getDIEInteger().getValue()); | |
| } | |
| return DWARFAddressRangesVector(); | |
| } | |
| static std::optional<uint64_t> getAsAddress(const DWARFUnit &DU, | |
| const DIEValue &AttrVal) { | |
| DWARFFormValue::ValueType Value(AttrVal.getDIEInteger().getValue()); | |
| if (std::optional<object::SectionedAddress> SA = | |
| DWARFFormValue::getAsSectionedAddress(Value, AttrVal.getForm(), &DU)) | |
| return SA->Address; | |
| return std::nullopt; | |
| } | |
| static std::unique_ptr<DIEStreamer> | |
| createDIEStreamer(const Triple &TheTriple, raw_pwrite_stream &OutFile, | |
| StringRef Swift5ReflectionSegmentName, DIEBuilder &DIEBldr, | |
| GDBIndex &GDBIndexSection) { | |
| std::unique_ptr<DIEStreamer> Streamer = std::make_unique<DIEStreamer>( | |
| &DIEBldr, GDBIndexSection, DWARFLinkerBase::OutputFileType::Object, | |
| OutFile, | |
| [&](const Twine &Warning, StringRef Context, const DWARFDie *) {}); | |
| Error Err = Streamer->init(TheTriple, Swift5ReflectionSegmentName); | |
| if (Err) | |
| errs() | |
| << "BOLT-WARNING: [internal-dwarf-error]: Could not init DIEStreamer!" | |
| << toString(std::move(Err)) << "\n"; | |
| return Streamer; | |
| } | |
| static void emitUnit(DIEBuilder &DIEBldr, DIEStreamer &Streamer, | |
| DWARFUnit &Unit) { | |
| DIE *UnitDIE = DIEBldr.getUnitDIEbyUnit(Unit); | |
| Streamer.getAsmPrinter().OutContext.setDwarfFormat( | |
| Unit.getFormParams().Format); | |
| Streamer.emitUnit(Unit, *UnitDIE); | |
| } | |
| static void emitDWOBuilder(const std::string &DWOName, | |
| DIEBuilder &DWODIEBuilder, DWARFRewriter &Rewriter, | |
| DWARFUnit &SplitCU, DWARFUnit &CU, | |
| DebugLocWriter &LocWriter, | |
| DebugStrOffsetsWriter &StrOffstsWriter, | |
| DebugStrWriter &StrWriter, GDBIndex &GDBIndexSection, | |
| DebugRangesSectionWriter &TempRangesSectionWriter) { | |
| // Populate debug_info and debug_abbrev for current dwo into StringRef. | |
| DWODIEBuilder.generateAbbrevs(); | |
| DWODIEBuilder.finish(); | |
| SmallVector<char, 20> OutBuffer; | |
| std::shared_ptr<raw_svector_ostream> ObjOS = | |
| std::make_shared<raw_svector_ostream>(OutBuffer); | |
| const object::ObjectFile *File = SplitCU.getContext().getDWARFObj().getFile(); | |
| auto TheTriple = std::make_unique<Triple>(File->makeTriple()); | |
| std::unique_ptr<DIEStreamer> Streamer = | |
| createDIEStreamer(*TheTriple, *ObjOS, "DwoStreamerInitAug2", | |
| DWODIEBuilder, GDBIndexSection); | |
| if (SplitCU.getContext().getMaxDWOVersion() >= 5) { | |
| for (DWARFUnit *CU : DWODIEBuilder.getDWARF5TUVector()) | |
| emitUnit(DWODIEBuilder, *Streamer, *CU); | |
| emitUnit(DWODIEBuilder, *Streamer, SplitCU); | |
| } else { | |
| emitUnit(DWODIEBuilder, *Streamer, SplitCU); | |
| // emit debug_types sections for dwarf4 | |
| for (DWARFUnit *CU : DWODIEBuilder.getDWARF4TUVector()) | |
| emitUnit(DWODIEBuilder, *Streamer, *CU); | |
| } | |
| Streamer->emitAbbrevs(DWODIEBuilder.getAbbrevs(), | |
| SplitCU.getContext().getMaxVersion()); | |
| Streamer->finish(); | |
| std::unique_ptr<MemoryBuffer> ObjectMemBuffer = | |
| MemoryBuffer::getMemBuffer(ObjOS->str(), "in-memory object file", false); | |
| std::unique_ptr<object::ObjectFile> Obj = cantFail( | |
| object::ObjectFile::createObjectFile(ObjectMemBuffer->getMemBufferRef()), | |
| "error creating in-memory object"); | |
| DWARFRewriter::OverriddenSectionsMap OverriddenSections; | |
| for (const SectionRef &Secs : Obj->sections()) { | |
| StringRef Contents = cantFail(Secs.getContents()); | |
| StringRef Name = cantFail(Secs.getName()); | |
| DWARFSectionKind Kind = | |
| StringSwitch<DWARFSectionKind>(Name) | |
| .Case(".debug_abbrev", DWARFSectionKind::DW_SECT_ABBREV) | |
| .Case(".debug_info", DWARFSectionKind::DW_SECT_INFO) | |
| .Case(".debug_types", DWARFSectionKind::DW_SECT_EXT_TYPES) | |
| .Default(DWARFSectionKind::DW_SECT_EXT_unknown); | |
| if (Kind == DWARFSectionKind::DW_SECT_EXT_unknown) | |
| continue; | |
| OverriddenSections[Kind] = Contents; | |
| } | |
| Rewriter.writeDWOFiles(CU, OverriddenSections, DWOName, LocWriter, | |
| StrOffstsWriter, StrWriter, TempRangesSectionWriter); | |
| } | |
| static SmallVector<SmallVector<DWARFUnit *>> partitionCUs(DWARFContext &DwCtx, | |
| const size_t Size) { | |
| SmallVector<DWARFUnit *, 0> AllCUs; | |
| AllCUs.reserve(Size); | |
| for (auto &CU : DwCtx.compile_units()) | |
| AllCUs.push_back(CU.get()); | |
| if (AllCUs.empty()) | |
| return {}; | |
| DenseSet<DWARFUnit *> CrossRefSet; | |
| EquivalenceClasses<DWARFUnit *> EC; | |
| for (DWARFUnit *CU : AllCUs) { | |
| const DWARFAbbreviationDeclarationSet *AbbrevSet = CU->getAbbreviations(); | |
| if (!AbbrevSet) | |
| continue; | |
| SmallDenseSet<const DWARFAbbreviationDeclaration *, 4> RefAddrAbbrevs; | |
| for (const auto &Decl : *AbbrevSet) | |
| for (const auto &Spec : Decl.attributes()) | |
| if (Spec.Form == dwarf::DW_FORM_ref_addr) { | |
| RefAddrAbbrevs.insert(&Decl); | |
| break; | |
| } | |
| if (RefAddrAbbrevs.empty()) | |
| continue; | |
| // Track CUs involved in cross-CU references via DW_FORM_ref_addr. Only DIE | |
| // attributes are inspected, so forEachDIEInUnit streams the DIEs without | |
| // materializing the unit's full DIE vector. | |
| forEachDIEInUnit(*CU, [&](const DWARFDie &Die) { | |
| if (!RefAddrAbbrevs.count(Die.getAbbreviationDeclarationPtr())) | |
| return; | |
| for (const DWARFAttribute &Attr : Die.attributes()) { | |
| if (Attr.Value.getForm() != dwarf::DW_FORM_ref_addr) | |
| continue; | |
| auto OptRef = Attr.Value.getAsDebugInfoReference(); | |
| if (!OptRef) | |
| continue; | |
| DWARFUnit *TargetCU = DwCtx.getUnitForOffset(*OptRef); | |
| if (!TargetCU || TargetCU == CU) | |
| continue; | |
| if (CrossRefSet.insert(CU).second) | |
| EC.insert(CU); | |
| if (CrossRefSet.insert(TargetCU).second) | |
| EC.insert(TargetCU); | |
| EC.unionSets(CU, TargetCU); | |
| } | |
| }); | |
| } | |
| DenseMap<DWARFUnit *, SmallVector<DWARFUnit *>> MembersByLeader; | |
| for (DWARFUnit *CU : AllCUs) { | |
| if (!CrossRefSet.count(CU)) | |
| continue; | |
| MembersByLeader[EC.getLeaderValue(CU)].push_back(CU); | |
| } | |
| SmallVector<DWARFUnit *> Leaders; | |
| Leaders.reserve(MembersByLeader.size()); | |
| for (auto &[Leader, Members] : MembersByLeader) | |
| Leaders.push_back(Leader); | |
| llvm::sort(Leaders, [&](DWARFUnit *A, DWARFUnit *B) { | |
| return MembersByLeader[A].front()->getOffset() < | |
| MembersByLeader[B].front()->getOffset(); | |
| }); | |
| // Emit cross-ref buckets, then singleton non-cross-ref CUs. | |
| SmallVector<SmallVector<DWARFUnit *>> Vec; | |
| for (DWARFUnit *Leader : Leaders) | |
| Vec.push_back(std::move(MembersByLeader[Leader])); | |
| for (DWARFUnit *CU : AllCUs) { | |
| if (!CrossRefSet.count(CU)) | |
| Vec.push_back({CU}); | |
| } | |
| return Vec; | |
| } | |
| static std::unordered_map<uint64_t, std::string> | |
| getDWONameMap(DWARFContext &DwCtx, const size_t Size) { | |
| std::unordered_map<uint64_t, std::string> DWOIDToNameMap(Size); | |
| std::unordered_map<std::string, uint32_t> NameToIndexMap(Size); | |
| for (const std::unique_ptr<DWARFUnit> &CU : DwCtx.compile_units()) { | |
| std::optional<uint64_t> DWOID = CU->getDWOId(); | |
| if (!DWOID) | |
| continue; | |
| const DWARFDie UnitDIE = CU->getUnitDIE(); | |
| auto DWONameAttr = | |
| UnitDIE.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}); | |
| // Skip invalid (broken) skeleton units that have a DWOId but lack | |
| // DW_AT_dwo_name / DW_AT_GNU_dwo_name. | |
| if (!CU->isDWOUnit() && !DWONameAttr) | |
| continue; | |
| std::string DWOName = dwarf::toString(DWONameAttr, ""); | |
| if (DWOName.empty()) | |
| continue; | |
| if (!opts::DwarfOutputPath.empty()) { | |
| DWOName = std::string(sys::path::filename(DWOName)); | |
| uint32_t &Index = NameToIndexMap[DWOName]; | |
| DWOName.append(std::to_string(Index)); | |
| ++Index; | |
| } | |
| DWOName.append(".dwo"); | |
| DWOIDToNameMap.emplace(*DWOID, std::move(DWOName)); | |
| } | |
| return DWOIDToNameMap; | |
| } | |
| void DWARFRewriter::finalizeMainCUStrOffsets( | |
| DIEBuilder &PartDIEBlder, ArrayRef<DWARFUnit *> SortedCUs, | |
| const std::unordered_map<uint64_t, std::string> &DWOToNameMap) { | |
| for (DWARFUnit *CU : SortedCUs) { | |
| const std::optional<uint64_t> DWOId = CU->getDWOId(); | |
| std::optional<DWARFUnit *> DWOCU = | |
| DWOId ? BC.getDWOCU(*DWOId) : std::nullopt; | |
| const bool HasSplitCU = DWOCU && *DWOCU != nullptr; | |
| const unsigned Version = CU->getVersion(); | |
| if (HasSplitCU) { | |
| auto It = DWOToNameMap.find(*DWOId); | |
| if (It != DWOToNameMap.end()) { | |
| PartDIEBlder.updateDWONameCompDir(*StrOffstsWriter, *StrWriter, *CU, | |
| opts::DwarfOutputPath, | |
| StringRef(It->second)); | |
| if (Version >= 5 && StrOffstsWriter->isStrOffsetsSectionModified()) | |
| StrOffstsWriter->finalizeSection(*CU, PartDIEBlder); | |
| } | |
| // split CU were just finalized above when the | |
| // .debug_str_offsets section was modified; | |
| continue; | |
| } | |
| if (Version >= 5) | |
| StrOffstsWriter->finalizeSection(*CU, PartDIEBlder); | |
| } | |
| } | |
| void DWARFRewriter::mergePerBucketLocs(DIEBuilder &PartDIEBlder, | |
| ArrayRef<DWARFUnit *> SortedCUs, | |
| BucketLocAccumOffset &Accum) { | |
| // Location lists are emitted later in Accum.LocListCUOrder order. Compute the | |
| // global base each CU will have in the final section and update DIEs now. | |
| for (DWARFUnit *CU : SortedCUs) { | |
| const uint64_t CUOffset = CU->getOffset(); | |
| // Record CUs order to make loc/loclist order correct. | |
| Accum.LocListCUOrder.push_back(CUOffset); | |
| uint64_t LoclistsBase = 0; | |
| uint64_t LegacyLocBase = 0; | |
| auto LocIt = LocListWritersByCU.find(CUOffset); | |
| if (LocIt != LocListWritersByCU.end()) { | |
| DebugLocWriter *LocWriter = LocIt->second.get(); | |
| auto *LocListWriter = dyn_cast<DebugLoclistWriter>(LocWriter); | |
| const uint64_t BufferSize = LocWriter->getLocBufferSize(); | |
| if (LocListWriter && LocListWriter->getDwarfVersion() >= 5 && | |
| !LocListWriter->isSplitDwarf() && BufferSize != 0) { | |
| LoclistsBase = Accum.LoclistsOffset; | |
| Accum.LoclistsOffset += BufferSize; | |
| } else if (!LocListWriter) { | |
| LegacyLocBase = Accum.LegacyLocOffset; | |
| // Each per-CU DWARF4 .debug_loc buffer begins with a 16-byte empty-list | |
| // sentinel (offset 0). That sentinel is emitted once for the whole | |
| // section in makeFinalLocListsSection, and each CU's leading 16 bytes | |
| // are stripped, so a CU contributes BufferSize-16 bytes to the global | |
| // section. | |
| if (BufferSize > 16) | |
| Accum.LegacyLocOffset += BufferSize - 16; | |
| } | |
| } | |
| if (LoclistsBase) { | |
| DIE *UnitDIE = PartDIEBlder.getUnitDIEbyUnit(*CU); | |
| if (UnitDIE) { | |
| DIEValue LocBase = UnitDIE->findAttribute(dwarf::DW_AT_loclists_base); | |
| if (LocBase.getType()) { | |
| const uint64_t OldVal = LocBase.getDIEInteger().getValue(); | |
| PartDIEBlder.replaceValue(UnitDIE, dwarf::DW_AT_loclists_base, | |
| LocBase.getForm(), | |
| DIEInteger(OldVal + LoclistsBase)); | |
| } | |
| } | |
| } | |
| if (LegacyLocBase) | |
| LocIt->second->applyBase(PartDIEBlder, LegacyLocBase); | |
| } | |
| } | |
| void DWARFRewriter::mergePerBucketRanges(DIEBuilder &PartDIEBlder, | |
| BucketLocalWriter &LocalWriters, | |
| ArrayRef<DWARFUnit *> SortedCUs) { | |
| // Range lists are stored in bucket-local buffers during parallel processing. | |
| // Fix DIE offsets from bucket-local coordinates into the current global | |
| // range section coordinates, then append the local buffers. | |
| if (RangeListsSectionWriter) { | |
| const uint64_t RangesOffset = RangeListsSectionWriter->getSectionOffset(); | |
| for (DWARFUnit *CU : SortedCUs) | |
| if (CU->getVersion() >= 5) { | |
| DIE *CUDie = PartDIEBlder.getUnitDIEbyUnit(*CU); | |
| DIEValue RngBaseVal = CUDie->findAttribute(dwarf::DW_AT_rnglists_base); | |
| if (RngBaseVal) { | |
| const uint64_t OldVal = RngBaseVal.getDIEInteger().getValue(); | |
| PartDIEBlder.replaceValue(CUDie, dwarf::DW_AT_rnglists_base, | |
| RngBaseVal.getForm(), | |
| DIEInteger(OldVal + RangesOffset)); | |
| } | |
| } | |
| } | |
| if (LocalWriters.RngListsWriter && RangeListsSectionWriter) { | |
| std::unique_ptr<DebugBufferVector> LocalBuf = | |
| LocalWriters.RngListsWriter->releaseBuffer(); | |
| RangeListsSectionWriter->appendToRangeBuffer(*LocalBuf); | |
| } | |
| if (!LocalWriters.LegacyRangesWriter || !LegacyRangesSectionWriter) | |
| return; | |
| std::unique_ptr<DebugBufferVector> LegacyBuf = | |
| LocalWriters.LegacyRangesWriter->releaseBuffer(); | |
| // Local buffers reserve offset 0 for an empty ranges terminator via | |
| // initSection(). When appending to the global buffer, skip that local | |
| // terminator since the global buffer has its own at offset 0. Adjust | |
| // the fixup offset to account for the skipped prefix. | |
| const uint64_t LocalTerminatorSize = 16; | |
| const uint64_t GlobalOffset = | |
| LegacyRangesSectionWriter->getSectionOffset() - LocalTerminatorSize; | |
| for (DWARFUnit *CU : SortedCUs) | |
| if (CU->getVersion() <= 4) { | |
| // Fix DWARF v4 DW_AT_ranges attribute. | |
| const auto &DIs = PartDIEBlder.getDIEsByUnit(*CU); | |
| for (const auto &DI : DIs) { | |
| DIE *Die = DI->Die; | |
| DIEValue RangesVal = Die->findAttribute(dwarf::DW_AT_ranges); | |
| if (RangesVal && RangesVal.getForm() == dwarf::DW_FORM_sec_offset) { | |
| const uint64_t OldVal = RangesVal.getDIEInteger().getValue(); | |
| // Empty ranges are stored at offset 0 in both local and global | |
| // buffers, so must not be shifted during merge. | |
| if (OldVal != DebugRangesSectionWriter::getEmptyRangesOffset()) | |
| PartDIEBlder.replaceValue(Die, dwarf::DW_AT_ranges, | |
| RangesVal.getForm(), | |
| DIEInteger(OldVal + GlobalOffset)); | |
| } | |
| } | |
| } | |
| DebugBufferVector TrimmedBuf(LegacyBuf->begin() + LocalTerminatorSize, | |
| LegacyBuf->end()); | |
| LegacyRangesSectionWriter->appendToRangeBuffer(TrimmedBuf); | |
| } | |
| void DWARFRewriter::createRangeLocListAndAddressWriters() { | |
| for (auto &DWCU : BC.DwCtx->compile_units()) { | |
| DWARFUnit &CU = *DWCU; | |
| if (LocListWritersByCU.count(CU.getOffset())) | |
| continue; | |
| const uint16_t DwarfVersion = CU.getVersion(); | |
| if (DwarfVersion >= 5) { | |
| auto AddrW = std::make_unique<DebugAddrWriterDwarf5>( | |
| &BC, CU.getAddressByteSize(), CU.getAddrOffsetSectionBase(), | |
| CU.getFormParams().Format); | |
| RangeListsSectionWriter->setAddressWriter(AddrW.get()); | |
| LocListWritersByCU[CU.getOffset()] = | |
| std::make_unique<DebugLoclistWriter>(CU, DwarfVersion, false, *AddrW); | |
| if (std::optional<uint64_t> DWOId = CU.getDWOId()) { | |
| assert(RangeListsWritersByCU.count(*DWOId) == 0 && | |
| "RangeLists writer for DWO unit already exists."); | |
| auto DWORangeListsSectionWriter = | |
| std::make_unique<DebugRangeListsSectionWriter>(); | |
| DWORangeListsSectionWriter->initSection(CU); | |
| DWORangeListsSectionWriter->setAddressWriter(AddrW.get()); | |
| RangeListsWritersByCU[*DWOId] = std::move(DWORangeListsSectionWriter); | |
| } | |
| AddressWritersByCU[CU.getOffset()] = std::move(AddrW); | |
| } else { | |
| auto AddrW = | |
| std::make_unique<DebugAddrWriter>(&BC, CU.getAddressByteSize()); | |
| AddressWritersByCU[CU.getOffset()] = std::move(AddrW); | |
| LocListWritersByCU[CU.getOffset()] = std::make_unique<DebugLocWriter>(); | |
| if (std::optional<uint64_t> DWOId = CU.getDWOId()) { | |
| assert(LegacyRangesWritersByCU.count(*DWOId) == 0 && | |
| "LegacyRangeLists writer for DWO unit already exists."); | |
| auto LegacyRangesSectionWriterByCU = | |
| std::make_unique<DebugRangesSectionWriter>(); | |
| LegacyRangesSectionWriterByCU->initSection(CU); | |
| LegacyRangesWritersByCU[*DWOId] = | |
| std::move(LegacyRangesSectionWriterByCU); | |
| } | |
| } | |
| } | |
| } | |
| void DWARFRewriter::processMainBinaryCU(DWARFUnit &Unit, DIEBuilder &DIEBlder, | |
| BucketLocalWriter &LocalWriter) { | |
| std::optional<DWARFUnit *> SplitCU; | |
| std::optional<uint64_t> RangesBase; | |
| std::optional<uint64_t> DWOId = Unit.getDWOId(); | |
| uint8_t DWARFVersion = Unit.getVersion(); | |
| if (DWOId) | |
| SplitCU = BC.getDWOCU(*DWOId); | |
| auto LocIt = LocListWritersByCU.find(Unit.getOffset()); | |
| assert(LocIt != LocListWritersByCU.end() && | |
| "LocListWriter does not exist for CU"); | |
| DebugLocWriter &DebugLocWriter = *LocIt->second.get(); | |
| auto AddrIt = AddressWritersByCU.find(Unit.getOffset()); | |
| assert(AddrIt != AddressWritersByCU.end() && | |
| "AddressWriter does not exist for CU"); | |
| DebugAddrWriter &AddressWriter = *AddrIt->second.get(); | |
| if (DWARFVersion >= 5) { | |
| if (!LocalWriter.RngListsWriter) | |
| LocalWriter.RngListsWriter = | |
| std::make_unique<DebugRangeListsSectionWriter>(); | |
| } else { | |
| if (!LocalWriter.LegacyRangesWriter) { | |
| LocalWriter.LegacyRangesWriter = | |
| std::make_unique<DebugRangesSectionWriter>(); | |
| LocalWriter.LegacyRangesWriter->initSection(); | |
| } | |
| } | |
| DebugRangesSectionWriter &RangesSectionWriter = | |
| DWARFVersion >= 5 ? *LocalWriter.RngListsWriter | |
| : *LocalWriter.LegacyRangesWriter; | |
| if (DWARFVersion >= 5) { | |
| LocalWriter.RngListsWriter->setAddressWriter(&AddressWriter); | |
| RangesBase = RangesSectionWriter.getSectionOffset() + | |
| getDWARF5RngListLocListHeaderSize(Unit.getFormParams().Format); | |
| RangesSectionWriter.initSection(Unit); | |
| } else if (SplitCU) { | |
| // DWARF4: only split-dwarf CUs need a ranges base | |
| RangesBase = LocalWriter.LegacyRangesWriter->getSectionOffset(); | |
| } | |
| updateUnitDebugInfo(Unit, DIEBlder, DebugLocWriter, RangesSectionWriter, | |
| AddressWriter, RangesBase); | |
| DebugLocWriter.finalize(DIEBlder, *DIEBlder.getUnitDIEbyUnit(Unit)); | |
| if (DWARFVersion >= 5) | |
| RangesSectionWriter.finalizeSection(); | |
| } | |
| void DWARFRewriter::processBucket( | |
| size_t Idx, SmallVector<SmallVector<DWARFUnit *>> &PartVec, | |
| std::vector<std::unique_ptr<DIEBuilder>> &BucketDIEBlders, | |
| std::vector<BucketLocalWriter> &LocalWriters, | |
| DWARF5AcceleratorTable &DebugNamesTable, | |
| std::unordered_map<uint64_t, std::string> &DWOToNameMap, | |
| GDBIndex &GDBIndexSection) { | |
| SmallVector<DWARFUnit *> &Vec = PartVec[Idx]; | |
| std::unique_ptr<DIEBuilder> &BucketDIEBlder = BucketDIEBlders[Idx]; | |
| BucketDIEBlder = | |
| std::make_unique<DIEBuilder>(BC, BC.DwCtx.get(), DebugNamesTable); | |
| DIEBuilder &PartDIEBlder = *BucketDIEBlder; | |
| PartDIEBlder.buildCompileUnits(Vec); | |
| auto ProcessSplitCU = [&](DWARFUnit &Unit, DWARFUnit &SplitCU, | |
| DebugRangesSectionWriter &TempRangesSectionWriter, | |
| DebugAddrWriter &AddressWriter, | |
| const std::string &DWOName, | |
| DIEBuilder &DWODIEBuilder) { | |
| DWODIEBuilder.buildDWOUnit(SplitCU); | |
| DebugStrOffsetsWriter DWOStrOffstsWriter(BC); | |
| DebugStrWriter DWOStrWriter(SplitCU.getContext(), true); | |
| DWODIEBuilder.updateDWONameCompDirForTypes(DWOStrOffstsWriter, DWOStrWriter, | |
| SplitCU, opts::DwarfOutputPath, | |
| DWOName); | |
| DebugLoclistWriter DebugLocDWoWriter(Unit, Unit.getVersion(), true, | |
| AddressWriter); | |
| updateUnitDebugInfo(SplitCU, DWODIEBuilder, DebugLocDWoWriter, | |
| TempRangesSectionWriter, AddressWriter); | |
| DebugLocDWoWriter.finalize(DWODIEBuilder, | |
| *DWODIEBuilder.getUnitDIEbyUnit(SplitCU)); | |
| if (Unit.getVersion() >= 5) | |
| TempRangesSectionWriter.finalizeSection(); | |
| emitDWOBuilder(DWOName, DWODIEBuilder, *this, SplitCU, Unit, | |
| DebugLocDWoWriter, DWOStrOffstsWriter, DWOStrWriter, | |
| GDBIndexSection, TempRangesSectionWriter); | |
| { | |
| std::lock_guard<std::mutex> Lock(DebugNamesUpdateMutex); | |
| DWODIEBuilder.updateDebugNamesTable(); | |
| } | |
| }; | |
| for (DWARFUnit *CU : PartDIEBlder.getProcessedCUs()) { | |
| std::optional<DWARFUnit *> SplitCU; | |
| std::optional<uint64_t> DWOId = CU->getDWOId(); | |
| if (DWOId) | |
| SplitCU = BC.getDWOCU(*DWOId); | |
| if (SplitCU) { | |
| DebugAddrWriter &AddressWriter = *AddressWritersByCU.at(CU->getOffset()); | |
| DebugRangesSectionWriter &TempRangesSectionWriter = | |
| CU->getVersion() >= 5 ? *RangeListsWritersByCU.at(*DWOId) | |
| : *LegacyRangesWritersByCU.at(*DWOId); | |
| auto NameIt = DWOToNameMap.find(*DWOId); | |
| if (NameIt == DWOToNameMap.end()) { | |
| BC.errs() << "BOLT-WARNING: [internal-dwarf-warning]: Could not find " | |
| "DWO name for DWO ID " | |
| << Twine::utohexstr(*DWOId) << ".\n"; | |
| continue; | |
| } | |
| auto DWOName = NameIt->second; | |
| auto DWODIEBuilderPtr = std::make_unique<DIEBuilder>( | |
| BC, &(**SplitCU).getContext(), DebugNamesTable, CU); | |
| DIEBuilder &DWODIEBuilder = *DWODIEBuilderPtr; | |
| ProcessSplitCU(*CU, **SplitCU, TempRangesSectionWriter, AddressWriter, | |
| DWOName, DWODIEBuilder); | |
| } | |
| processMainBinaryCU(*CU, PartDIEBlder, LocalWriters[Idx]); | |
| } | |
| } | |
| void DWARFRewriter::updateDebugInfo() { | |
| ErrorOr<BinarySection &> DebugInfo = BC.getUniqueSectionByName(".debug_info"); | |
| if (!DebugInfo) | |
| return; | |
| ARangesSectionWriter = std::make_unique<DebugARangesSectionWriter>(); | |
| StrWriter = std::make_unique<DebugStrWriter>(*BC.DwCtx, false); | |
| StrOffstsWriter = std::make_unique<DebugStrOffsetsWriter>(BC); | |
| /// Stores and serializes information that will be put into the | |
| /// .debug_addr DWARF section. | |
| std::unique_ptr<DebugAddrWriter> FinalAddrWriter; | |
| if (BC.isDWARF5Used()) { | |
| FinalAddrWriter = std::make_unique<DebugAddrWriterDwarf5>(&BC); | |
| RangeListsSectionWriter = std::make_unique<DebugRangeListsSectionWriter>(); | |
| } else { | |
| FinalAddrWriter = std::make_unique<DebugAddrWriter>(&BC); | |
| } | |
| if (BC.isDWARFLegacyUsed()) { | |
| LegacyRangesSectionWriter = std::make_unique<DebugRangesSectionWriter>(); | |
| LegacyRangesSectionWriter->initSection(); | |
| } | |
| uint32_t CUSize = std::distance(BC.DwCtx->compile_units().begin(), | |
| BC.DwCtx->compile_units().end()); | |
| // Pre-create per-CU writers in a single thread. | |
| // Worker threads should only read from these maps. | |
| createRangeLocListAndAddressWriters(); | |
| // If the user requested an output directory for rewritten .dwo files, make | |
| // sure it exists before any worker attempt to create the files. | |
| if (!opts::DwarfOutputPath.empty() && !sys::fs::exists(opts::DwarfOutputPath)) | |
| (void)sys::fs::create_directories(opts::DwarfOutputPath); | |
| std::unordered_map<uint64_t, std::string> DWOToNameMap = | |
| getDWONameMap(*BC.DwCtx, CUSize); | |
| DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC, | |
| *StrWriter); | |
| if (DebugNamesTable.isCreated()) | |
| DebugNamesTable.preAllocateUnits(*BC.DwCtx); | |
| GDBIndex GDBIndexSection(BC); | |
| DIEBuilder DIEBlder(BC, BC.DwCtx.get(), DebugNamesTable); | |
| DIEBlder.buildTypeUnits(StrOffstsWriter.get()); | |
| SmallVector<char, 20> OutBuffer; | |
| std::unique_ptr<raw_svector_ostream> ObjOS = | |
| std::make_unique<raw_svector_ostream>(OutBuffer); | |
| const object::ObjectFile *File = BC.DwCtx->getDWARFObj().getFile(); | |
| auto TheTriple = std::make_unique<Triple>(File->makeTriple()); | |
| std::unique_ptr<DIEStreamer> Streamer = createDIEStreamer( | |
| *TheTriple, *ObjOS, "TypeStreamer", DIEBlder, GDBIndexSection); | |
| CUOffsetMap OffsetMap = | |
| finalizeTypeSections(DIEBlder, *Streamer, GDBIndexSection); | |
| SmallVector<SmallVector<DWARFUnit *>> PartVec = | |
| partitionCUs(*BC.DwCtx, CUSize); | |
| // Buckets below run in parallel, and with a .dwp package they all read the | |
| // same split DWARF context. Create that context now to avoid a race | |
| // (note this is a DWP-only issue, DWOs are safe since each thread operate | |
| // on its own DWARF context). | |
| BC.openSharedDWOContext(); | |
| const unsigned int ThreadCount = | |
| std::min(opts::DebugThreadCount, opts::ThreadCount); | |
| llvm::ThreadPoolInterface &ThreadPool = | |
| getOrCreateDebugInfoThreadPool(ThreadCount); | |
| std::vector<BucketLocalWriter> LocalWriters(PartVec.size()); | |
| std::vector<std::unique_ptr<DIEBuilder>> BucketDIEBlders(PartVec.size()); | |
| std::mutex MergeQueueMutex; | |
| std::condition_variable MergeQueueCV; | |
| std::queue<size_t> MergeQueue; | |
| const size_t TotalTasks = PartVec.size(); | |
| std::vector<char> BucketDone(TotalTasks, 0); | |
| BucketLocAccumOffset Accum; | |
| auto MergeBucket = [&](size_t Idx) -> void { | |
| std::unique_ptr<DIEBuilder> &BucketDIEBlder = BucketDIEBlders[Idx]; | |
| assert(BucketDIEBlder && "Bucket DIEBuilder is null."); | |
| const auto &Processed = BucketDIEBlder->getProcessedCUs(); | |
| std::vector<DWARFUnit *> SortedCUs; | |
| SortedCUs.assign(Processed.begin(), Processed.end()); | |
| // Ensure deterministic output by sorting CUs in section offset order. | |
| llvm::sort(SortedCUs, [](const DWARFUnit *A, const DWARFUnit *B) { | |
| return A->getOffset() < B->getOffset(); | |
| }); | |
| finalizeMainCUStrOffsets(*BucketDIEBlder, SortedCUs, DWOToNameMap); | |
| mergePerBucketLocs(*BucketDIEBlder, SortedCUs, Accum); | |
| mergePerBucketRanges(*BucketDIEBlder, LocalWriters[Idx], SortedCUs); | |
| finalizeCompileUnits(*BucketDIEBlder, DIEBlder, *Streamer, OffsetMap, | |
| BucketDIEBlder->getProcessedCUs(), *FinalAddrWriter); | |
| // Release memory for this bucket. | |
| BucketDIEBlders[Idx].reset(); | |
| LocalWriters[Idx].RngListsWriter.reset(); | |
| LocalWriters[Idx].LegacyRangesWriter.reset(); | |
| for (DWARFUnit *CU : SortedCUs) | |
| if (std::optional<uint64_t> DWOId = CU->getDWOId()) | |
| BC.releaseDWOCU(*DWOId); | |
| }; | |
| for (size_t I = 0; I < TotalTasks; ++I) { | |
| ThreadPool.async([&, I] { | |
| processBucket(I, PartVec, BucketDIEBlders, LocalWriters, DebugNamesTable, | |
| DWOToNameMap, GDBIndexSection); | |
| { | |
| std::lock_guard<std::mutex> Lock(MergeQueueMutex); | |
| BucketDone[I] = 1; | |
| if (!opts::PreserveOrder) | |
| MergeQueue.push(I); | |
| } | |
| // Only the merge thread waits on the CV and it waits on a specific | |
| // index. notify_one is sufficient and avoids waking all workers. | |
| MergeQueueCV.notify_one(); | |
| }); | |
| } | |
| // Merge completed buckets in-order or as they finish, depending on | |
| // PreserveOrder. | |
| for (size_t Idx = 0; Idx < TotalTasks; ++Idx) { | |
| size_t ProcessIndex; | |
| { | |
| std::unique_lock<std::mutex> Lock(MergeQueueMutex); | |
| if (opts::PreserveOrder) { | |
| MergeQueueCV.wait(Lock, [&] { return BucketDone[Idx] != 0; }); | |
| ProcessIndex = Idx; | |
| } else { | |
| MergeQueueCV.wait(Lock, [&] { return !MergeQueue.empty(); }); | |
| ProcessIndex = MergeQueue.front(); | |
| MergeQueue.pop(); | |
| } | |
| } | |
| MergeBucket(ProcessIndex); | |
| } | |
| ThreadPool.wait(); | |
| DebugNamesTable.emitAccelTable(); | |
| finalizeDebugSections(DIEBlder, DebugNamesTable, *Streamer, *ObjOS, OffsetMap, | |
| *FinalAddrWriter, Accum.LocListCUOrder); | |
| GDBIndexSection.updateGdbIndexSection(OffsetMap, CUSize, | |
| *ARangesSectionWriter); | |
| } | |
| void DWARFRewriter::updateUnitDebugInfo( | |
| DWARFUnit &Unit, DIEBuilder &DIEBldr, DebugLocWriter &DebugLocWriter, | |
| DebugRangesSectionWriter &RangesSectionWriter, | |
| DebugAddrWriter &AddressWriter, std::optional<uint64_t> RangesBase) { | |
| // Cache debug ranges so that the offset for identical ranges could be reused. | |
| std::map<DebugAddressRangesVector, uint64_t> CachedRanges; | |
| uint64_t DIEOffset = Unit.getOffset() + Unit.getHeaderSize(); | |
| uint64_t NextCUOffset = Unit.getNextUnitOffset(); | |
| const std::vector<std::unique_ptr<DIEBuilder::DIEInfo>> &DIs = | |
| DIEBldr.getDIEsByUnit(Unit); | |
| // Either updates or normalizes DW_AT_range to DW_AT_low_pc and DW_AT_high_pc. | |
| auto updateLowPCHighPC = [&](DIE *Die, const DIEValue &LowPCVal, | |
| const DIEValue &HighPCVal, uint64_t LowPC, | |
| const uint64_t HighPC) { | |
| dwarf::Attribute AttrLowPC = dwarf::DW_AT_low_pc; | |
| dwarf::Form FormLowPC = dwarf::DW_FORM_addr; | |
| dwarf::Attribute AttrHighPC = dwarf::DW_AT_high_pc; | |
| // DW_AT_high_pc holding an offset from DW_AT_low_pc is a DWARF 4 addition; | |
| // before that the attribute is always address. | |
| dwarf::Form FormHighPC = | |
| Unit.getVersion() >= 4 ? dwarf::DW_FORM_data4 : dwarf::DW_FORM_addr; | |
| // Computed before LowPC is possibly replaced by an address table index. | |
| const uint64_t Size = HighPC - LowPC; | |
| // Whatever was generated is not low_pc/high_pc, so will reset to | |
| // default for size 1. | |
| if (!LowPCVal || !HighPCVal) { | |
| if (Unit.getVersion() >= 5) | |
| FormLowPC = dwarf::DW_FORM_addrx; | |
| else if (Unit.isDWOUnit()) | |
| FormLowPC = dwarf::DW_FORM_GNU_addr_index; | |
| } else { | |
| AttrLowPC = LowPCVal.getAttribute(); | |
| FormLowPC = LowPCVal.getForm(); | |
| AttrHighPC = HighPCVal.getAttribute(); | |
| FormHighPC = HighPCVal.getForm(); | |
| } | |
| // A fixed-width indexed form caps the index it can encode, and the index | |
| // assigned below comes from a rebuilt .debug_addr that may hold more | |
| // entries than the input's. Normalize to the ULEB-encoded form so the | |
| // value always fits. DW_FORM_addrx is DWARF 5, so the GNU extension used | |
| // by earlier split DWARF keeps its own form. | |
| auto normalizeIndexedForm = [](dwarf::Form Form) { | |
| return Form == dwarf::DW_FORM_GNU_addr_index ? Form | |
| : dwarf::DW_FORM_addrx; | |
| }; | |
| if (isIndexedAddressForm(FormLowPC)) { | |
| LowPC = AddressWriter.getIndexFromAddress(LowPC, Unit); | |
| FormLowPC = normalizeIndexedForm(FormLowPC); | |
| } | |
| // The value has to match the class of the form it is written under. | |
| uint64_t HighPCEncoded = Size; | |
| if (isIndexedAddressForm(FormHighPC)) { | |
| HighPCEncoded = AddressWriter.getIndexFromAddress(HighPC, Unit); | |
| FormHighPC = normalizeIndexedForm(FormHighPC); | |
| } else if (isAddressForm(FormHighPC)) | |
| HighPCEncoded = HighPC; | |
| if (LowPCVal) | |
| DIEBldr.replaceValue(Die, AttrLowPC, FormLowPC, DIEInteger(LowPC)); | |
| else | |
| DIEBldr.addValue(Die, AttrLowPC, FormLowPC, DIEInteger(LowPC)); | |
| if (HighPCVal) { | |
| DIEBldr.replaceValue(Die, AttrHighPC, FormHighPC, | |
| DIEInteger(HighPCEncoded)); | |
| } else { | |
| DIEBldr.deleteValue(Die, dwarf::DW_AT_ranges); | |
| DIEBldr.addValue(Die, AttrHighPC, FormHighPC, DIEInteger(HighPCEncoded)); | |
| } | |
| }; | |
| for (const std::unique_ptr<DIEBuilder::DIEInfo> &DI : DIs) { | |
| DIE *Die = DI->Die; | |
| switch (Die->getTag()) { | |
| case dwarf::DW_TAG_compile_unit: | |
| case dwarf::DW_TAG_skeleton_unit: { | |
| // For dwarf5 section 3.1.3 | |
| // The following attributes are not part of a split full compilation unit | |
| // entry but instead are inherited (if present) from the corresponding | |
| // skeleton compilation unit: DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges, | |
| // DW_AT_stmt_list, DW_AT_comp_dir, DW_AT_str_offsets_base, | |
| // DW_AT_addr_base and DW_AT_rnglists_base. | |
| if (Unit.getVersion() == 5 && Unit.isDWOUnit()) | |
| continue; | |
| auto ModuleRangesOrError = getDIEAddressRanges(*Die, Unit); | |
| if (!ModuleRangesOrError) { | |
| consumeError(ModuleRangesOrError.takeError()); | |
| break; | |
| } | |
| DWARFAddressRangesVector &ModuleRanges = *ModuleRangesOrError; | |
| DebugAddressRangesVector OutputRanges = | |
| BC.translateModuleAddressRanges(ModuleRanges); | |
| DIEValue LowPCAttrInfo = Die->findAttribute(dwarf::DW_AT_low_pc); | |
| // For a case where LLD GCs only function used in the CU. | |
| // If CU doesn't have DW_AT_low_pc we are not going to convert, | |
| // so don't need to do anything. | |
| if (OutputRanges.empty() && !Unit.isDWOUnit() && LowPCAttrInfo) | |
| OutputRanges.push_back({0, 0}); | |
| const uint64_t RangesSectionOffset = | |
| RangesSectionWriter.addRanges(OutputRanges); | |
| // Don't emit the zero low_pc arange. | |
| if (!Unit.isDWOUnit() && !OutputRanges.empty() && | |
| OutputRanges.back().LowPC) | |
| ARangesSectionWriter->addCURanges(Unit.getOffset(), | |
| std::move(OutputRanges)); | |
| updateDWARFObjectAddressRanges(Unit, DIEBldr, *Die, RangesSectionOffset, | |
| AddressWriter, RangesBase); | |
| DIEValue StmtListAttrVal = Die->findAttribute(dwarf::DW_AT_stmt_list); | |
| if (LineTablePatchMap.count(&Unit)) | |
| DIEBldr.replaceValue(Die, dwarf::DW_AT_stmt_list, | |
| StmtListAttrVal.getForm(), | |
| DIEInteger(LineTablePatchMap[&Unit])); | |
| break; | |
| } | |
| case dwarf::DW_TAG_subprogram: { | |
| // Get function address either from ranges or [LowPC, HighPC) pair. | |
| uint64_t Address = UINT64_MAX; | |
| uint64_t SectionIndex, HighPC; | |
| DebugAddressRangesVector FunctionRanges; | |
| if (!getLowAndHighPC(*Die, Unit, Address, HighPC, SectionIndex)) { | |
| Expected<DWARFAddressRangesVector> RangesOrError = | |
| getDIEAddressRanges(*Die, Unit); | |
| if (!RangesOrError) { | |
| consumeError(RangesOrError.takeError()); | |
| break; | |
| } | |
| DWARFAddressRangesVector Ranges = *RangesOrError; | |
| // Not a function definition. | |
| if (Ranges.empty()) | |
| break; | |
| for (const DWARFAddressRange &Range : Ranges) { | |
| if (const BinaryFunction *Function = | |
| BC.getBinaryFunctionAtAddress(Range.LowPC)) | |
| FunctionRanges.append(Function->getOutputAddressRanges()); | |
| } | |
| } else { | |
| if (const BinaryFunction *Function = | |
| BC.getBinaryFunctionAtAddress(Address)) | |
| FunctionRanges = Function->getOutputAddressRanges(); | |
| } | |
| // Clear cached ranges as the new function will have its own set. | |
| CachedRanges.clear(); | |
| DIEValue LowPCVal = Die->findAttribute(dwarf::DW_AT_low_pc); | |
| DIEValue HighPCVal = Die->findAttribute(dwarf::DW_AT_high_pc); | |
| if (FunctionRanges.empty()) { | |
| // There is no output range for this DIE, so point it at address 0 | |
| // and keep its original size. The stored DW_AT_high_pc is that size | |
| // only for non-address forms; for an address form it is the end | |
| // address, and for an indexed one it is an address table index that | |
| // cannot be resolved to a size here. | |
| uint64_t OriginalSize = 1; | |
| if (LowPCVal && HighPCVal) { | |
| const dwarf::Form HighForm = HighPCVal.getForm(); | |
| if (!isAddressForm(HighForm)) | |
| OriginalSize = HighPCVal.getDIEInteger().getValue(); | |
| else if (HighForm == dwarf::DW_FORM_addr && | |
| LowPCVal.getForm() == dwarf::DW_FORM_addr) | |
| OriginalSize = HighPCVal.getDIEInteger().getValue() - | |
| LowPCVal.getDIEInteger().getValue(); | |
| } | |
| FunctionRanges.push_back({0, OriginalSize}); | |
| } | |
| if (FunctionRanges.size() == 1 && !opts::AlwaysConvertToRanges) { | |
| updateLowPCHighPC(Die, LowPCVal, HighPCVal, FunctionRanges.back().LowPC, | |
| FunctionRanges.back().HighPC); | |
| break; | |
| } | |
| updateDWARFObjectAddressRanges( | |
| Unit, DIEBldr, *Die, RangesSectionWriter.addRanges(FunctionRanges), | |
| AddressWriter); | |
| break; | |
| } | |
| case dwarf::DW_TAG_lexical_block: | |
| case dwarf::DW_TAG_inlined_subroutine: | |
| case dwarf::DW_TAG_try_block: | |
| case dwarf::DW_TAG_catch_block: { | |
| uint64_t RangesSectionOffset = 0; | |
| Expected<DWARFAddressRangesVector> RangesOrError = | |
| getDIEAddressRanges(*Die, Unit); | |
| const BinaryFunction *Function = | |
| RangesOrError && !RangesOrError->empty() | |
| ? BC.getBinaryFunctionContainingAddress( | |
| RangesOrError->front().LowPC) | |
| : nullptr; | |
| DebugAddressRangesVector OutputRanges; | |
| if (Function) { | |
| OutputRanges = translateInputToOutputRanges(*Function, *RangesOrError); | |
| LLVM_DEBUG(if (OutputRanges.empty() != RangesOrError->empty()) { | |
| dbgs() << "BOLT-DEBUG: problem with DIE at 0x" | |
| << Twine::utohexstr(Die->getOffset()) << " in CU at 0x" | |
| << Twine::utohexstr(Unit.getOffset()) << '\n'; | |
| }); | |
| if (opts::AlwaysConvertToRanges || OutputRanges.size() > 1) { | |
| RangesSectionOffset = RangesSectionWriter.addRanges( | |
| std::move(OutputRanges), CachedRanges); | |
| OutputRanges.clear(); | |
| } else if (OutputRanges.empty()) { | |
| OutputRanges.push_back({0, RangesOrError.get().front().HighPC}); | |
| } | |
| } else if (!RangesOrError) { | |
| consumeError(RangesOrError.takeError()); | |
| } else { | |
| OutputRanges.push_back({0, !RangesOrError->empty() | |
| ? RangesOrError.get().front().HighPC | |
| : 0}); | |
| } | |
| DIEValue LowPCVal = Die->findAttribute(dwarf::DW_AT_low_pc); | |
| DIEValue HighPCVal = Die->findAttribute(dwarf::DW_AT_high_pc); | |
| if (OutputRanges.size() == 1) { | |
| updateLowPCHighPC(Die, LowPCVal, HighPCVal, OutputRanges.back().LowPC, | |
| OutputRanges.back().HighPC); | |
| break; | |
| } | |
| updateDWARFObjectAddressRanges(Unit, DIEBldr, *Die, RangesSectionOffset, | |
| AddressWriter); | |
| break; | |
| } | |
| case dwarf::DW_TAG_call_site: { | |
| auto patchPC = [&](DIE *Die, DIEValue &AttrVal, StringRef Entry) -> void { | |
| std::optional<uint64_t> Address = getAsAddress(Unit, AttrVal); | |
| const BinaryFunction *Function = | |
| BC.getBinaryFunctionContainingAddress(*Address); | |
| uint64_t UpdatedAddress = *Address; | |
| if (Function) | |
| UpdatedAddress = | |
| Function->translateInputToOutputAddress(UpdatedAddress); | |
| if (isIndexedAddressForm(AttrVal.getForm())) { | |
| const uint32_t Index = | |
| AddressWriter.getIndexFromAddress(UpdatedAddress, Unit); | |
| DIEBldr.replaceValue(Die, AttrVal.getAttribute(), AttrVal.getForm(), | |
| DIEInteger(Index)); | |
| } else if (AttrVal.getForm() == dwarf::DW_FORM_addr) { | |
| DIEBldr.replaceValue(Die, AttrVal.getAttribute(), AttrVal.getForm(), | |
| DIEInteger(UpdatedAddress)); | |
| } else { | |
| errs() << "BOLT-ERROR: unsupported form for " << Entry << "\n"; | |
| } | |
| }; | |
| DIEValue CallPcAttrVal = Die->findAttribute(dwarf::DW_AT_call_pc); | |
| if (CallPcAttrVal) | |
| patchPC(Die, CallPcAttrVal, "DW_AT_call_pc"); | |
| DIEValue CallRetPcAttrVal = | |
| Die->findAttribute(dwarf::DW_AT_call_return_pc); | |
| if (CallRetPcAttrVal) | |
| patchPC(Die, CallRetPcAttrVal, "DW_AT_call_return_pc"); | |
| break; | |
| } | |
| default: { | |
| // Handle any tag that can have DW_AT_location attribute. | |
| DIEValue LocAttrInfo = Die->findAttribute(dwarf::DW_AT_location); | |
| DIEValue LowPCAttrInfo = Die->findAttribute(dwarf::DW_AT_low_pc); | |
| if (LocAttrInfo) { | |
| if (doesFormBelongToClass(LocAttrInfo.getForm(), | |
| DWARFFormValue::FC_Constant, | |
| Unit.getVersion()) || | |
| doesFormBelongToClass(LocAttrInfo.getForm(), | |
| DWARFFormValue::FC_SectionOffset, | |
| Unit.getVersion())) { | |
| uint64_t Offset = LocAttrInfo.getForm() == dwarf::DW_FORM_loclistx | |
| ? LocAttrInfo.getDIELocList().getValue() | |
| : LocAttrInfo.getDIEInteger().getValue(); | |
| DebugLocationsVector InputLL; | |
| std::optional<object::SectionedAddress> SectionAddress = | |
| Unit.getBaseAddress(); | |
| uint64_t BaseAddress = 0; | |
| if (SectionAddress) | |
| BaseAddress = SectionAddress->Address; | |
| if (Unit.getVersion() >= 5 && | |
| LocAttrInfo.getForm() == dwarf::DW_FORM_loclistx) { | |
| std::optional<uint64_t> LocOffset = Unit.getLoclistOffset(Offset); | |
| assert(LocOffset && "Location Offset is invalid."); | |
| Offset = *LocOffset; | |
| } | |
| Error E = Unit.getLocationTable().visitLocationList( | |
| &Offset, [&](const DWARFLocationEntry &Entry) { | |
| switch (Entry.Kind) { | |
| default: | |
| llvm_unreachable("Unsupported DWARFLocationEntry Kind."); | |
| case dwarf::DW_LLE_end_of_list: | |
| return false; | |
| case dwarf::DW_LLE_base_address: { | |
| assert(Entry.SectionIndex == SectionedAddress::UndefSection && | |
| "absolute address expected"); | |
| BaseAddress = Entry.Value0; | |
| break; | |
| } | |
| case dwarf::DW_LLE_offset_pair: | |
| assert( | |
| (Entry.SectionIndex == SectionedAddress::UndefSection && | |
| (!Unit.isDWOUnit() || Unit.getVersion() == 5)) && | |
| "absolute address expected"); | |
| InputLL.emplace_back(DebugLocationEntry{ | |
| BaseAddress + Entry.Value0, BaseAddress + Entry.Value1, | |
| Entry.Loc}); | |
| break; | |
| case dwarf::DW_LLE_start_length: | |
| InputLL.emplace_back(DebugLocationEntry{ | |
| Entry.Value0, Entry.Value0 + Entry.Value1, Entry.Loc}); | |
| break; | |
| case dwarf::DW_LLE_base_addressx: { | |
| std::optional<object::SectionedAddress> EntryAddress = | |
| Unit.getAddrOffsetSectionItem(Entry.Value0); | |
| assert(EntryAddress && "base Address not found."); | |
| BaseAddress = EntryAddress->Address; | |
| break; | |
| } | |
| case dwarf::DW_LLE_startx_length: { | |
| std::optional<object::SectionedAddress> EntryAddress = | |
| Unit.getAddrOffsetSectionItem(Entry.Value0); | |
| assert(EntryAddress && "Address does not exist."); | |
| InputLL.emplace_back(DebugLocationEntry{ | |
| EntryAddress->Address, | |
| EntryAddress->Address + Entry.Value1, Entry.Loc}); | |
| break; | |
| } | |
| case dwarf::DW_LLE_startx_endx: { | |
| std::optional<object::SectionedAddress> StartAddress = | |
| Unit.getAddrOffsetSectionItem(Entry.Value0); | |
| assert(StartAddress && "Start Address does not exist."); | |
| std::optional<object::SectionedAddress> EndAddress = | |
| Unit.getAddrOffsetSectionItem(Entry.Value1); | |
| assert(EndAddress && "Start Address does not exist."); | |
| InputLL.emplace_back(DebugLocationEntry{ | |
| StartAddress->Address, EndAddress->Address, Entry.Loc}); | |
| break; | |
| } | |
| } | |
| return true; | |
| }); | |
| if (E || InputLL.empty()) { | |
| consumeError(std::move(E)); | |
| errs() << "BOLT-WARNING: empty location list detected at 0x" | |
| << Twine::utohexstr(Offset) << " for DIE at 0x" << Die | |
| << " in CU at 0x" << Twine::utohexstr(Unit.getOffset()) | |
| << '\n'; | |
| } else { | |
| const uint64_t Address = InputLL.front().LowPC; | |
| DebugLocationsVector OutputLL; | |
| if (const BinaryFunction *Function = | |
| BC.getBinaryFunctionContainingAddress(Address)) { | |
| OutputLL = translateInputToOutputLocationList(*Function, InputLL); | |
| LLVM_DEBUG(if (OutputLL.empty()) { | |
| dbgs() << "BOLT-DEBUG: location list translated to an empty " | |
| "one at 0x" | |
| << Die << " in CU at 0x" | |
| << Twine::utohexstr(Unit.getOffset()) << '\n'; | |
| }); | |
| } else { | |
| // It's possible for a subprogram to be removed and to have | |
| // address of 0. Adding this entry to output to preserve debug | |
| // information. | |
| OutputLL = InputLL; | |
| } | |
| DebugLocWriter.addList(DIEBldr, *Die, LocAttrInfo, OutputLL); | |
| } | |
| } else { | |
| assert((doesFormBelongToClass(LocAttrInfo.getForm(), | |
| DWARFFormValue::FC_Exprloc, | |
| Unit.getVersion()) || | |
| doesFormBelongToClass(LocAttrInfo.getForm(), | |
| DWARFFormValue::FC_Block, | |
| Unit.getVersion())) && | |
| "unexpected DW_AT_location form"); | |
| if (Unit.isDWOUnit() || Unit.getVersion() >= 5) { | |
| std::vector<uint8_t> Sblock; | |
| DIEValueList *AttrLocValList; | |
| if (doesFormBelongToClass(LocAttrInfo.getForm(), | |
| DWARFFormValue::FC_Exprloc, | |
| Unit.getVersion())) { | |
| for (const DIEValue &Val : LocAttrInfo.getDIELoc().values()) { | |
| Sblock.push_back(Val.getDIEInteger().getValue()); | |
| } | |
| DIELoc *LocAttr = const_cast<DIELoc *>(&LocAttrInfo.getDIELoc()); | |
| AttrLocValList = static_cast<DIEValueList *>(LocAttr); | |
| } else { | |
| for (const DIEValue &Val : LocAttrInfo.getDIEBlock().values()) { | |
| Sblock.push_back(Val.getDIEInteger().getValue()); | |
| } | |
| DIEBlock *BlockAttr = | |
| const_cast<DIEBlock *>(&LocAttrInfo.getDIEBlock()); | |
| AttrLocValList = static_cast<DIEValueList *>(BlockAttr); | |
| } | |
| DataExtractor Data(Sblock, Unit.getContext().isLittleEndian()); | |
| DWARFExpression LocExpr(Data, Unit.getAddressByteSize(), | |
| Unit.getFormParams().Format); | |
| uint32_t PrevOffset = 0; | |
| DIEValueList *NewAttr; | |
| DIEValue Value; | |
| uint32_t NewExprSize = 0; | |
| DIELoc *Loc = nullptr; | |
| DIEBlock *Block = nullptr; | |
| if (LocAttrInfo.getForm() == dwarf::DW_FORM_exprloc) { | |
| Loc = DIEBldr.allocateDIEValue<DIELoc>(); | |
| NewAttr = Loc; | |
| Value = DIEValue(LocAttrInfo.getAttribute(), | |
| LocAttrInfo.getForm(), Loc); | |
| } else if (doesFormBelongToClass(LocAttrInfo.getForm(), | |
| DWARFFormValue::FC_Block, | |
| Unit.getVersion())) { | |
| Block = DIEBldr.allocateDIEValue<DIEBlock>(); | |
| NewAttr = Block; | |
| Value = DIEValue(LocAttrInfo.getAttribute(), | |
| LocAttrInfo.getForm(), Block); | |
| } else { | |
| errs() << "BOLT-WARNING: Unexpected Form value in Updating " | |
| "DW_AT_Location\n"; | |
| continue; | |
| } | |
| for (const DWARFExpression::Operation &Expr : LocExpr) { | |
| uint32_t CurEndOffset = PrevOffset + 1; | |
| if (Expr.getDescription().Op.size() == 1) | |
| CurEndOffset = Expr.getOperandEndOffset(0); | |
| if (Expr.getDescription().Op.size() == 2) | |
| CurEndOffset = Expr.getOperandEndOffset(1); | |
| if (Expr.getDescription().Op.size() > 2) | |
| errs() << "BOLT-WARNING: [internal-dwarf-error]: Unsupported " | |
| "number of operands.\n"; | |
| // not addr index, just copy. | |
| if (!(Expr.getCode() == dwarf::DW_OP_GNU_addr_index || | |
| Expr.getCode() == dwarf::DW_OP_addrx)) { | |
| auto Itr = AttrLocValList->values().begin(); | |
| std::advance(Itr, PrevOffset); | |
| uint32_t CopyNum = CurEndOffset - PrevOffset; | |
| NewExprSize += CopyNum; | |
| while (CopyNum--) { | |
| DIEBldr.addValue(NewAttr, *Itr); | |
| std::advance(Itr, 1); | |
| } | |
| } else { | |
| const uint64_t Index = Expr.getRawOperand(0); | |
| std::optional<object::SectionedAddress> EntryAddress = | |
| Unit.getAddrOffsetSectionItem(Index); | |
| assert(EntryAddress && "Address is not found."); | |
| assert(Index <= std::numeric_limits<uint32_t>::max() && | |
| "Invalid Operand Index."); | |
| const uint32_t AddrIndex = AddressWriter.getIndexFromAddress( | |
| EntryAddress->Address, Unit); | |
| // update Index into .debug_address section for DW_AT_location. | |
| // The Size field is not stored in IR, we need to minus 1 in | |
| // offset for each expr. | |
| SmallString<8> Tmp; | |
| raw_svector_ostream OSE(Tmp); | |
| encodeULEB128(AddrIndex, OSE); | |
| DIEBldr.addValue(NewAttr, static_cast<dwarf::Attribute>(0), | |
| dwarf::DW_FORM_data1, | |
| DIEInteger(Expr.getCode())); | |
| NewExprSize += 1; | |
| for (uint8_t Byte : Tmp) { | |
| DIEBldr.addValue(NewAttr, static_cast<dwarf::Attribute>(0), | |
| dwarf::DW_FORM_data1, DIEInteger(Byte)); | |
| NewExprSize += 1; | |
| } | |
| } | |
| PrevOffset = CurEndOffset; | |
| } | |
| // update the size since the index might be changed | |
| if (Loc) | |
| Loc->setSize(NewExprSize); | |
| else | |
| Block->setSize(NewExprSize); | |
| DIEBldr.replaceValue(Die, LocAttrInfo.getAttribute(), | |
| LocAttrInfo.getForm(), Value); | |
| } | |
| } | |
| } else if (LowPCAttrInfo) { | |
| uint64_t Address = 0; | |
| uint64_t SectionIndex = 0; | |
| if (getLowPC(*Die, Unit, Address, SectionIndex)) { | |
| uint64_t NewAddress = 0; | |
| if (const BinaryFunction *Function = | |
| BC.getBinaryFunctionContainingAddress(Address)) { | |
| NewAddress = Function->translateInputToOutputAddress(Address); | |
| LLVM_DEBUG(dbgs() | |
| << "BOLT-DEBUG: Fixing low_pc 0x" | |
| << Twine::utohexstr(Address) << " for DIE with tag " | |
| << Die->getTag() << " to 0x" | |
| << Twine::utohexstr(NewAddress) << '\n'); | |
| } | |
| dwarf::Form Form = LowPCAttrInfo.getForm(); | |
| assert(Form != dwarf::DW_FORM_LLVM_addrx_offset && | |
| "DW_FORM_LLVM_addrx_offset is not supported"); | |
| if (isIndexedAddressForm(Form)) { | |
| const uint32_t Index = AddressWriter.getIndexFromAddress( | |
| NewAddress ? NewAddress : Address, Unit); | |
| DIEBldr.replaceValue(Die, LowPCAttrInfo.getAttribute(), | |
| LowPCAttrInfo.getForm(), DIEInteger(Index)); | |
| } else { | |
| DIEBldr.replaceValue(Die, LowPCAttrInfo.getAttribute(), | |
| LowPCAttrInfo.getForm(), | |
| DIEInteger(NewAddress)); | |
| } | |
| } else if (opts::Verbosity >= 1) { | |
| errs() << "BOLT-WARNING: unexpected form value for attribute " | |
| "LowPCAttrInfo\n"; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (DIEOffset > NextCUOffset) | |
| errs() << "BOLT-WARNING: corrupt DWARF detected at 0x" | |
| << Twine::utohexstr(Unit.getOffset()) << '\n'; | |
| } | |
| void DWARFRewriter::updateDWARFObjectAddressRanges( | |
| DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die, uint64_t DebugRangesOffset, | |
| DebugAddrWriter &AddressWriter, std::optional<uint64_t> RangesBase) { | |
| if (RangesBase) { | |
| // If DW_AT_GNU_ranges_base is present, update it. No further modifications | |
| // are needed for ranges base. | |
| DIEValue RangesBaseInfo = Die.findAttribute(dwarf::DW_AT_GNU_ranges_base); | |
| if (!RangesBaseInfo) { | |
| RangesBaseInfo = Die.findAttribute(dwarf::DW_AT_rnglists_base); | |
| } | |
| if (RangesBaseInfo) { | |
| if (RangesBaseInfo.getAttribute() == dwarf::DW_AT_GNU_ranges_base) { | |
| auto RangesWriterIterator = | |
| LegacyRangesWritersByCU.find(*Unit.getDWOId()); | |
| assert(RangesWriterIterator != LegacyRangesWritersByCU.end() && | |
| "RangesWriter does not exist for DWOId"); | |
| RangesWriterIterator->second->setDie(&Die); | |
| } else { | |
| DIEBldr.replaceValue(&Die, RangesBaseInfo.getAttribute(), | |
| RangesBaseInfo.getForm(), DIEInteger(*RangesBase)); | |
| } | |
| RangesBase = std::nullopt; | |
| } | |
| } | |
| DIEValue LowPCAttrInfo = Die.findAttribute(dwarf::DW_AT_low_pc); | |
| DIEValue RangesAttrInfo = Die.findAttribute(dwarf::DW_AT_ranges); | |
| if (RangesAttrInfo) { | |
| // Case 1: The object was already non-contiguous and had DW_AT_ranges. | |
| // In this case we simply need to update the value of DW_AT_ranges | |
| // and introduce DW_AT_GNU_ranges_base if required. | |
| // For DWARF5 converting all of DW_AT_ranges into DW_FORM_rnglistx | |
| bool NeedConverted = false; | |
| if (Unit.getVersion() >= 5 && | |
| RangesAttrInfo.getForm() == dwarf::DW_FORM_sec_offset) | |
| NeedConverted = true; | |
| if (NeedConverted || RangesAttrInfo.getForm() == dwarf::DW_FORM_rnglistx) | |
| DIEBldr.replaceValue(&Die, dwarf::DW_AT_ranges, dwarf::DW_FORM_rnglistx, | |
| DIEInteger(DebugRangesOffset)); | |
| else | |
| DIEBldr.replaceValue(&Die, dwarf::DW_AT_ranges, RangesAttrInfo.getForm(), | |
| DIEInteger(DebugRangesOffset)); | |
| if (!RangesBase) { | |
| if (LowPCAttrInfo && !isIndexedAddressForm(LowPCAttrInfo.getForm())) | |
| DIEBldr.replaceValue(&Die, dwarf::DW_AT_low_pc, LowPCAttrInfo.getForm(), | |
| DIEInteger(0)); | |
| return; | |
| } | |
| if (!(Die.getTag() == dwarf::DW_TAG_compile_unit || | |
| Die.getTag() == dwarf::DW_TAG_skeleton_unit)) | |
| return; | |
| // If we are at this point we are in the CU/Skeleton CU, and | |
| // DW_AT_GNU_ranges_base or DW_AT_rnglists_base doesn't exist. | |
| if (Unit.getVersion() <= 4) { | |
| DIEBldr.addValue(&Die, dwarf::DW_AT_GNU_ranges_base, dwarf::DW_FORM_data4, | |
| DIEInteger(INT_MAX)); | |
| auto RangesWriterIterator = | |
| LegacyRangesWritersByCU.find(*Unit.getDWOId()); | |
| assert(RangesWriterIterator != LegacyRangesWritersByCU.end() && | |
| "RangesWriter does not exist for DWOId"); | |
| RangesWriterIterator->second->setDie(&Die); | |
| } else if (Unit.getVersion() >= 5) { | |
| DIEBldr.addValue(&Die, dwarf::DW_AT_rnglists_base, | |
| dwarf::DW_FORM_sec_offset, DIEInteger(*RangesBase)); | |
| } | |
| return; | |
| } | |
| // Case 2: The object has both DW_AT_low_pc and DW_AT_high_pc emitted back | |
| // to back. Replace with new attributes and patch the DIE. | |
| DIEValue HighPCAttrInfo = Die.findAttribute(dwarf::DW_AT_high_pc); | |
| if (LowPCAttrInfo && HighPCAttrInfo) { | |
| convertToRangesPatchDebugInfo(Unit, DIEBldr, Die, DebugRangesOffset, | |
| LowPCAttrInfo, HighPCAttrInfo, AddressWriter, | |
| RangesBase); | |
| } else if (!(Unit.isDWOUnit() && | |
| Die.getTag() == dwarf::DW_TAG_compile_unit)) { | |
| if (opts::Verbosity >= 1) | |
| errs() << "BOLT-WARNING: cannot update ranges for DIE in Unit offset 0x" | |
| << Twine::utohexstr(Unit.getOffset()) << '\n'; | |
| } | |
| } | |
| void DWARFRewriter::updateLineTableOffsets(const MCAssembler &Asm) { | |
| ErrorOr<BinarySection &> DbgInfoSection = | |
| BC.getUniqueSectionByName(".debug_info"); | |
| ErrorOr<BinarySection &> TypeInfoSection = | |
| BC.getUniqueSectionByName(".debug_types"); | |
| assert(((BC.DwCtx->getNumTypeUnits() > 0 && TypeInfoSection) || | |
| BC.DwCtx->getNumTypeUnits() == 0) && | |
| "Was not able to retrieve Debug Types section."); | |
| // There is no direct connection between CU and TU, but same offsets, | |
| // encoded in DW_AT_stmt_list, into .debug_line get modified. | |
| // We take advantage of that to map original CU line table offsets to new | |
| // ones. | |
| std::unordered_map<uint64_t, uint64_t> DebugLineOffsetMap; | |
| auto GetStatementListValue = | |
| [](const DWARFDie &DIE) -> std::optional<uint64_t> { | |
| std::optional<DWARFFormValue> StmtList = DIE.find(dwarf::DW_AT_stmt_list); | |
| if (!StmtList) | |
| return std::nullopt; | |
| std::optional<uint64_t> Offset = dwarf::toSectionOffset(StmtList); | |
| assert(Offset && "Was not able to retrieve value of DW_AT_stmt_list."); | |
| return *Offset; | |
| }; | |
| SmallVector<DWARFUnit *, 1> TUs; | |
| for (const std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->info_section_units()) { | |
| if (CU->isTypeUnit()) { | |
| TUs.push_back(CU.get()); | |
| continue; | |
| } | |
| const unsigned CUID = CU->getOffset(); | |
| MCSymbol *Label = BC.getDwarfLineTable(CUID).getLabel(); | |
| if (!Label) | |
| continue; | |
| std::optional<uint64_t> StmtOffset = | |
| GetStatementListValue(CU->getUnitDIE()); | |
| if (!StmtOffset) | |
| continue; | |
| const uint64_t LineTableOffset = Asm.getSymbolOffset(*Label); | |
| DebugLineOffsetMap[*StmtOffset] = LineTableOffset; | |
| assert(DbgInfoSection && ".debug_info section must exist"); | |
| LineTablePatchMap[CU.get()] = LineTableOffset; | |
| } | |
| for (const std::unique_ptr<DWARFUnit> &TU : BC.DwCtx->types_section_units()) | |
| TUs.push_back(TU.get()); | |
| for (DWARFUnit *TU : TUs) { | |
| std::optional<uint64_t> StmtOffset = | |
| GetStatementListValue(TU->getUnitDIE()); | |
| if (!StmtOffset) | |
| continue; | |
| auto Iter = DebugLineOffsetMap.find(*StmtOffset); | |
| if (Iter == DebugLineOffsetMap.end()) { | |
| // Implementation depends on TU sharing DW_AT_stmt_list with a CU. | |
| // Only case that it hasn't been true was for manually modified assembly | |
| // file. Adding this warning in case assumption is false. | |
| errs() | |
| << "BOLT-WARNING: [internal-dwarf-error]: A TU at offset: 0x" | |
| << Twine::utohexstr(TU->getOffset()) | |
| << " is not sharing " | |
| ".debug_line entry with CU. DW_AT_stmt_list for this TU won't be " | |
| "updated.\n"; | |
| continue; | |
| } | |
| TypeUnitRelocMap[TU] = Iter->second; | |
| } | |
| // Set .debug_info as finalized so it won't be skipped over when | |
| // we process sections while writing out the new binary. This ensures | |
| // that the pending relocations will be processed and not ignored. | |
| if (DbgInfoSection) | |
| DbgInfoSection->setIsFinalized(); | |
| if (TypeInfoSection) | |
| TypeInfoSection->setIsFinalized(); | |
| } | |
| CUOffsetMap DWARFRewriter::finalizeTypeSections(DIEBuilder &DIEBlder, | |
| DIEStreamer &Streamer, | |
| GDBIndex &GDBIndexSection) { | |
| // update TypeUnit DW_AT_stmt_list with new .debug_line information. | |
| auto updateLineTable = [&](const DWARFUnit &Unit) -> void { | |
| DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(Unit); | |
| DIEValue StmtAttrInfo = UnitDIE->findAttribute(dwarf::DW_AT_stmt_list); | |
| if (!StmtAttrInfo || !TypeUnitRelocMap.count(&Unit)) | |
| return; | |
| DIEBlder.replaceValue(UnitDIE, dwarf::DW_AT_stmt_list, | |
| StmtAttrInfo.getForm(), | |
| DIEInteger(TypeUnitRelocMap[&Unit])); | |
| }; | |
| // generate and populate abbrevs here | |
| DIEBlder.generateAbbrevs(); | |
| DIEBlder.finish(); | |
| DIEBlder.updateDebugNamesTable(); | |
| SmallVector<char, 20> OutBuffer; | |
| std::shared_ptr<raw_svector_ostream> ObjOS = | |
| std::make_shared<raw_svector_ostream>(OutBuffer); | |
| const object::ObjectFile *File = BC.DwCtx->getDWARFObj().getFile(); | |
| auto TheTriple = std::make_unique<Triple>(File->makeTriple()); | |
| std::unique_ptr<DIEStreamer> TypeStreamer = createDIEStreamer( | |
| *TheTriple, *ObjOS, "TypeStreamer", DIEBlder, GDBIndexSection); | |
| // generate debug_info and CUMap | |
| CUOffsetMap CUMap; | |
| for (std::unique_ptr<llvm::DWARFUnit> &CU : BC.DwCtx->info_section_units()) { | |
| if (!CU->isTypeUnit()) | |
| continue; | |
| updateLineTable(*CU); | |
| emitUnit(DIEBlder, Streamer, *CU); | |
| uint64_t StartOffset = CUOffset; | |
| DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(*CU); | |
| CUOffset += CU->getHeaderSize(); | |
| CUOffset += UnitDIE->getSize(); | |
| const dwarf::DwarfFormat Format = CU->getFormParams().Format; | |
| CUMap[CU->getOffset()] = {StartOffset, | |
| CUOffset - StartOffset - | |
| dwarf::getUnitLengthFieldByteSize(Format), | |
| Format}; | |
| } | |
| // Emit Type Unit of DWARF 4 to .debug_type section | |
| for (DWARFUnit *TU : DIEBlder.getDWARF4TUVector()) { | |
| updateLineTable(*TU); | |
| emitUnit(DIEBlder, *TypeStreamer, *TU); | |
| } | |
| TypeStreamer->finish(); | |
| std::unique_ptr<MemoryBuffer> ObjectMemBuffer = | |
| MemoryBuffer::getMemBuffer(ObjOS->str(), "in-memory object file", false); | |
| std::unique_ptr<object::ObjectFile> Obj = cantFail( | |
| object::ObjectFile::createObjectFile(ObjectMemBuffer->getMemBufferRef()), | |
| "error creating in-memory object"); | |
| for (const SectionRef &Section : Obj->sections()) { | |
| StringRef Contents = cantFail(Section.getContents()); | |
| StringRef Name = cantFail(Section.getName()); | |
| if (Name == ".debug_types") | |
| BC.registerOrUpdateNoteSection(".debug_types", copyByteArray(Contents), | |
| Contents.size()); | |
| } | |
| return CUMap; | |
| } | |
| void DWARFRewriter::finalizeDebugSections( | |
| DIEBuilder &DIEBlder, DWARF5AcceleratorTable &DebugNamesTable, | |
| DIEStreamer &Streamer, raw_svector_ostream &ObjOS, CUOffsetMap &CUMap, | |
| DebugAddrWriter &FinalAddrWriter, std::vector<uint64_t> SortedCU) { | |
| if (StrWriter->isInitialized()) { | |
| RewriteInstance::addToDebugSectionsToOverwrite(".debug_str"); | |
| std::unique_ptr<DebugStrBufferVector> DebugStrSectionContents = | |
| StrWriter->releaseBuffer(); | |
| BC.registerOrUpdateNoteSection(".debug_str", | |
| copyByteArray(*DebugStrSectionContents), | |
| DebugStrSectionContents->size()); | |
| } | |
| if (StrOffstsWriter->isFinalized()) { | |
| RewriteInstance::addToDebugSectionsToOverwrite(".debug_str_offsets"); | |
| std::unique_ptr<DebugStrOffsetsBufferVector> | |
| DebugStrOffsetsSectionContents = StrOffstsWriter->releaseBuffer(); | |
| BC.registerOrUpdateNoteSection( | |
| ".debug_str_offsets", copyByteArray(*DebugStrOffsetsSectionContents), | |
| DebugStrOffsetsSectionContents->size()); | |
| } | |
| if (BC.isDWARFLegacyUsed()) { | |
| std::unique_ptr<DebugBufferVector> RangesSectionContents = | |
| LegacyRangesSectionWriter->releaseBuffer(); | |
| BC.registerOrUpdateNoteSection(".debug_ranges", | |
| copyByteArray(*RangesSectionContents), | |
| RangesSectionContents->size()); | |
| } | |
| if (BC.isDWARF5Used()) { | |
| std::unique_ptr<DebugBufferVector> RangesSectionContents = | |
| RangeListsSectionWriter->releaseBuffer(); | |
| BC.registerOrUpdateNoteSection(".debug_rnglists", | |
| copyByteArray(*RangesSectionContents), | |
| RangesSectionContents->size()); | |
| } | |
| if (BC.isDWARF5Used()) { | |
| std::unique_ptr<DebugBufferVector> LocationListSectionContents = | |
| makeFinalLocListsSection(DWARFVersion::DWARF5, SortedCU); | |
| if (!LocationListSectionContents->empty()) | |
| BC.registerOrUpdateNoteSection( | |
| ".debug_loclists", copyByteArray(*LocationListSectionContents), | |
| LocationListSectionContents->size()); | |
| } | |
| if (BC.isDWARFLegacyUsed()) { | |
| std::unique_ptr<DebugBufferVector> LocationListSectionContents = | |
| makeFinalLocListsSection(DWARFVersion::DWARFLegacy, SortedCU); | |
| if (!LocationListSectionContents->empty()) | |
| BC.registerOrUpdateNoteSection( | |
| ".debug_loc", copyByteArray(*LocationListSectionContents), | |
| LocationListSectionContents->size()); | |
| } | |
| if (FinalAddrWriter.isInitialized()) { | |
| std::unique_ptr<AddressSectionBuffer> AddressSectionContents = | |
| FinalAddrWriter.releaseBuffer(); | |
| BC.registerOrUpdateNoteSection(".debug_addr", | |
| copyByteArray(*AddressSectionContents), | |
| AddressSectionContents->size()); | |
| } | |
| Streamer.emitAbbrevs(DIEBlder.getAbbrevs(), BC.DwCtx->getMaxVersion()); | |
| Streamer.finish(); | |
| std::unique_ptr<MemoryBuffer> ObjectMemBuffer = | |
| MemoryBuffer::getMemBuffer(ObjOS.str(), "in-memory object file", false); | |
| std::unique_ptr<object::ObjectFile> Obj = cantFail( | |
| object::ObjectFile::createObjectFile(ObjectMemBuffer->getMemBufferRef()), | |
| "error creating in-memory object"); | |
| for (const SectionRef &Secs : Obj->sections()) { | |
| StringRef Contents = cantFail(Secs.getContents()); | |
| StringRef Name = cantFail(Secs.getName()); | |
| if (Name == ".debug_abbrev") { | |
| BC.registerOrUpdateNoteSection(".debug_abbrev", copyByteArray(Contents), | |
| Contents.size()); | |
| } else if (Name == ".debug_info") { | |
| BC.registerOrUpdateNoteSection(".debug_info", copyByteArray(Contents), | |
| Contents.size()); | |
| } | |
| } | |
| // Skip .debug_aranges if we are re-generating .gdb_index. | |
| if (opts::KeepARanges || !BC.getGdbIndexSection()) { | |
| SmallVector<char, 16> ARangesBuffer; | |
| raw_svector_ostream OS(ARangesBuffer); | |
| auto MAB = std::unique_ptr<MCAsmBackend>( | |
| BC.TheTarget->createMCAsmBackend(*BC.STI, *BC.MRI, MCTargetOptions())); | |
| ARangesSectionWriter->writeARangesSection(OS, CUMap); | |
| const StringRef &ARangesContents = OS.str(); | |
| BC.registerOrUpdateNoteSection(".debug_aranges", | |
| copyByteArray(ARangesContents), | |
| ARangesContents.size()); | |
| } | |
| if (DebugNamesTable.isCreated()) { | |
| RewriteInstance::addToDebugSectionsToOverwrite(".debug_names"); | |
| std::unique_ptr<DebugBufferVector> DebugNamesSectionContents = | |
| DebugNamesTable.releaseBuffer(); | |
| BC.registerOrUpdateNoteSection(".debug_names", | |
| copyByteArray(*DebugNamesSectionContents), | |
| DebugNamesSectionContents->size()); | |
| } | |
| } | |
| void DWARFRewriter::finalizeCompileUnits(DIEBuilder &DIEBlder, | |
| DIEBuilder &MainDIEBuilder, | |
| DIEStreamer &Streamer, | |
| CUOffsetMap &CUMap, | |
| const std::list<DWARFUnit *> &CUs, | |
| DebugAddrWriter &FinalAddrWriter) { | |
| for (DWARFUnit *CU : CUs) { | |
| auto AddressWriterIterator = AddressWritersByCU.find(CU->getOffset()); | |
| assert(AddressWriterIterator != AddressWritersByCU.end() && | |
| "AddressWriter does not exist for CU"); | |
| DebugAddrWriter *AddressWriter = AddressWriterIterator->second.get(); | |
| const size_t BufferOffset = FinalAddrWriter.getBufferSize(); | |
| std::optional<uint64_t> Offset = AddressWriter->finalize(BufferOffset); | |
| /// If Offset already exists in UnmodifiedAddressOffsets, then update with | |
| /// Offset, else update with BufferOffset. | |
| if (Offset) | |
| AddressWriter->updateAddrBase(DIEBlder, *CU, *Offset); | |
| else if (AddressWriter->isInitialized()) | |
| AddressWriter->updateAddrBase(DIEBlder, *CU, BufferOffset); | |
| if (AddressWriter->isInitialized()) { | |
| std::unique_ptr<AddressSectionBuffer> AddressSectionContents = | |
| AddressWriter->releaseBuffer(); | |
| FinalAddrWriter.appendToAddressBuffer(*AddressSectionContents); | |
| } | |
| if (CU->getVersion() != 4) | |
| continue; | |
| std::optional<uint64_t> DWOId = CU->getDWOId(); | |
| if (!DWOId) | |
| continue; | |
| auto RangesWriterIterator = LegacyRangesWritersByCU.find(*DWOId); | |
| assert(RangesWriterIterator != LegacyRangesWritersByCU.end() && | |
| "RangesWriter does not exist for DWOId"); | |
| std::unique_ptr<DebugRangesSectionWriter> &LegacyRangesWriter = | |
| RangesWriterIterator->second; | |
| DIE *Die = LegacyRangesWriter->getDie(); | |
| if (!Die) | |
| continue; | |
| DIEValue DvalGNUBase = Die->findAttribute(dwarf::DW_AT_GNU_ranges_base); | |
| assert(DvalGNUBase && "GNU_ranges_base attribute does not exist for DWOId"); | |
| DIEBlder.replaceValue( | |
| Die, dwarf::DW_AT_GNU_ranges_base, DvalGNUBase.getForm(), | |
| DIEInteger(LegacyRangesSectionWriter->getSectionOffset())); | |
| std::unique_ptr<DebugBufferVector> RangesWritersContents = | |
| LegacyRangesWriter->releaseBuffer(); | |
| LegacyRangesSectionWriter->appendToRangeBuffer(*RangesWritersContents); | |
| } | |
| // Assign abbreviation numbers from the main DIEBuilder so all bucket DIEs | |
| // share a single global .debug_abbrev table. | |
| for (DWARFUnit *DU : CUs) { | |
| DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(*DU); | |
| MainDIEBuilder.generateUnitAbbrevs(UnitDIE); | |
| } | |
| DIEBlder.setUnitOffsetBases(CUOffset); | |
| DIEBlder.finish(); | |
| { | |
| std::lock_guard<std::mutex> Lock(DebugNamesUpdateMutex); | |
| DIEBlder.updateDebugNamesTable(); | |
| } | |
| // generate debug_info and CUMap | |
| for (DWARFUnit *CU : CUs) { | |
| emitUnit(DIEBlder, Streamer, *CU); | |
| const uint64_t StartOffset = CUOffset; | |
| DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(*CU); | |
| CUOffset += CU->getHeaderSize(); | |
| CUOffset += UnitDIE->getSize(); | |
| const dwarf::DwarfFormat Format = CU->getFormParams().Format; | |
| CUMap[CU->getOffset()] = {StartOffset, | |
| CUOffset - StartOffset - | |
| dwarf::getUnitLengthFieldByteSize(Format), | |
| Format}; | |
| } | |
| } | |
| // Creates all the data structures necessary for creating MCStreamer. | |
| // They are passed by reference because they need to be kept around. | |
| // Also creates known debug sections. These are sections handled by | |
| // handleDebugDataPatching. | |
| namespace { | |
| std::unique_ptr<BinaryContext> | |
| createDwarfOnlyBC(const object::ObjectFile &File) { | |
| return cantFail(BinaryContext::createBinaryContext( | |
| File.makeTriple(), std::make_shared<orc::SymbolStringPool>(), | |
| File.getFileName(), nullptr, false, | |
| DWARFContext::create(File, DWARFContext::ProcessDebugRelocations::Ignore, | |
| nullptr, "", WithColor::defaultErrorHandler, | |
| WithColor::defaultWarningHandler), | |
| {llvm::outs(), llvm::errs()})); | |
| } | |
| StringMap<DWARFRewriter::KnownSectionsEntry> | |
| createKnownSectionsMap(const MCObjectFileInfo &MCOFI) { | |
| StringMap<DWARFRewriter::KnownSectionsEntry> KnownSectionsTemp = { | |
| {"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}}, | |
| {"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_EXT_TYPES}}, | |
| {"debug_str_offsets.dwo", | |
| {MCOFI.getDwarfStrOffDWOSection(), DW_SECT_STR_OFFSETS}}, | |
| {"debug_str.dwo", {MCOFI.getDwarfStrDWOSection(), DW_SECT_EXT_unknown}}, | |
| {"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_EXT_LOC}}, | |
| {"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}}, | |
| {"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}}, | |
| {"debug_loclists.dwo", | |
| {MCOFI.getDwarfLoclistsDWOSection(), DW_SECT_LOCLISTS}}, | |
| {"debug_rnglists.dwo", | |
| {MCOFI.getDwarfRnglistsDWOSection(), DW_SECT_RNGLISTS}}}; | |
| return KnownSectionsTemp; | |
| } | |
| StringRef getSectionName(const SectionRef &Section) { | |
| Expected<StringRef> SectionName = Section.getName(); | |
| assert(SectionName && "Invalid section name."); | |
| StringRef Name = *SectionName; | |
| Name = Name.substr(Name.find_first_not_of("._")); | |
| return Name; | |
| } | |
| /// Extracts the slice of the .debug_str.dwo section for a given CU from a DWP | |
| /// file, based on the .debug_str_offsets.dwo section. This helps address DWO | |
| /// bloat that may occur after updates. | |
| /// | |
| /// A slice of .debug_str.dwo may be composed of several non-contiguous | |
| /// fragments. These non-contiguous string views will be written out | |
| /// sequentially, avoiding the copying overhead caused by assembling them. | |
| /// | |
| /// The .debug_str_offsets for the first CU often does not need to be updated, | |
| /// so copying is only performed when .debug_str_offsets requires updating. | |
| static void UpdateStrAndStrOffsets(StringRef StrDWOContent, | |
| StringRef StrOffsetsContent, | |
| SmallVectorImpl<StringRef> &StrDWOOutData, | |
| std::string &StrOffsetsOutData, | |
| const DWARFUnit &CU, bool IsLittleEndian) { | |
| const llvm::endianness Endian = | |
| IsLittleEndian ? llvm::endianness::little : llvm::endianness::big; | |
| const dwarf::DwarfFormat Format = CU.getFormParams().Format; | |
| uint64_t HeaderOffset = 0; | |
| if (CU.getVersion() >= 5) { | |
| HeaderOffset = dwarf::getUnitLengthFieldByteSize(Format) + 4; | |
| } | |
| const size_t SizeOfOffset = dwarf::getDwarfOffsetByteSize(Format); | |
| const uint64_t NumOffsets = | |
| (StrOffsetsContent.size() - HeaderOffset) / SizeOfOffset; | |
| DataExtractor Extractor(StrOffsetsContent, IsLittleEndian); | |
| uint64_t ExtractionOffset = HeaderOffset; | |
| using StringFragment = DWARFUnitIndex::Entry::SectionContribution; | |
| const auto getStringLength = [](StringRef Content, | |
| uint64_t Offset) -> uint64_t { | |
| size_t NullPos = Content.find('\0', Offset); | |
| return (NullPos != StringRef::npos) ? (NullPos - Offset + 1) : 0; | |
| }; | |
| const auto isContiguous = [](const StringFragment &Fragment, | |
| uint64_t NextOffset) -> bool { | |
| return NextOffset == Fragment.getOffset() + Fragment.getLength(); | |
| }; | |
| std::optional<StringFragment> CurrentFragment; | |
| uint64_t AccumulatedStrLen = 0; | |
| for (uint64_t I = 0; I < NumOffsets; ++I) { | |
| const uint64_t StrOffset = | |
| Extractor.getUnsigned(&ExtractionOffset, SizeOfOffset); | |
| const uint64_t StringLength = getStringLength(StrDWOContent, StrOffset); | |
| if (!CurrentFragment) { | |
| // First init. | |
| CurrentFragment = StringFragment(StrOffset, StringLength); | |
| } else { | |
| if (isContiguous(*CurrentFragment, StrOffset)) { | |
| // Expanding the current fragment. | |
| CurrentFragment->setLength(CurrentFragment->getLength() + StringLength); | |
| } else { | |
| // Saving the current fragment and start a new one. | |
| StrDWOOutData.push_back(StrDWOContent.substr( | |
| CurrentFragment->getOffset(), CurrentFragment->getLength())); | |
| CurrentFragment = StringFragment(StrOffset, StringLength); | |
| } | |
| } | |
| if (AccumulatedStrLen != StrOffset) { | |
| // Updating str offsets. | |
| if (StrOffsetsOutData.empty()) | |
| StrOffsetsOutData = StrOffsetsContent.str(); | |
| if (Format == dwarf::DwarfFormat::DWARF64) | |
| llvm::support::endian::write64( | |
| &StrOffsetsOutData[HeaderOffset + I * SizeOfOffset], | |
| AccumulatedStrLen, Endian); | |
| else | |
| llvm::support::endian::write32( | |
| &StrOffsetsOutData[HeaderOffset + I * SizeOfOffset], | |
| static_cast<uint32_t>(AccumulatedStrLen), Endian); | |
| } | |
| AccumulatedStrLen += StringLength; | |
| } | |
| if (CurrentFragment) | |
| StrDWOOutData.push_back(StrDWOContent.substr(CurrentFragment->getOffset(), | |
| CurrentFragment->getLength())); | |
| } | |
| // Exctracts an appropriate slice if input is DWP. | |
| // Applies patches or overwrites the section. | |
| std::optional<StringRef> updateDebugData( | |
| DWARFContext &DWCtx, StringRef SectionName, StringRef SectionContents, | |
| const StringMap<DWARFRewriter::KnownSectionsEntry> &KnownSections, | |
| MCStreamer &Streamer, DWARFRewriter &Writer, | |
| const DWARFUnitIndex::Entry *CUDWOEntry, uint64_t DWOId, | |
| std::unique_ptr<DebugBufferVector> &OutputBuffer, | |
| DebugRangeListsSectionWriter *RangeListsWriter, DebugLocWriter &LocWriter, | |
| DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter, | |
| const llvm::bolt::DWARFRewriter::OverriddenSectionsMap &OverridenSections) { | |
| using DWOSectionContribution = | |
| const DWARFUnitIndex::Entry::SectionContribution; | |
| auto getSliceData = [&](const DWARFUnitIndex::Entry *DWOEntry, | |
| StringRef OutData, DWARFSectionKind Sec, | |
| uint64_t &DWPOffset) -> StringRef { | |
| if (DWOEntry) { | |
| DWOSectionContribution *DWOContrubution = DWOEntry->getContribution(Sec); | |
| if (!DWOContrubution) | |
| return OutData; | |
| DWPOffset = DWOContrubution->getOffset(); | |
| OutData = OutData.substr(DWPOffset, DWOContrubution->getLength()); | |
| } | |
| return OutData; | |
| }; | |
| auto SectionIter = KnownSections.find(SectionName); | |
| if (SectionIter == KnownSections.end()) | |
| return std::nullopt; | |
| Streamer.switchSection(SectionIter->second.first); | |
| uint64_t DWPOffset = 0; | |
| auto getOverridenSection = | |
| [&](DWARFSectionKind Kind) -> std::optional<StringRef> { | |
| auto Iter = OverridenSections.find(Kind); | |
| if (Iter == OverridenSections.end()) { | |
| errs() | |
| << "BOLT-WARNING: [internal-dwarf-error]: Could not find overridden " | |
| "section for: " | |
| << Twine::utohexstr(DWOId) << ".\n"; | |
| return std::nullopt; | |
| } | |
| return Iter->second; | |
| }; | |
| switch (SectionIter->second.second) { | |
| default: { | |
| if (SectionName != "debug_str.dwo") | |
| errs() << "BOLT-WARNING: unsupported debug section: " << SectionName | |
| << "\n"; | |
| if (StrWriter.isInitialized()) { | |
| if (CUDWOEntry) | |
| return StrWriter.getBufferStr(); | |
| OutputBuffer = StrWriter.releaseBuffer(); | |
| return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()), | |
| OutputBuffer->size()); | |
| } | |
| return SectionContents; | |
| } | |
| case DWARFSectionKind::DW_SECT_INFO: { | |
| return getOverridenSection(DWARFSectionKind::DW_SECT_INFO); | |
| } | |
| case DWARFSectionKind::DW_SECT_EXT_TYPES: { | |
| return getOverridenSection(DWARFSectionKind::DW_SECT_EXT_TYPES); | |
| } | |
| case DWARFSectionKind::DW_SECT_STR_OFFSETS: { | |
| if (StrOffstsWriter.isFinalized()) { | |
| if (CUDWOEntry) | |
| return StrOffstsWriter.getBufferStr(); | |
| OutputBuffer = StrOffstsWriter.releaseBuffer(); | |
| return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()), | |
| OutputBuffer->size()); | |
| } | |
| return getSliceData(CUDWOEntry, SectionContents, | |
| DWARFSectionKind::DW_SECT_STR_OFFSETS, DWPOffset); | |
| } | |
| case DWARFSectionKind::DW_SECT_ABBREV: { | |
| return getOverridenSection(DWARFSectionKind::DW_SECT_ABBREV); | |
| } | |
| case DWARFSectionKind::DW_SECT_EXT_LOC: | |
| case DWARFSectionKind::DW_SECT_LOCLISTS: { | |
| OutputBuffer = LocWriter.getBuffer(); | |
| // Creating explicit StringRef here, otherwise | |
| // with implicit conversion it will take null byte as end of | |
| // string. | |
| return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()), | |
| OutputBuffer->size()); | |
| } | |
| case DWARFSectionKind::DW_SECT_LINE: { | |
| return getSliceData(CUDWOEntry, SectionContents, | |
| DWARFSectionKind::DW_SECT_LINE, DWPOffset); | |
| } | |
| case DWARFSectionKind::DW_SECT_RNGLISTS: { | |
| assert(RangeListsWriter && "RangeListsWriter was not created."); | |
| OutputBuffer = RangeListsWriter->releaseBuffer(); | |
| return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()), | |
| OutputBuffer->size()); | |
| } | |
| } | |
| } | |
| } // namespace | |
| void DWARFRewriter::writeDWOFiles( | |
| DWARFUnit &CU, const OverriddenSectionsMap &OverridenSections, | |
| const std::string &DWOName, DebugLocWriter &LocWriter, | |
| DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter, | |
| DebugRangesSectionWriter &TempRangesSectionWriter) { | |
| const uint64_t DWOId = *CU.getDWOId(); | |
| // Skipping CUs that we failed to load. | |
| std::optional<DWARFUnit *> DWOCU = BC.getDWOCU(DWOId); | |
| if (!DWOCU) { | |
| errs() << "BOLT-WARNING: [internal-dwarf-error]: CU for DWO_ID " | |
| << Twine::utohexstr(DWOId) << " is not found.\n"; | |
| return; | |
| } | |
| std::string CompDir = CU.getCompilationDir(); | |
| SmallString<16> AbsolutePath(DWOName); | |
| if (!opts::DwarfOutputPath.empty()) | |
| CompDir = opts::DwarfOutputPath.c_str(); | |
| else if (!opts::CompDirOverride.empty()) | |
| CompDir = opts::CompDirOverride; | |
| else if (!sys::fs::exists(CompDir)) | |
| CompDir = "."; | |
| // Prevent failures when DWOName is already an absolute path. | |
| sys::path::make_absolute(CompDir, AbsolutePath); | |
| std::error_code EC; | |
| std::unique_ptr<ToolOutputFile> TempOut = | |
| std::make_unique<ToolOutputFile>(AbsolutePath, EC, sys::fs::OF_None); | |
| const DWARFUnitIndex::Entry *CUDWOEntry = | |
| !BC.usesDWP() ? nullptr | |
| : (*DWOCU)->getContext().getCUIndex().getFromHash(DWOId); | |
| const object::ObjectFile *File = | |
| (*DWOCU)->getContext().getDWARFObj().getFile(); | |
| std::unique_ptr<BinaryContext> TmpBC = createDwarfOnlyBC(*File); | |
| std::unique_ptr<MCStreamer> Streamer = TmpBC->createStreamer(TempOut->os()); | |
| const MCObjectFileInfo &MCOFI = *Streamer->getContext().getObjectFileInfo(); | |
| StringMap<KnownSectionsEntry> KnownSections = createKnownSectionsMap(MCOFI); | |
| DebugRangeListsSectionWriter *RangeListssWriter = nullptr; | |
| if (CU.getVersion() == 5) { | |
| RangeListssWriter = | |
| llvm::dyn_cast<DebugRangeListsSectionWriter>(&TempRangesSectionWriter); | |
| // Handling .debug_rnglists.dwo separately. The original .o/.dwo might not | |
| // have .debug_rnglists so won't be part of the loop below. | |
| if (!RangeListssWriter->empty()) { | |
| std::unique_ptr<DebugBufferVector> OutputData; | |
| if (std::optional<StringRef> OutData = | |
| updateDebugData((*DWOCU)->getContext(), "debug_rnglists.dwo", "", | |
| KnownSections, *Streamer, *this, CUDWOEntry, | |
| DWOId, OutputData, RangeListssWriter, LocWriter, | |
| StrOffstsWriter, StrWriter, OverridenSections)) | |
| Streamer->emitBytes(*OutData); | |
| } | |
| } | |
| StringRef StrDWOContent; | |
| StringRef StrOffsetsContent; | |
| llvm::SmallVector<StringRef, 3> StrDWOOutData; | |
| std::string StrOffsetsOutData; | |
| for (const SectionRef &Section : File->sections()) { | |
| std::unique_ptr<DebugBufferVector> OutputData; | |
| StringRef SectionName = getSectionName(Section); | |
| if (SectionName == "debug_rnglists.dwo") | |
| continue; | |
| Expected<StringRef> ContentsExp = Section.getContents(); | |
| assert(ContentsExp && "Invalid contents."); | |
| if (BC.usesDWP() && SectionName == "debug_str.dwo") { | |
| if (StrWriter.isInitialized()) | |
| StrDWOContent = StrWriter.getBufferStr(); | |
| else | |
| StrDWOContent = *ContentsExp; | |
| continue; | |
| } | |
| if (std::optional<StringRef> OutData = updateDebugData( | |
| (*DWOCU)->getContext(), SectionName, *ContentsExp, KnownSections, | |
| *Streamer, *this, CUDWOEntry, DWOId, OutputData, RangeListssWriter, | |
| LocWriter, StrOffstsWriter, StrWriter, OverridenSections)) { | |
| if (BC.usesDWP() && SectionName == "debug_str_offsets.dwo") { | |
| StrOffsetsContent = *OutData; | |
| continue; | |
| } | |
| Streamer->emitBytes(*OutData); | |
| } | |
| } | |
| if (BC.usesDWP()) { | |
| // Handling both .debug_str.dwo and .debug_str_offsets.dwo concurrently. In | |
| // the original DWP, .debug_str is a deduplicated global table, and the | |
| // .debug_str.dwo slice for a single CU needs to be extracted according to | |
| // .debug_str_offsets.dwo. | |
| UpdateStrAndStrOffsets(StrDWOContent, StrOffsetsContent, StrDWOOutData, | |
| StrOffsetsOutData, CU, | |
| (*DWOCU)->getContext().isLittleEndian()); | |
| auto SectionIter = KnownSections.find("debug_str.dwo"); | |
| if (SectionIter != KnownSections.end()) { | |
| Streamer->switchSection(SectionIter->second.first); | |
| for (size_t i = 0; i < StrDWOOutData.size(); ++i) { | |
| StringRef OutData = StrDWOOutData[i]; | |
| if (!OutData.empty()) | |
| Streamer->emitBytes(OutData); | |
| } | |
| } | |
| SectionIter = KnownSections.find("debug_str_offsets.dwo"); | |
| if (SectionIter != KnownSections.end()) { | |
| Streamer->switchSection(SectionIter->second.first); | |
| if (!StrOffsetsOutData.empty()) | |
| Streamer->emitBytes(StrOffsetsOutData); | |
| else | |
| Streamer->emitBytes(StrOffsetsContent); | |
| } | |
| } | |
| Streamer->finish(); | |
| TempOut->keep(); | |
| } | |
| std::unique_ptr<DebugBufferVector> | |
| DWARFRewriter::makeFinalLocListsSection(DWARFVersion Version, | |
| ArrayRef<uint64_t> CUOrder) { | |
| auto LocBuffer = std::make_unique<DebugBufferVector>(); | |
| auto LocStream = std::make_unique<raw_svector_ostream>(*LocBuffer); | |
| auto Writer = | |
| std::unique_ptr<MCObjectWriter>(BC.createObjectWriter(*LocStream)); | |
| // For DWARF4 .debug_loc, reserve the first 16 bytes for the empty list. | |
| if (Version == DWARFVersion::DWARFLegacy) | |
| LocStream->write_zeros(16); | |
| for (uint64_t CUOffset : CUOrder) { | |
| auto It = LocListWritersByCU.find(CUOffset); | |
| if (It == LocListWritersByCU.end()) | |
| continue; | |
| DebugLocWriter *LocWriter = It->second.get(); | |
| auto *LocListWriter = dyn_cast<DebugLoclistWriter>(LocWriter); | |
| // Filter out DWARF4, writing out DWARF5 | |
| if (Version == DWARFVersion::DWARF5 && | |
| (!LocListWriter || LocListWriter->getDwarfVersion() <= 4)) | |
| continue; | |
| // Filter out DWARF5, writing out DWARF4 | |
| if (Version == DWARFVersion::DWARFLegacy && | |
| (LocListWriter && LocListWriter->getDwarfVersion() >= 5)) | |
| continue; | |
| // Skipping DWARF4/5 split dwarf. | |
| if (LocListWriter && LocListWriter->getDwarfVersion() <= 4) | |
| continue; | |
| std::unique_ptr<DebugBufferVector> CurrCULocationLists = | |
| LocWriter->getBuffer(); | |
| // For DWARF4, each per-CU buffer begins with a 16-byte empty list. | |
| // Emit the empty list only once globally (reserved above) | |
| if (Version == DWARFVersion::DWARFLegacy) { | |
| if (CurrCULocationLists->size() > 16) | |
| *LocStream << StringRef( | |
| reinterpret_cast<const char *>(CurrCULocationLists->data() + 16), | |
| CurrCULocationLists->size() - 16); | |
| continue; | |
| } | |
| *LocStream << *CurrCULocationLists; | |
| } | |
| return LocBuffer; | |
| } | |
| void DWARFRewriter::convertToRangesPatchDebugInfo( | |
| DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die, | |
| uint64_t RangesSectionOffset, DIEValue &LowPCAttrInfo, | |
| DIEValue &HighPCAttrInfo, DebugAddrWriter &AddressWriter, | |
| std::optional<uint64_t> RangesBase) { | |
| dwarf::Form LowForm = LowPCAttrInfo.getForm(); | |
| dwarf::Attribute RangeBaseAttribute = dwarf::DW_AT_GNU_ranges_base; | |
| dwarf::Form RangesForm = dwarf::DW_FORM_sec_offset; | |
| if (Unit.getVersion() >= 5) { | |
| RangeBaseAttribute = dwarf::DW_AT_rnglists_base; | |
| RangesForm = dwarf::DW_FORM_rnglistx; | |
| } else if (Unit.getVersion() < 4) { | |
| RangesForm = dwarf::DW_FORM_data4; | |
| } | |
| bool IsUnitDie = Die.getTag() == dwarf::DW_TAG_compile_unit || | |
| Die.getTag() == dwarf::DW_TAG_skeleton_unit; | |
| if (!IsUnitDie) | |
| DIEBldr.deleteValue(&Die, LowPCAttrInfo.getAttribute()); | |
| // In DWARF 5 we can have DW_AT_low_pc either as DW_FORM_addr, or | |
| // DW_FORM_addrx. Former is when DW_AT_rnglists_base is present. Latter is | |
| // when it's absent. | |
| if (IsUnitDie) { | |
| if (isIndexedAddressForm(LowForm)) { | |
| const uint32_t Index = AddressWriter.getIndexFromAddress(0, Unit); | |
| DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(), | |
| LowPCAttrInfo.getForm(), DIEInteger(Index)); | |
| } else { | |
| DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(), | |
| LowPCAttrInfo.getForm(), DIEInteger(0)); | |
| } | |
| // Original CU didn't have DW_AT_*_base. We converted it's children (or | |
| // dwo), so need to insert it into CU. | |
| if (RangesBase) { | |
| if (Unit.getVersion() >= 5) { | |
| DIEBldr.addValue(&Die, RangeBaseAttribute, dwarf::DW_FORM_sec_offset, | |
| DIEInteger(*RangesBase)); | |
| } else { | |
| DIEBldr.addValue(&Die, RangeBaseAttribute, dwarf::DW_FORM_sec_offset, | |
| DIEInteger(INT_MAX)); | |
| auto RangesWriterIterator = | |
| LegacyRangesWritersByCU.find(*Unit.getDWOId()); | |
| assert(RangesWriterIterator != LegacyRangesWritersByCU.end() && | |
| "RangesWriter does not exist for DWOId"); | |
| RangesWriterIterator->second->setDie(&Die); | |
| } | |
| } | |
| } | |
| // HighPC was converted into DW_AT_ranges. | |
| // For DWARF5 we only access ranges through index. | |
| DIEBldr.replaceValue(&Die, HighPCAttrInfo.getAttribute(), dwarf::DW_AT_ranges, | |
| RangesForm, DIEInteger(RangesSectionOffset)); | |
| } |