| //===- 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/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/DWARFExpression.h" |
| #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" |
| #include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h" |
| #include "llvm/DebugInfo/DWARF/DWARFUnit.h" |
| #include "llvm/MC/MCAsmBackend.h" |
| #include "llvm/MC/MCAsmLayout.h" |
| #include "llvm/MC/MCObjectWriter.h" |
| #include "llvm/MC/MCStreamer.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 <cstdint> |
| #include <functional> |
| #include <iterator> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #undef DEBUG_TYPE |
| #define DEBUG_TYPE "bolt" |
| |
| 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. |
| LLVM_ATTRIBUTE_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 (const DWARFAbbreviationDeclaration *AbbrDecl = |
| 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; |
| |
| /// 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; |
| DWARFRewriter &Rewriter; |
| |
| 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.emitInt32(Unit.getHeaderSize() + UnitDIE.getSize() - 4); |
| 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.emitInt32(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); |
| Rewriter.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, DWARFRewriter &Rewriter, |
| DWARFLinkerBase::OutputFileType OutFileType, |
| raw_pwrite_stream &OutFile, |
| DWARFLinkerBase::MessageHandlerTy Warning) |
| : DwarfStreamer(OutFileType, OutFile, Warning), DIEBldr(DIEBldr), |
| Rewriter(Rewriter){}; |
| |
| 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<bool> |
| DeterministicDebugInfo("deterministic-debuginfo", |
| cl::desc("disables parallel execution of tasks that may produce " |
| "nondeterministic debug info"), |
| cl::init(true), |
| cl::cat(BoltCategory)); |
| |
| static cl::opt<std::string> DwarfOutputPath( |
| "dwarf-output-path", |
| cl::desc("Path to where .dwo files or dwp file will be written out to."), |
| cl::init(""), cl::cat(BoltCategory)); |
| |
| static cl::opt<bool> |
| WriteDWP("write-dwp", |
| cl::desc("output a single dwarf package file (dwp) instead of " |
| "multiple non-relocatable dwarf object files (dwo)."), |
| cl::init(false), 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 offsetrs for abbrev and debu_info of " |
| "Skeleton CUs that get patched."), |
| cl::ZeroOrMore, 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 |
| |
| /// 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 (Form == dwarf::DW_FORM_GNU_addr_index || Form == dwarf::DW_FORM_addrx || |
| 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 uint64_t LowPC, uint64_t &HighPC) { |
| DIEValue DvalHighPc = Die.findAttribute(dwarf::DW_AT_high_pc); |
| if (!DvalHighPc) |
| return false; |
| if (DvalHighPc.getForm() == dwarf::DW_FORM_addr) |
| HighPC = DvalHighPc.getDIEInteger().getValue(); |
| else |
| HighPC = LowPC + DvalHighPc.getDIEInteger().getValue(); |
| 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, 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; |
| } |
| |
| /// Returns DWO Name to be used. Handles case where user specifies output DWO |
| /// directory, and there are duplicate names. Assumes DWO ID is unique. |
| static std::string |
| getDWOName(llvm::DWARFUnit &CU, |
| std::unordered_map<std::string, uint32_t> &NameToIndexMap) { |
| std::optional<uint64_t> DWOId = CU.getDWOId(); |
| assert(DWOId && "DWO ID not found."); |
| (void)DWOId; |
| |
| std::string DWOName = dwarf::toString( |
| CU.getUnitDIE().find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), |
| ""); |
| assert(!DWOName.empty() && |
| "DW_AT_dwo_name/DW_AT_GNU_dwo_name does not exists."); |
| if (!opts::DwarfOutputPath.empty()) { |
| DWOName = std::string(sys::path::filename(DWOName)); |
| auto Iter = NameToIndexMap.find(DWOName); |
| if (Iter == NameToIndexMap.end()) |
| Iter = NameToIndexMap.insert({DWOName, 0}).first; |
| DWOName.append(std::to_string(Iter->second)); |
| ++Iter->second; |
| } |
| DWOName.append(".dwo"); |
| return DWOName; |
| } |
| |
| static std::unique_ptr<DIEStreamer> |
| createDIEStreamer(const Triple &TheTriple, raw_pwrite_stream &OutFile, |
| StringRef Swift5ReflectionSegmentName, DIEBuilder &DIEBldr, |
| DWARFRewriter &Rewriter) { |
| |
| std::unique_ptr<DIEStreamer> Streamer = std::make_unique<DIEStreamer>( |
| &DIEBldr, Rewriter, 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 DWARFRewriter::UnitMeta |
| emitUnit(DIEBuilder &DIEBldr, DIEStreamer &Streamer, DWARFUnit &Unit) { |
| DIE *UnitDIE = DIEBldr.getUnitDIEbyUnit(Unit); |
| const DIEBuilder::DWARFUnitInfo &U = DIEBldr.getUnitInfoByDwarfUnit(Unit); |
| Streamer.emitUnit(Unit, *UnitDIE); |
| uint64_t TypeHash = 0; |
| if (DWARFTypeUnit *DTU = dyn_cast_or_null<DWARFTypeUnit>(&Unit)) |
| TypeHash = DTU->getTypeHash(); |
| return {U.UnitOffset, U.UnitLength, TypeHash}; |
| } |
| |
| static void emitDWOBuilder(const std::string &DWOName, |
| DIEBuilder &DWODIEBuilder, DWARFRewriter &Rewriter, |
| DWARFUnit &SplitCU, DWARFUnit &CU, |
| DWARFRewriter::DWPState &State, |
| DebugLocWriter &LocWriter) { |
| // 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, Rewriter); |
| DWARFRewriter::UnitMetaVectorType TUMetaVector; |
| DWARFRewriter::UnitMeta CUMI = {0, 0, 0}; |
| if (SplitCU.getContext().getMaxDWOVersion() >= 5) { |
| for (std::unique_ptr<llvm::DWARFUnit> &CU : |
| SplitCU.getContext().dwo_info_section_units()) { |
| if (!CU->isTypeUnit()) |
| continue; |
| DWARFRewriter::UnitMeta MI = |
| emitUnit(DWODIEBuilder, *Streamer, *CU.get()); |
| TUMetaVector.emplace_back(MI); |
| } |
| CUMI = emitUnit(DWODIEBuilder, *Streamer, SplitCU); |
| } else { |
| for (std::unique_ptr<llvm::DWARFUnit> &CU : |
| SplitCU.getContext().dwo_compile_units()) |
| emitUnit(DWODIEBuilder, *Streamer, *CU.get()); |
| |
| // emit debug_types sections for dwarf4 |
| for (DWARFUnit *CU : DWODIEBuilder.getDWARF4TUVector()) { |
| DWARFRewriter::UnitMeta MI = emitUnit(DWODIEBuilder, *Streamer, *CU); |
| TUMetaVector.emplace_back(MI); |
| } |
| } |
| |
| 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; |
| } |
| if (opts::WriteDWP) |
| Rewriter.updateDWP(CU, OverriddenSections, CUMI, TUMetaVector, State, |
| LocWriter); |
| else |
| Rewriter.writeDWOFiles(CU, OverriddenSections, DWOName, LocWriter); |
| } |
| |
| void DWARFRewriter::addStringHelper(DIEBuilder &DIEBldr, DIE &Die, |
| const DWARFUnit &Unit, |
| DIEValue &DIEAttrInfo, StringRef Str) { |
| uint32_t NewOffset = StrWriter->addString(Str); |
| if (Unit.getVersion() >= 5) { |
| StrOffstsWriter->updateAddressMap(DIEAttrInfo.getDIEInteger().getValue(), |
| NewOffset); |
| return; |
| } |
| DIEBldr.replaceValue(&Die, DIEAttrInfo.getAttribute(), DIEAttrInfo.getForm(), |
| DIEInteger(NewOffset)); |
| } |
| |
| using DWARFUnitVec = std::vector<DWARFUnit *>; |
| using CUPartitionVector = std::vector<DWARFUnitVec>; |
| /// Partitions CUs in to buckets. Bucket size is controlled by |
| /// cu-processing-batch-size. All the CUs that have cross CU reference reference |
| /// as a source are put in to the same initial bucket. |
| static CUPartitionVector partitionCUs(DWARFContext &DwCtx) { |
| CUPartitionVector Vec(2); |
| unsigned Counter = 0; |
| const DWARFDebugAbbrev *Abbr = DwCtx.getDebugAbbrev(); |
| for (std::unique_ptr<DWARFUnit> &CU : DwCtx.compile_units()) { |
| Expected<const DWARFAbbreviationDeclarationSet *> AbbrDeclSet = |
| Abbr->getAbbreviationDeclarationSet(CU->getAbbreviationsOffset()); |
| if (!AbbrDeclSet) { |
| consumeError(AbbrDeclSet.takeError()); |
| return Vec; |
| } |
| bool CrossCURefFound = false; |
| for (const DWARFAbbreviationDeclaration &Decl : *AbbrDeclSet.get()) { |
| for (const DWARFAbbreviationDeclaration::AttributeSpec &Attr : |
| Decl.attributes()) { |
| if (Attr.Form == dwarf::DW_FORM_ref_addr) { |
| CrossCURefFound = true; |
| break; |
| } |
| } |
| if (CrossCURefFound) |
| break; |
| } |
| if (CrossCURefFound) { |
| Vec[0].push_back(CU.get()); |
| } else { |
| ++Counter; |
| Vec.back().push_back(CU.get()); |
| } |
| if (Counter % opts::BatchSize == 0 && !Vec.back().empty()) |
| Vec.push_back({}); |
| } |
| return Vec; |
| } |
| |
| void DWARFRewriter::updateDebugInfo() { |
| ErrorOr<BinarySection &> DebugInfo = BC.getUniqueSectionByName(".debug_info"); |
| if (!DebugInfo) |
| return; |
| |
| ARangesSectionWriter = std::make_unique<DebugARangesSectionWriter>(); |
| StrWriter = std::make_unique<DebugStrWriter>(BC); |
| |
| StrOffstsWriter = std::make_unique<DebugStrOffsetsWriter>(); |
| |
| if (!opts::DeterministicDebugInfo) { |
| opts::DeterministicDebugInfo = true; |
| errs() << "BOLT-WARNING: --deterministic-debuginfo is being deprecated\n"; |
| } |
| |
| if (BC.isDWARF5Used()) { |
| AddrWriter = std::make_unique<DebugAddrWriterDwarf5>(&BC); |
| RangeListsSectionWriter = std::make_unique<DebugRangeListsSectionWriter>(); |
| DebugRangeListsSectionWriter::setAddressWriter(AddrWriter.get()); |
| } else { |
| AddrWriter = std::make_unique<DebugAddrWriter>(&BC); |
| } |
| |
| if (BC.isDWARFLegacyUsed()) |
| LegacyRangesSectionWriter = std::make_unique<DebugRangesSectionWriter>(); |
| |
| DebugLoclistWriter::setAddressWriter(AddrWriter.get()); |
| |
| uint32_t CUIndex = 0; |
| std::mutex AccessMutex; |
| // Needs to be invoked in the same order as CUs are processed. |
| auto createRangeLocList = [&](DWARFUnit &CU) -> DebugLocWriter * { |
| std::lock_guard<std::mutex> Lock(AccessMutex); |
| const uint16_t DwarfVersion = CU.getVersion(); |
| if (DwarfVersion >= 5) { |
| LocListWritersByCU[CUIndex] = |
| std::make_unique<DebugLoclistWriter>(CU, DwarfVersion, false); |
| |
| if (std::optional<uint64_t> DWOId = CU.getDWOId()) { |
| assert(RangeListsWritersByCU.count(*DWOId) == 0 && |
| "RangeLists writer for DWO unit already exists."); |
| auto RangeListsSectionWriter = |
| std::make_unique<DebugRangeListsSectionWriter>(); |
| RangeListsSectionWriter->initSection(CU); |
| RangeListsWritersByCU[*DWOId] = std::move(RangeListsSectionWriter); |
| } |
| |
| } else { |
| LocListWritersByCU[CUIndex] = std::make_unique<DebugLocWriter>(); |
| } |
| return LocListWritersByCU[CUIndex++].get(); |
| }; |
| |
| // Unordered maps to handle name collision if output DWO directory is |
| // specified. |
| std::unordered_map<std::string, uint32_t> NameToIndexMap; |
| |
| auto updateDWONameCompDir = [&](DWARFUnit &Unit, DIEBuilder &DIEBldr, |
| DIE &UnitDIE) -> std::string { |
| DIEValue DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_dwo_name); |
| if (!DWONameAttrInfo) |
| DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_GNU_dwo_name); |
| assert(DWONameAttrInfo && "DW_AT_dwo_name is not in Skeleton CU."); |
| std::string ObjectName; |
| |
| { |
| std::lock_guard<std::mutex> Lock(AccessMutex); |
| ObjectName = getDWOName(Unit, NameToIndexMap); |
| } |
| addStringHelper(DIEBldr, UnitDIE, Unit, DWONameAttrInfo, |
| ObjectName.c_str()); |
| |
| DIEValue CompDirAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_comp_dir); |
| assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU."); |
| |
| if (!opts::DwarfOutputPath.empty()) { |
| if (!sys::fs::exists(opts::DwarfOutputPath)) |
| sys::fs::create_directory(opts::DwarfOutputPath); |
| addStringHelper(DIEBldr, UnitDIE, Unit, CompDirAttrInfo, |
| opts::DwarfOutputPath.c_str()); |
| } |
| return ObjectName; |
| }; |
| |
| DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC, |
| *StrWriter); |
| DWPState State; |
| if (opts::WriteDWP) |
| initDWPState(State); |
| auto processUnitDIE = [&](DWARFUnit *Unit, DIEBuilder *DIEBlder) { |
| // Check if the unit is a skeleton and we need special updates for it and |
| // its matching split/DWO CU. |
| std::optional<DWARFUnit *> SplitCU; |
| std::optional<uint64_t> RangesBase; |
| std::optional<uint64_t> DWOId = Unit->getDWOId(); |
| if (DWOId) |
| SplitCU = BC.getDWOCU(*DWOId); |
| DebugLocWriter *DebugLocWriter = createRangeLocList(*Unit); |
| DebugRangesSectionWriter *RangesSectionWriter = |
| Unit->getVersion() >= 5 ? RangeListsSectionWriter.get() |
| : LegacyRangesSectionWriter.get(); |
| // Skipping CUs that failed to load. |
| if (SplitCU) { |
| DIEBuilder DWODIEBuilder(BC, &(*SplitCU)->getContext(), DebugNamesTable, |
| Unit); |
| DWODIEBuilder.buildDWOUnit(**SplitCU); |
| std::string DWOName = updateDWONameCompDir( |
| *Unit, *DIEBlder, *DIEBlder->getUnitDIEbyUnit(*Unit)); |
| |
| DebugLoclistWriter DebugLocDWoWriter(*Unit, Unit->getVersion(), true); |
| DebugRangesSectionWriter *TempRangesSectionWriter = RangesSectionWriter; |
| if (Unit->getVersion() >= 5) { |
| TempRangesSectionWriter = RangeListsWritersByCU[*DWOId].get(); |
| } else { |
| RangesBase = RangesSectionWriter->getSectionOffset(); |
| setDwoRangesBase(*DWOId, *RangesBase); |
| } |
| |
| updateUnitDebugInfo(*(*SplitCU), DWODIEBuilder, DebugLocDWoWriter, |
| *TempRangesSectionWriter); |
| DebugLocDWoWriter.finalize(DWODIEBuilder, |
| *DWODIEBuilder.getUnitDIEbyUnit(**SplitCU)); |
| if (Unit->getVersion() >= 5) |
| TempRangesSectionWriter->finalizeSection(); |
| |
| emitDWOBuilder(DWOName, DWODIEBuilder, *this, **SplitCU, *Unit, State, |
| DebugLocDWoWriter); |
| } |
| |
| if (Unit->getVersion() >= 5) { |
| RangesBase = RangesSectionWriter->getSectionOffset() + |
| getDWARF5RngListLocListHeaderSize(); |
| RangesSectionWriter->initSection(*Unit); |
| StrOffstsWriter->finalizeSection(*Unit, *DIEBlder); |
| } |
| |
| updateUnitDebugInfo(*Unit, *DIEBlder, *DebugLocWriter, *RangesSectionWriter, |
| RangesBase); |
| DebugLocWriter->finalize(*DIEBlder, *DIEBlder->getUnitDIEbyUnit(*Unit)); |
| if (Unit->getVersion() >= 5) |
| RangesSectionWriter->finalizeSection(); |
| AddrWriter->update(*DIEBlder, *Unit); |
| }; |
| |
| 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, *this); |
| CUOffsetMap OffsetMap = finalizeTypeSections(DIEBlder, *Streamer); |
| |
| const bool SingleThreadedMode = |
| opts::NoThreads || opts::DeterministicDebugInfo; |
| if (!SingleThreadedMode) |
| DIEBlder.buildCompileUnits(); |
| if (SingleThreadedMode) { |
| CUPartitionVector PartVec = partitionCUs(*BC.DwCtx); |
| for (std::vector<DWARFUnit *> &Vec : PartVec) { |
| DIEBlder.buildCompileUnits(Vec); |
| for (DWARFUnit *CU : DIEBlder.getProcessedCUs()) |
| processUnitDIE(CU, &DIEBlder); |
| finalizeCompileUnits(DIEBlder, *Streamer, OffsetMap, |
| DIEBlder.getProcessedCUs()); |
| } |
| } else { |
| // Update unit debug info in parallel |
| ThreadPoolInterface &ThreadPool = ParallelUtilities::getThreadPool(); |
| for (std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) |
| ThreadPool.async(processUnitDIE, CU.get(), &DIEBlder); |
| ThreadPool.wait(); |
| } |
| |
| DebugNamesTable.emitAccelTable(); |
| |
| if (opts::WriteDWP) |
| finalizeDWP(State); |
| |
| finalizeDebugSections(DIEBlder, DebugNamesTable, *Streamer, *ObjOS, |
| OffsetMap); |
| updateGdbIndexSection(OffsetMap, CUIndex); |
| } |
| |
| void DWARFRewriter::updateUnitDebugInfo( |
| DWARFUnit &Unit, DIEBuilder &DIEBldr, DebugLocWriter &DebugLocWriter, |
| DebugRangesSectionWriter &RangesSectionWriter, |
| 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; |
| dwarf::Form FormHighPC = dwarf::DW_FORM_data4; |
| const uint32_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(); |
| } |
| |
| if (FormLowPC == dwarf::DW_FORM_addrx || |
| FormLowPC == dwarf::DW_FORM_GNU_addr_index) |
| LowPC = AddrWriter->getIndexFromAddress(LowPC, Unit); |
| |
| 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(Size)); |
| } else { |
| DIEBldr.deleteValue(Die, dwarf::DW_AT_ranges); |
| DIEBldr.addValue(Die, AttrHighPC, FormHighPC, DIEInteger(Size)); |
| } |
| }; |
| |
| 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, |
| 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()) { |
| if (LowPCVal && HighPCVal) |
| FunctionRanges.push_back({0, HighPCVal.getDIEInteger().getValue()}); |
| else |
| FunctionRanges.push_back({0, 1}); |
| } |
| |
| if (FunctionRanges.size() == 1 && !opts::AlwaysConvertToRanges) { |
| updateLowPCHighPC(Die, LowPCVal, HighPCVal, FunctionRanges.back().LowPC, |
| FunctionRanges.back().HighPC); |
| break; |
| } |
| |
| updateDWARFObjectAddressRanges( |
| Unit, DIEBldr, *Die, RangesSectionWriter.addRanges(FunctionRanges)); |
| |
| 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); |
| 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 (AttrVal.getForm() == dwarf::DW_FORM_addrx) { |
| const uint32_t Index = |
| AddrWriter->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); |
| } |
| ArrayRef<uint8_t> Expr = ArrayRef<uint8_t>(Sblock); |
| DataExtractor Data( |
| StringRef((const char *)Expr.data(), Expr.size()), |
| Unit.getContext().isLittleEndian(), 0); |
| 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 = AddrWriter->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"); |
| std::lock_guard<std::mutex> Lock(DWARFRewriterMutex); |
| if (Form == dwarf::DW_FORM_addrx || |
| Form == dwarf::DW_FORM_GNU_addr_index) { |
| const uint32_t Index = AddrWriter->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, |
| 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) { |
| DIEBldr.replaceValue(&Die, RangesBaseInfo.getAttribute(), |
| RangesBaseInfo.getForm(), |
| DIEInteger(static_cast<uint32_t>(*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; |
| |
| uint64_t CurRangeBase = 0; |
| if (Unit.isDWOUnit()) { |
| if (std::optional<uint64_t> DWOId = Unit.getDWOId()) |
| CurRangeBase = getDwoRangesBase(*DWOId); |
| else |
| errs() << "BOLT-WARNING: [internal-dwarf-error]: DWOId is not found " |
| "for DWO Unit."; |
| } |
| 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 - CurRangeBase)); |
| |
| if (!RangesBase) { |
| if (LowPCAttrInfo && |
| LowPCAttrInfo.getForm() != dwarf::DW_FORM_GNU_addr_index && |
| LowPCAttrInfo.getForm() != dwarf::DW_FORM_addrx) |
| 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(*RangesBase)); |
| else if (Unit.getVersion() == 5) |
| DIEBldr.addValue(&Die, dwarf::DW_AT_rnglists_base, |
| dwarf::DW_FORM_sec_offset, DIEInteger(*RangesBase)); |
| else |
| 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, 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" |
| << Unit.getOffset() << '\n'; |
| } |
| } |
| |
| void DWARFRewriter::updateLineTableOffsets(const MCAsmLayout &Layout) { |
| 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.get()->getUnitDIE()); |
| if (!StmtOffset) |
| continue; |
| |
| const uint64_t LineTableOffset = Layout.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) { |
| // 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(); |
| 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, *this); |
| |
| // 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.get()); |
| emitUnit(DIEBlder, Streamer, *CU.get()); |
| uint32_t StartOffset = CUOffset; |
| DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(*CU.get()); |
| CUOffset += CU.get()->getHeaderSize(); |
| CUOffset += UnitDIE->getSize(); |
| CUMap[CU.get()->getOffset()] = {StartOffset, CUOffset - StartOffset - 4}; |
| } |
| |
| // 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.equals(".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) { |
| 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); |
| if (!LocationListSectionContents->empty()) |
| BC.registerOrUpdateNoteSection( |
| ".debug_loclists", copyByteArray(*LocationListSectionContents), |
| LocationListSectionContents->size()); |
| } |
| |
| if (BC.isDWARFLegacyUsed()) { |
| std::unique_ptr<DebugBufferVector> LocationListSectionContents = |
| makeFinalLocListsSection(DWARFVersion::DWARFLegacy); |
| if (!LocationListSectionContents->empty()) |
| BC.registerOrUpdateNoteSection( |
| ".debug_loc", copyByteArray(*LocationListSectionContents), |
| LocationListSectionContents->size()); |
| } |
| |
| // AddrWriter should be finalized after debug_loc since more addresses can be |
| // added there. |
| if (AddrWriter->isInitialized()) { |
| AddressSectionBuffer AddressSectionContents = AddrWriter->finalize(); |
| 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.equals(".debug_abbrev")) { |
| BC.registerOrUpdateNoteSection(".debug_abbrev", copyByteArray(Contents), |
| Contents.size()); |
| } else if (Name.equals(".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, |
| DIEStreamer &Streamer, |
| CUOffsetMap &CUMap, |
| const std::list<DWARFUnit *> &CUs) { |
| DIEBlder.generateAbbrevs(); |
| DIEBlder.finish(); |
| // generate debug_info and CUMap |
| for (DWARFUnit *CU : CUs) { |
| emitUnit(DIEBlder, Streamer, *CU); |
| const uint32_t StartOffset = CUOffset; |
| DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(*CU); |
| CUOffset += CU->getHeaderSize(); |
| CUOffset += UnitDIE->getSize(); |
| CUMap[CU->getOffset()] = {StartOffset, CUOffset - StartOffset - 4}; |
| } |
| } |
| |
| // 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(), 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; |
| } |
| |
| // 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, |
| 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); |
| 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 overriden " |
| "section for: " |
| << Twine::utohexstr(DWOId) << ".\n"; |
| return std::nullopt; |
| } |
| return Iter->second; |
| }; |
| switch (SectionIter->second.second) { |
| default: { |
| if (!SectionName.equals("debug_str.dwo")) |
| errs() << "BOLT-WARNING: unsupported debug section: " << SectionName |
| << "\n"; |
| 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: { |
| 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::initDWPState(DWPState &State) { |
| SmallString<0> OutputNameStr; |
| StringRef OutputName; |
| if (opts::DwarfOutputPath.empty()) { |
| OutputName = |
| Twine(opts::OutputFilename).concat(".dwp").toStringRef(OutputNameStr); |
| } else { |
| StringRef ExeFileName = llvm::sys::path::filename(opts::OutputFilename); |
| OutputName = Twine(opts::DwarfOutputPath) |
| .concat("/") |
| .concat(ExeFileName) |
| .concat(".dwp") |
| .toStringRef(OutputNameStr); |
| errs() << "BOLT-WARNING: dwarf-output-path is in effect and .dwp file will " |
| "possibly be written to another location that is not the same as " |
| "the executable\n"; |
| } |
| std::error_code EC; |
| State.Out = |
| std::make_unique<ToolOutputFile>(OutputName, EC, sys::fs::OF_None); |
| const object::ObjectFile *File = BC.DwCtx->getDWARFObj().getFile(); |
| State.TmpBC = createDwarfOnlyBC(*File); |
| State.Streamer = State.TmpBC->createStreamer(State.Out->os()); |
| State.MCOFI = State.Streamer->getContext().getObjectFileInfo(); |
| State.KnownSections = createKnownSectionsMap(*State.MCOFI); |
| MCSection *const StrSection = State.MCOFI->getDwarfStrDWOSection(); |
| |
| // Data Structures for DWP book keeping |
| // Size of array corresponds to the number of sections supported by DWO format |
| // in DWARF4/5. |
| |
| State.Strings = std::make_unique<DWPStringPool>(*State.Streamer, StrSection); |
| |
| // Setup DWP code once. |
| DWARFContext *DWOCtx = BC.getDWOContext(); |
| |
| if (DWOCtx) { |
| State.CUIndex = &DWOCtx->getCUIndex(); |
| State.IsDWP = !State.CUIndex->getRows().empty(); |
| } |
| } |
| |
| void DWARFRewriter::finalizeDWP(DWPState &State) { |
| if (State.Version < 5) { |
| // Lie about there being no info contributions so the TU index only includes |
| // the type unit contribution for DWARF < 5. In DWARFv5 the TU index has a |
| // contribution to the info section, so we do not want to lie about it. |
| State.ContributionOffsets[0] = 0; |
| } |
| writeIndex(*State.Streamer.get(), State.MCOFI->getDwarfTUIndexSection(), |
| State.ContributionOffsets, State.TypeIndexEntries, |
| State.IndexVersion); |
| |
| if (State.Version < 5) { |
| // Lie about the type contribution for DWARF < 5. In DWARFv5 the type |
| // section does not exist, so no need to do anything about this. |
| State.ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES, 2)] = 0; |
| // Unlie about the info contribution |
| State.ContributionOffsets[0] = 1; |
| } |
| writeIndex(*State.Streamer.get(), State.MCOFI->getDwarfCUIndexSection(), |
| State.ContributionOffsets, State.IndexEntries, State.IndexVersion); |
| |
| State.Streamer->finish(); |
| State.Out->keep(); |
| } |
| |
| void DWARFRewriter::updateDWP(DWARFUnit &CU, |
| const OverriddenSectionsMap &OverridenSections, |
| const DWARFRewriter::UnitMeta &CUMI, |
| DWARFRewriter::UnitMetaVectorType &TUMetaVector, |
| DWPState &State, DebugLocWriter &LocWriter) { |
| const uint64_t DWOId = *CU.getDWOId(); |
| MCSection *const StrOffsetSection = State.MCOFI->getDwarfStrOffDWOSection(); |
| assert(StrOffsetSection && "StrOffsetSection does not exist."); |
| // Skipping CUs that we failed to load. |
| std::optional<DWARFUnit *> DWOCU = BC.getDWOCU(DWOId); |
| if (!DWOCU) |
| return; |
| |
| if (State.Version == 0) { |
| State.Version = CU.getVersion(); |
| State.IndexVersion = State.Version < 5 ? 2 : 5; |
| } else if (State.Version != CU.getVersion()) { |
| errs() << "BOLT-ERROR: incompatible DWARF compile unit versions\n"; |
| exit(1); |
| } |
| |
| UnitIndexEntry CurEntry = {}; |
| CurEntry.DWOName = dwarf::toString( |
| CU.getUnitDIE().find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), |
| ""); |
| const char *Name = CU.getUnitDIE().getShortName(); |
| if (Name) |
| CurEntry.Name = Name; |
| StringRef CurStrSection; |
| StringRef CurStrOffsetSection; |
| |
| // This maps each section contained in this file to its length. |
| // This information is later on used to calculate the contributions, |
| // i.e. offset and length, of each compile/type unit to a section. |
| std::vector<std::pair<DWARFSectionKind, uint32_t>> SectionLength; |
| |
| const DWARFUnitIndex::Entry *CUDWOEntry = nullptr; |
| if (State.IsDWP) |
| CUDWOEntry = State.CUIndex->getFromHash(DWOId); |
| |
| bool StrSectionWrittenOut = false; |
| const object::ObjectFile *DWOFile = |
| (*DWOCU)->getContext().getDWARFObj().getFile(); |
| |
| DebugRangeListsSectionWriter *RangeListssWriter = nullptr; |
| if (CU.getVersion() == 5) { |
| assert(RangeListsWritersByCU.count(DWOId) != 0 && |
| "No RangeListsWriter for DWO ID."); |
| RangeListssWriter = RangeListsWritersByCU[DWOId].get(); |
| } |
| auto AddType = [&](unsigned int Index, uint32_t IndexVersion, uint64_t Offset, |
| uint64_t Length, uint64_t Hash) -> void { |
| UnitIndexEntry TUEntry = CurEntry; |
| if (IndexVersion < 5) |
| TUEntry.Contributions[0] = {}; |
| TUEntry.Contributions[Index].setOffset(Offset); |
| TUEntry.Contributions[Index].setLength(Length); |
| State.ContributionOffsets[Index] += |
| TUEntry.Contributions[Index].getLength32(); |
| State.TypeIndexEntries.insert(std::make_pair(Hash, TUEntry)); |
| }; |
| for (const SectionRef &Section : DWOFile->sections()) { |
| std::unique_ptr<DebugBufferVector> OutputData; |
| StringRef SectionName = getSectionName(Section); |
| Expected<StringRef> ContentsExp = Section.getContents(); |
| assert(ContentsExp && "Invalid contents."); |
| std::optional<StringRef> TOutData = updateDebugData( |
| (*DWOCU)->getContext(), SectionName, *ContentsExp, State.KnownSections, |
| *State.Streamer, *this, CUDWOEntry, DWOId, OutputData, |
| RangeListssWriter, LocWriter, OverridenSections); |
| if (!TOutData) |
| continue; |
| |
| StringRef OutData = *TOutData; |
| if (SectionName == "debug_types.dwo") { |
| State.Streamer->emitBytes(OutData); |
| continue; |
| } |
| |
| if (SectionName.equals("debug_str.dwo")) { |
| CurStrSection = OutData; |
| } else { |
| // Since handleDebugDataPatching returned true, we already know this is |
| // a known section. |
| auto SectionIter = State.KnownSections.find(SectionName); |
| if (SectionIter->second.second == DWARFSectionKind::DW_SECT_STR_OFFSETS) |
| CurStrOffsetSection = OutData; |
| else |
| State.Streamer->emitBytes(OutData); |
| unsigned int Index = |
| getContributionIndex(SectionIter->second.second, State.IndexVersion); |
| uint64_t Offset = State.ContributionOffsets[Index]; |
| uint64_t Length = OutData.size(); |
| if (CU.getVersion() >= 5 && |
| SectionIter->second.second == DWARFSectionKind::DW_SECT_INFO) { |
| for (UnitMeta &MI : TUMetaVector) |
| MI.Offset += State.DebugInfoSize; |
| |
| Offset = State.DebugInfoSize + CUMI.Offset; |
| Length = CUMI.Length; |
| State.DebugInfoSize += OutData.size(); |
| } |
| CurEntry.Contributions[Index].setOffset(Offset); |
| CurEntry.Contributions[Index].setLength(Length); |
| State.ContributionOffsets[Index] += |
| CurEntry.Contributions[Index].getLength32(); |
| } |
| |
| // Strings are combined in to a new string section, and de-duplicated |
| // based on hash. |
| if (!StrSectionWrittenOut && !CurStrOffsetSection.empty() && |
| !CurStrSection.empty()) { |
| writeStringsAndOffsets(*State.Streamer.get(), *State.Strings.get(), |
| StrOffsetSection, CurStrSection, |
| CurStrOffsetSection, CU.getVersion()); |
| StrSectionWrittenOut = true; |
| } |
| } |
| CompileUnitIdentifiers CUI{DWOId, CurEntry.Name.c_str(), |
| CurEntry.DWOName.c_str()}; |
| auto P = State.IndexEntries.insert(std::make_pair(CUI.Signature, CurEntry)); |
| if (!P.second) { |
| Error Err = buildDuplicateError(*P.first, CUI, ""); |
| errs() << "BOLT-ERROR: " << toString(std::move(Err)) << "\n"; |
| return; |
| } |
| |
| // Handling TU |
| const unsigned Index = getContributionIndex( |
| State.IndexVersion < 5 ? DW_SECT_EXT_TYPES : DW_SECT_INFO, |
| State.IndexVersion); |
| for (UnitMeta &MI : TUMetaVector) |
| AddType(Index, State.IndexVersion, MI.Offset, MI.Length, MI.TUHash); |
| } |
| |
| void DWARFRewriter::writeDWOFiles( |
| DWARFUnit &CU, const OverriddenSectionsMap &OverridenSections, |
| const std::string &DWOName, DebugLocWriter &LocWriter) { |
| // Setup DWP code once. |
| DWARFContext *DWOCtx = BC.getDWOContext(); |
| const uint64_t DWOId = *CU.getDWOId(); |
| const DWARFUnitIndex *CUIndex = nullptr; |
| bool IsDWP = false; |
| if (DWOCtx) { |
| CUIndex = &DWOCtx->getCUIndex(); |
| IsDWP = !CUIndex->getRows().empty(); |
| } |
| |
| // 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(); |
| |
| if (!opts::DwarfOutputPath.empty()) |
| CompDir = opts::DwarfOutputPath.c_str(); |
| else if (!opts::CompDirOverride.empty()) |
| CompDir = opts::CompDirOverride; |
| |
| SmallString<16> AbsolutePath; |
| sys::path::append(AbsolutePath, CompDir); |
| sys::path::append(AbsolutePath, DWOName); |
| |
| std::error_code EC; |
| std::unique_ptr<ToolOutputFile> TempOut = |
| std::make_unique<ToolOutputFile>(AbsolutePath, EC, sys::fs::OF_None); |
| |
| const DWARFUnitIndex::Entry *CUDWOEntry = nullptr; |
| if (IsDWP) |
| CUDWOEntry = CUIndex->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) { |
| assert(RangeListsWritersByCU.count(DWOId) != 0 && |
| "No RangeListsWriter for DWO ID."); |
| RangeListssWriter = RangeListsWritersByCU[DWOId].get(); |
| |
| // 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, OverridenSections)) |
| Streamer->emitBytes(*OutData); |
| } |
| } |
| |
| 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 (std::optional<StringRef> OutData = updateDebugData( |
| (*DWOCU)->getContext(), SectionName, *ContentsExp, KnownSections, |
| *Streamer, *this, CUDWOEntry, DWOId, OutputData, RangeListssWriter, |
| LocWriter, OverridenSections)) |
| Streamer->emitBytes(*OutData); |
| } |
| Streamer->finish(); |
| TempOut->keep(); |
| } |
| |
| void DWARFRewriter::addGDBTypeUnitEntry(const GDBIndexTUEntry &&Entry) { |
| std::lock_guard<std::mutex> Lock(DWARFRewriterMutex); |
| if (!BC.getGdbIndexSection()) |
| return; |
| GDBIndexTUEntryVector.emplace_back(Entry); |
| } |
| |
| void DWARFRewriter::updateGdbIndexSection(CUOffsetMap &CUMap, uint32_t NumCUs) { |
| if (!BC.getGdbIndexSection()) |
| return; |
| |
| // See https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html |
| // for .gdb_index section format. |
| |
| StringRef GdbIndexContents = BC.getGdbIndexSection()->getContents(); |
| |
| const char *Data = GdbIndexContents.data(); |
| |
| // Parse the header. |
| const uint32_t Version = read32le(Data); |
| if (Version != 7 && Version != 8) { |
| errs() << "BOLT-ERROR: can only process .gdb_index versions 7 and 8\n"; |
| exit(1); |
| } |
| |
| // Some .gdb_index generators use file offsets while others use section |
| // offsets. Hence we can only rely on offsets relative to each other, |
| // and ignore their absolute values. |
| const uint32_t CUListOffset = read32le(Data + 4); |
| const uint32_t CUTypesOffset = read32le(Data + 8); |
| const uint32_t AddressTableOffset = read32le(Data + 12); |
| const uint32_t SymbolTableOffset = read32le(Data + 16); |
| const uint32_t ConstantPoolOffset = read32le(Data + 20); |
| Data += 24; |
| |
| // Map CUs offsets to indices and verify existing index table. |
| std::map<uint32_t, uint32_t> OffsetToIndexMap; |
| const uint32_t CUListSize = CUTypesOffset - CUListOffset; |
| const uint32_t TUListSize = AddressTableOffset - CUTypesOffset; |
| const unsigned NUmCUsEncoded = CUListSize / 16; |
| unsigned MaxDWARFVersion = BC.DwCtx->getMaxVersion(); |
| unsigned NumDWARF5TUs = |
| getGDBIndexTUEntryVector().size() - BC.DwCtx->getNumTypeUnits(); |
| bool SkipTypeUnits = false; |
| // For DWARF5 Types are in .debug_info. |
| // LLD doesn't generate Types CU List, and in CU list offset |
| // only includes CUs. |
| // GDB 11+ includes only CUs in CU list and generates Types |
| // list. |
| // GDB 9 includes CUs and TUs in CU list and generates TYpes |
| // list. The NumCUs is CUs + TUs, so need to modify the check. |
| // For split-dwarf |
| // GDB-11, DWARF5: TU units from dwo are not included. |
| // GDB-11, DWARF4: TU units from dwo are included. |
| if (MaxDWARFVersion >= 5) |
| SkipTypeUnits = !TUListSize ? true |
| : ((NUmCUsEncoded + NumDWARF5TUs) == |
| BC.DwCtx->getNumCompileUnits()); |
| |
| if (!((CUListSize == NumCUs * 16) || |
| (CUListSize == (NumCUs + NumDWARF5TUs) * 16))) { |
| errs() << "BOLT-ERROR: .gdb_index: CU count mismatch\n"; |
| exit(1); |
| } |
| DenseSet<uint64_t> OriginalOffsets; |
| for (unsigned Index = 0, Units = BC.DwCtx->getNumCompileUnits(); |
| Index < Units; ++Index) { |
| const DWARFUnit *CU = BC.DwCtx->getUnitAtIndex(Index); |
| if (SkipTypeUnits && CU->isTypeUnit()) |
| continue; |
| const uint64_t Offset = read64le(Data); |
| Data += 16; |
| if (CU->getOffset() != Offset) { |
| errs() << "BOLT-ERROR: .gdb_index CU offset mismatch\n"; |
| exit(1); |
| } |
| |
| OriginalOffsets.insert(Offset); |
| OffsetToIndexMap[Offset] = Index; |
| } |
| |
| // Ignore old address table. |
| const uint32_t OldAddressTableSize = SymbolTableOffset - AddressTableOffset; |
| // Move Data to the beginning of symbol table. |
| Data += SymbolTableOffset - CUTypesOffset; |
| |
| // Calculate the size of the new address table. |
| uint32_t NewAddressTableSize = 0; |
| for (const auto &CURangesPair : ARangesSectionWriter->getCUAddressRanges()) { |
| const SmallVector<DebugAddressRange, 2> &Ranges = CURangesPair.second; |
| NewAddressTableSize += Ranges.size() * 20; |
| } |
| |
| // Difference between old and new table (and section) sizes. |
| // Could be negative. |
| int32_t Delta = NewAddressTableSize - OldAddressTableSize; |
| |
| size_t NewGdbIndexSize = GdbIndexContents.size() + Delta; |
| |
| // Free'd by ExecutableFileMemoryManager. |
| auto *NewGdbIndexContents = new uint8_t[NewGdbIndexSize]; |
| uint8_t *Buffer = NewGdbIndexContents; |
| |
| write32le(Buffer, Version); |
| write32le(Buffer + 4, CUListOffset); |
| write32le(Buffer + 8, CUTypesOffset); |
| write32le(Buffer + 12, AddressTableOffset); |
| write32le(Buffer + 16, SymbolTableOffset + Delta); |
| write32le(Buffer + 20, ConstantPoolOffset + Delta); |
| Buffer += 24; |
| |
| using MapEntry = std::pair<uint32_t, CUInfo>; |
| std::vector<MapEntry> CUVector(CUMap.begin(), CUMap.end()); |
| // Need to sort since we write out all of TUs in .debug_info before CUs. |
| std::sort(CUVector.begin(), CUVector.end(), |
| [](const MapEntry &E1, const MapEntry &E2) -> bool { |
| return E1.second.Offset < E2.second.Offset; |
| }); |
| // Writing out CU List <Offset, Size> |
| for (auto &CUInfo : CUVector) { |
| // Skipping TU for DWARF5 when they are not included in CU list. |
| if (!OriginalOffsets.count(CUInfo.first)) |
| continue; |
| write64le(Buffer, CUInfo.second.Offset); |
| // Length encoded in CU doesn't contain first 4 bytes that encode length. |
| write64le(Buffer + 8, CUInfo.second.Length + 4); |
| Buffer += 16; |
| } |
| |
| // Rewrite TU CU List, since abbrevs can be different. |
| // Entry example: |
| // 0: offset = 0x00000000, type_offset = 0x0000001e, type_signature = |
| // 0x418503b8111e9a7b Spec says " triplet, the first value is the CU offset, |
| // the second value is the type offset in the CU, and the third value is the |
| // type signature" Looking at what is being generated by gdb-add-index. The |
| // first entry is TU offset, second entry is offset from it, and third entry |
| // is the type signature. |
| if (TUListSize) |
| for (const GDBIndexTUEntry &Entry : getGDBIndexTUEntryVector()) { |
| write64le(Buffer, Entry.UnitOffset); |
| write64le(Buffer + 8, Entry.TypeDIERelativeOffset); |
| write64le(Buffer + 16, Entry.TypeHash); |
| Buffer += sizeof(GDBIndexTUEntry); |
| } |
| |
| // Generate new address table. |
| for (const std::pair<const uint64_t, DebugAddressRangesVector> &CURangesPair : |
| ARangesSectionWriter->getCUAddressRanges()) { |
| const uint32_t CUIndex = OffsetToIndexMap[CURangesPair.first]; |
| const DebugAddressRangesVector &Ranges = CURangesPair.second; |
| for (const DebugAddressRange &Range : Ranges) { |
| write64le(Buffer, Range.LowPC); |
| write64le(Buffer + 8, Range.HighPC); |
| write32le(Buffer + 16, CUIndex); |
| Buffer += 20; |
| } |
| } |
| |
| const size_t TrailingSize = |
| GdbIndexContents.data() + GdbIndexContents.size() - Data; |
| assert(Buffer + TrailingSize == NewGdbIndexContents + NewGdbIndexSize && |
| "size calculation error"); |
| |
| // Copy over the rest of the original data. |
| memcpy(Buffer, Data, TrailingSize); |
| |
| // Register the new section. |
| BC.registerOrUpdateNoteSection(".gdb_index", NewGdbIndexContents, |
| NewGdbIndexSize); |
| } |
| |
| std::unique_ptr<DebugBufferVector> |
| DWARFRewriter::makeFinalLocListsSection(DWARFVersion Version) { |
| 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 (std::pair<const uint64_t, std::unique_ptr<DebugLocWriter>> &Loc : |
| LocListWritersByCU) { |
| DebugLocWriter *LocWriter = Loc.second.get(); |
| auto *LocListWriter = llvm::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(); |
| *LocStream << *CurrCULocationLists; |
| } |
| |
| return LocBuffer; |
| } |
| |
| void DWARFRewriter::convertToRangesPatchDebugInfo( |
| DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die, |
| uint64_t RangesSectionOffset, DIEValue &LowPCAttrInfo, |
| DIEValue &HighPCAttrInfo, std::optional<uint64_t> RangesBase) { |
| uint32_t BaseOffset = 0; |
| 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 DWARF4 for DW_AT_low_pc in binary DW_FORM_addr is used. In the DWO |
| // section DW_FORM_GNU_addr_index is used. So for if we are converting |
| // DW_AT_low_pc/DW_AT_high_pc and see DW_FORM_GNU_addr_index. We are |
| // converting in DWO section, and DW_AT_ranges [DW_FORM_sec_offset] is |
| // relative to DW_AT_GNU_ranges_base. |
| if (LowForm == dwarf::DW_FORM_GNU_addr_index) { |
| // Ranges are relative to DW_AT_GNU_ranges_base. |
| uint64_t CurRangeBase = 0; |
| if (std::optional<uint64_t> DWOId = Unit.getDWOId()) { |
| CurRangeBase = getDwoRangesBase(*DWOId); |
| } |
| BaseOffset = CurRangeBase; |
| } else { |
| // 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 (LowForm == dwarf::DW_FORM_addrx) { |
| const uint32_t Index = AddrWriter->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) |
| DIEBldr.addValue(&Die, RangeBaseAttribute, dwarf::DW_FORM_sec_offset, |
| DIEInteger(*RangesBase)); |
| } |
| |
| uint64_t RangeAttrVal = RangesSectionOffset - BaseOffset; |
| if (Unit.getVersion() >= 5) |
| RangeAttrVal = RangesSectionOffset; |
| // HighPC was conveted into DW_AT_ranges. |
| // For DWARF5 we only access ranges through index. |
| |
| DIEBldr.replaceValue(&Die, HighPCAttrInfo.getAttribute(), dwarf::DW_AT_ranges, |
| RangesForm, DIEInteger(RangeAttrVal)); |
| } |