diff --git a/llvm/include/llvm/DWARFLinker/Classic/DWARFLinker.h b/llvm/include/llvm/DWARFLinker/Classic/DWARFLinker.h index b1d3f03394f5e..3608e7821bbc4 100644 --- a/llvm/include/llvm/DWARFLinker/Classic/DWARFLinker.h +++ b/llvm/include/llvm/DWARFLinker/Classic/DWARFLinker.h @@ -122,10 +122,13 @@ class DwarfEmitter { const AddressRanges &LinkedRanges) = 0; /// Emit specified \p LineTable into .debug_line table. - virtual void emitLineTableForUnit(const DWARFDebugLine::LineTable &LineTable, - const CompileUnit &Unit, - OffsetsStringPool &DebugStrPool, - OffsetsStringPool &DebugLineStrPool) = 0; + /// The optional parameter RowOffsets, if provided, will be populated with the + /// offsets of each line table row in the output .debug_line section. + virtual void + emitLineTableForUnit(const DWARFDebugLine::LineTable &LineTable, + const CompileUnit &Unit, OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool, + std::vector *RowOffsets = nullptr) = 0; /// Emit the .debug_pubnames contribution for \p Unit. virtual void emitPubNamesForUnit(const CompileUnit &Unit) = 0; diff --git a/llvm/include/llvm/DWARFLinker/Classic/DWARFLinkerCompileUnit.h b/llvm/include/llvm/DWARFLinker/Classic/DWARFLinkerCompileUnit.h index cdb6f4a4443ab..7106889d8ec76 100644 --- a/llvm/include/llvm/DWARFLinker/Classic/DWARFLinkerCompileUnit.h +++ b/llvm/include/llvm/DWARFLinker/Classic/DWARFLinkerCompileUnit.h @@ -57,6 +57,7 @@ struct PatchLocation { using RngListAttributesTy = SmallVector; using LocListAttributesTy = SmallVector; +using StmtSeqListAttributesTy = SmallVector; /// Stores all information relating to a compile unit, be it in its original /// instance in the object file to its brand new cloned and generated DIE tree. @@ -175,6 +176,12 @@ class CompileUnit { return LocationAttributes; } + // Provide access to the list of DW_AT_LLVM_stmt_sequence attributes that may + // need to be patched. + const StmtSeqListAttributesTy &getStmtSeqListAttributes() const { + return StmtSeqListAttributes; + } + /// Mark every DIE in this unit as kept. This function also /// marks variables as InDebugMap so that they appear in the /// reconstructed accelerator tables. @@ -210,6 +217,10 @@ class CompileUnit { /// debug_loc section. void noteLocationAttribute(PatchLocation Attr); + // Record that the given DW_AT_LLVM_stmt_sequence attribute may need to be + // patched later. + void noteStmtSeqListAttribute(PatchLocation Attr); + /// Add a name accelerator entry for \a Die with \a Name. void addNamespaceAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name); @@ -309,6 +320,12 @@ class CompileUnit { /// location expression. LocListAttributesTy LocationAttributes; + // List of DW_AT_LLVM_stmt_sequence attributes that may need to be patched + // after the dwarf linker rewrites the line table. During line table rewrite + // the line table format might change, so we have to patch any offsets that + // reference its contents. + StmtSeqListAttributesTy StmtSeqListAttributes; + /// Accelerator entries for the unit, both for the pub* /// sections and the apple* ones. /// @{ diff --git a/llvm/include/llvm/DWARFLinker/Classic/DWARFStreamer.h b/llvm/include/llvm/DWARFLinker/Classic/DWARFStreamer.h index e7a1a3cd838c2..40740a3f2210b 100644 --- a/llvm/include/llvm/DWARFLinker/Classic/DWARFStreamer.h +++ b/llvm/include/llvm/DWARFLinker/Classic/DWARFStreamer.h @@ -149,10 +149,13 @@ class DwarfStreamer : public DwarfEmitter { } /// Emit .debug_line table entry for specified \p LineTable - void emitLineTableForUnit(const DWARFDebugLine::LineTable &LineTable, - const CompileUnit &Unit, - OffsetsStringPool &DebugStrPool, - OffsetsStringPool &DebugLineStrPool) override; + /// The optional parameter RowOffsets, if provided, will be populated with the + /// offsets of each line table row in the output .debug_line section. + void + emitLineTableForUnit(const DWARFDebugLine::LineTable &LineTable, + const CompileUnit &Unit, OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool, + std::vector *RowOffsets = nullptr) override; uint64_t getLineSectionSize() const override { return LineSectionSize; } @@ -266,7 +269,8 @@ class DwarfStreamer : public DwarfEmitter { const DWARFDebugLine::Prologue &P, OffsetsStringPool &DebugStrPool, OffsetsStringPool &DebugLineStrPool); void emitLineTableRows(const DWARFDebugLine::LineTable &LineTable, - MCSymbol *LineEndSym, unsigned AddressByteSize); + MCSymbol *LineEndSym, unsigned AddressByteSize, + std::vector *RowOffsets = nullptr); void emitIntOffset(uint64_t Offset, dwarf::DwarfFormat Format, uint64_t &SectionSize); void emitLabelDifference(const MCSymbol *Hi, const MCSymbol *Lo, diff --git a/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp b/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp index d2b3561ee1c80..f66773ad2e694 100644 --- a/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp +++ b/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp @@ -1447,6 +1447,18 @@ unsigned DWARFLinker::DIECloner::cloneScalarAttribute( ->sizeOf(Unit.getOrigUnit().getFormParams()); } + if (AttrSpec.Attr == dwarf::DW_AT_LLVM_stmt_sequence) { + // If needed, we'll patch this sec_offset later with the correct offset. + auto Patch = Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::DW_FORM_sec_offset, + DIEInteger(*Val.getAsSectionOffset())); + + // Record this patch location so that it can be fixed up later. + Unit.noteStmtSeqListAttribute(Patch); + + return Unit.getOrigUnit().getFormParams().getDwarfOffsetByteSize(); + } + if (LLVM_UNLIKELY(Linker.Options.Update)) { if (auto OptionalValue = Val.getAsUnsignedConstant()) Value = *OptionalValue; @@ -2081,29 +2093,43 @@ void DWARFLinker::DIECloner::emitDebugAddrSection( Emitter->emitDwarfDebugAddrsFooter(Unit, EndLabel); } +/// A helper struct to help keep track of the association between the input and +/// output rows during line table rewriting. This is used to patch +/// DW_AT_LLVM_stmt_sequence attributes, which reference a particular line table +/// row. +struct TrackedRow { + DWARFDebugLine::Row Row; + size_t OriginalRowIndex; + bool isStartSeqInOutput; +}; + /// Insert the new line info sequence \p Seq into the current /// set of already linked line info \p Rows. -static void insertLineSequence(std::vector &Seq, - std::vector &Rows) { +static void insertLineSequence(std::vector &Seq, + std::vector &Rows) { if (Seq.empty()) return; - if (!Rows.empty() && Rows.back().Address < Seq.front().Address) { + // Mark the first row in Seq to indicate it is the start of a sequence + // in the output line table. + Seq.front().isStartSeqInOutput = true; + + if (!Rows.empty() && Rows.back().Row.Address < Seq.front().Row.Address) { llvm::append_range(Rows, Seq); Seq.clear(); return; } - object::SectionedAddress Front = Seq.front().Address; + object::SectionedAddress Front = Seq.front().Row.Address; auto InsertPoint = partition_point( - Rows, [=](const DWARFDebugLine::Row &O) { return O.Address < Front; }); + Rows, [=](const TrackedRow &O) { return O.Row.Address < Front; }); // FIXME: this only removes the unneeded end_sequence if the // sequences have been inserted in order. Using a global sort like - // described in generateLineTableForUnit() and delaying the end_sequene + // described in generateLineTableForUnit() and delaying the end_sequence // elimination to emitLineTableForUnit() we can get rid of all of them. - if (InsertPoint != Rows.end() && InsertPoint->Address == Front && - InsertPoint->EndSequence) { + if (InsertPoint != Rows.end() && InsertPoint->Row.Address == Front && + InsertPoint->Row.EndSequence) { *InsertPoint = Seq.front(); Rows.insert(InsertPoint + 1, Seq.begin() + 1, Seq.end()); } else { @@ -2171,22 +2197,32 @@ void DWARFLinker::DIECloner::generateLineTableForUnit(CompileUnit &Unit) { LineTable.Rows.clear(); LineTable.Sequences = LT->Sequences; + + Emitter->emitLineTableForUnit(LineTable, Unit, DebugStrPool, + DebugLineStrPool); } else { - // This vector is the output line table. - std::vector NewRows; - NewRows.reserve(LT->Rows.size()); + // Create TrackedRow objects for all input rows. + std::vector InputRows; + InputRows.reserve(LT->Rows.size()); + for (size_t i = 0; i < LT->Rows.size(); i++) + InputRows.emplace_back(TrackedRow{LT->Rows[i], i, false}); + + // This vector is the output line table (still in TrackedRow form). + std::vector OutputRows; + OutputRows.reserve(InputRows.size()); // Current sequence of rows being extracted, before being inserted - // in NewRows. - std::vector Seq; + // in OutputRows. + std::vector Seq; + Seq.reserve(InputRows.size()); const auto &FunctionRanges = Unit.getFunctionRanges(); std::optional CurrRange; // FIXME: This logic is meant to generate exactly the same output as // Darwin's classic dsymutil. There is a nicer way to implement this - // by simply putting all the relocated line info in NewRows and simply - // sorting NewRows before passing it to emitLineTableForUnit. This + // by simply putting all the relocated line info in OutputRows and simply + // sorting OutputRows before passing it to emitLineTableForUnit. This // should be correct as sequences for a function should stay // together in the sorted output. There are a few corner cases that // look suspicious though, and that required to implement the logic @@ -2194,29 +2230,32 @@ void DWARFLinker::DIECloner::generateLineTableForUnit(CompileUnit &Unit) { // Iterate over the object file line info and extract the sequences // that correspond to linked functions. - for (DWARFDebugLine::Row Row : LT->Rows) { + for (size_t i = 0; i < InputRows.size(); i++) { + TrackedRow TR = InputRows[i]; + // Check whether we stepped out of the range. The range is - // half-open, but consider accept the end address of the range if + // half-open, but consider accepting the end address of the range if // it is marked as end_sequence in the input (because in that // case, the relocation offset is accurate and that entry won't // serve as the start of another function). - if (!CurrRange || !CurrRange->Range.contains(Row.Address.Address)) { - // We just stepped out of a known range. Insert a end_sequence + if (!CurrRange || !CurrRange->Range.contains(TR.Row.Address.Address)) { + // We just stepped out of a known range. Insert an end_sequence // corresponding to the end of the range. uint64_t StopAddress = CurrRange ? CurrRange->Range.end() + CurrRange->Value : -1ULL; - CurrRange = FunctionRanges.getRangeThatContains(Row.Address.Address); + CurrRange = + FunctionRanges.getRangeThatContains(TR.Row.Address.Address); if (StopAddress != -1ULL && !Seq.empty()) { // Insert end sequence row with the computed end address, but // the same line as the previous one. auto NextLine = Seq.back(); - NextLine.Address.Address = StopAddress; - NextLine.EndSequence = 1; - NextLine.PrologueEnd = 0; - NextLine.BasicBlock = 0; - NextLine.EpilogueBegin = 0; + NextLine.Row.Address.Address = StopAddress; + NextLine.Row.EndSequence = 1; + NextLine.Row.PrologueEnd = 0; + NextLine.Row.BasicBlock = 0; + NextLine.Row.EpilogueBegin = 0; Seq.push_back(NextLine); - insertLineSequence(Seq, NewRows); + insertLineSequence(Seq, OutputRows); } if (!CurrRange) @@ -2224,22 +2263,78 @@ void DWARFLinker::DIECloner::generateLineTableForUnit(CompileUnit &Unit) { } // Ignore empty sequences. - if (Row.EndSequence && Seq.empty()) + if (TR.Row.EndSequence && Seq.empty()) continue; // Relocate row address and add it to the current sequence. - Row.Address.Address += CurrRange->Value; - Seq.emplace_back(Row); + TR.Row.Address.Address += CurrRange->Value; + Seq.push_back(TR); - if (Row.EndSequence) - insertLineSequence(Seq, NewRows); + if (TR.Row.EndSequence) + insertLineSequence(Seq, OutputRows); } - LineTable.Rows = std::move(NewRows); + // Materialize the tracked rows into final DWARFDebugLine::Row objects. + LineTable.Rows.clear(); + LineTable.Rows.reserve(OutputRows.size()); + for (auto &TR : OutputRows) + LineTable.Rows.push_back(TR.Row); + + // Use OutputRowOffsets to store the offsets of each line table row in the + // output .debug_line section. + std::vector OutputRowOffsets; + + // The unit might not have any DW_AT_LLVM_stmt_sequence attributes, so use + // hasStmtSeq to skip the patching logic. + bool hasStmtSeq = Unit.getStmtSeqListAttributes().size() > 0; + Emitter->emitLineTableForUnit(LineTable, Unit, DebugStrPool, + DebugLineStrPool, + hasStmtSeq ? &OutputRowOffsets : nullptr); + + if (hasStmtSeq) { + assert(OutputRowOffsets.size() == OutputRows.size() && + "must have an offset for each row"); + + // Create a map of stmt sequence offsets to original row indices. + DenseMap SeqOffToOrigRow; + for (const DWARFDebugLine::Sequence &Seq : LT->Sequences) + SeqOffToOrigRow[Seq.StmtSeqOffset] = Seq.FirstRowIndex; + + // Create a map of original row indices to new row indices. + DenseMap OrigRowToNewRow; + for (size_t i = 0; i < OutputRows.size(); ++i) + OrigRowToNewRow[OutputRows[i].OriginalRowIndex] = i; + + // Patch DW_AT_LLVM_stmt_sequence attributes in the compile unit DIE + // with the correct offset into the .debug_line section. + for (const auto &StmtSeq : Unit.getStmtSeqListAttributes()) { + uint64_t OrigStmtSeq = StmtSeq.get(); + // 1. Get the original row index from the stmt list offset. + auto OrigRowIter = SeqOffToOrigRow.find(OrigStmtSeq); + assert(OrigRowIter != SeqOffToOrigRow.end() && + "Stmt list offset not found in sequence offsets map"); + size_t OrigRowIndex = OrigRowIter->second; + + // 2. Get the new row index from the original row index. + auto NewRowIter = OrigRowToNewRow.find(OrigRowIndex); + if (NewRowIter == OrigRowToNewRow.end()) { + // If the original row index is not found in the map, update the + // stmt_sequence attribute to the 'invalid offset' magic value. + StmtSeq.set(UINT64_MAX); + continue; + } + + // 3. Get the offset of the new row in the output .debug_line section. + assert(NewRowIter->second < OutputRowOffsets.size() && + "New row index out of bounds"); + uint64_t NewStmtSeqOffset = OutputRowOffsets[NewRowIter->second]; + + // 4. Patch the stmt_list attribute with the new offset. + StmtSeq.set(NewStmtSeqOffset); + } + } } - Emitter->emitLineTableForUnit(LineTable, Unit, DebugStrPool, - DebugLineStrPool); } else Linker.reportWarning("Cann't load line table.", ObjFile); } diff --git a/llvm/lib/DWARFLinker/Classic/DWARFLinkerCompileUnit.cpp b/llvm/lib/DWARFLinker/Classic/DWARFLinkerCompileUnit.cpp index 1eb3a70a55135..66bf158e60f1d 100644 --- a/llvm/lib/DWARFLinker/Classic/DWARFLinkerCompileUnit.cpp +++ b/llvm/lib/DWARFLinker/Classic/DWARFLinkerCompileUnit.cpp @@ -185,6 +185,10 @@ void CompileUnit::noteLocationAttribute(PatchLocation Attr) { LocationAttributes.emplace_back(Attr); } +void CompileUnit::noteStmtSeqListAttribute(PatchLocation Attr) { + StmtSeqListAttributes.emplace_back(Attr); +} + void CompileUnit::addNamespaceAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name) { Namespaces.emplace_back(Name, Die); diff --git a/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp b/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp index 55e40cd779cbf..d4f62d351548e 100644 --- a/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp +++ b/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp @@ -809,7 +809,8 @@ void DwarfStreamer::emitDwarfDebugLocListsTableFragment( void DwarfStreamer::emitLineTableForUnit( const DWARFDebugLine::LineTable &LineTable, const CompileUnit &Unit, - OffsetsStringPool &DebugStrPool, OffsetsStringPool &DebugLineStrPool) { + OffsetsStringPool &DebugStrPool, OffsetsStringPool &DebugLineStrPool, + std::vector *RowOffsets) { // Switch to the section where the table will be emitted into. MS->switchSection(MC->getObjectFileInfo()->getDwarfLineSection()); @@ -830,7 +831,7 @@ void DwarfStreamer::emitLineTableForUnit( // Emit rows. emitLineTableRows(LineTable, LineEndSym, - Unit.getOrigUnit().getAddressByteSize()); + Unit.getOrigUnit().getAddressByteSize(), RowOffsets); } void DwarfStreamer::emitLineTablePrologue(const DWARFDebugLine::Prologue &P, @@ -1036,7 +1037,7 @@ void DwarfStreamer::emitLineTableProloguePayload( void DwarfStreamer::emitLineTableRows( const DWARFDebugLine::LineTable &LineTable, MCSymbol *LineEndSym, - unsigned AddressByteSize) { + unsigned AddressByteSize, std::vector *RowOffsets) { MCDwarfLineTableParams Params; Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase; @@ -1068,6 +1069,11 @@ void DwarfStreamer::emitLineTableRows( unsigned RowsSinceLastSequence = 0; for (const DWARFDebugLine::Row &Row : LineTable.Rows) { + // If we're tracking row offsets, record the current section size as the + // offset of this row. + if (RowOffsets) + RowOffsets->push_back(LineSectionSize); + int64_t AddressDelta; if (Address == -1ULL) { MS->emitIntValue(dwarf::DW_LNS_extended_op, 1); diff --git a/llvm/test/tools/dsymutil/ARM/stmt-seq-macho.test b/llvm/test/tools/dsymutil/ARM/stmt-seq-macho.test new file mode 100644 index 0000000000000..b5093ba767894 --- /dev/null +++ b/llvm/test/tools/dsymutil/ARM/stmt-seq-macho.test @@ -0,0 +1,74 @@ +RUN: dsymutil --flat -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/stmt_seq/stmt_seq_macho.exe -o %t.stmt_seq_macho.dSYM +RUN: llvm-dwarfdump --debug-info --debug-line -v %t.stmt_seq_macho.dSYM | sort | FileCheck %s -check-prefix=CHECK_DSYM + +# CHECK_DSYM: DW_AT_LLVM_stmt_sequence [DW_FORM_sec_offset] ([[OFFSET1:(0x[0-9a-f]+)]]) +# CHECK_DSYM: DW_AT_LLVM_stmt_sequence [DW_FORM_sec_offset] ([[OFFSET2:(0x[0-9a-f]+)]]) +# CHECK_DSYM: DW_AT_LLVM_stmt_sequence [DW_FORM_sec_offset] ([[OFFSET3:(0x[0-9a-f]+)]]) +# CHECK_DSYM: DW_AT_LLVM_stmt_sequence [DW_FORM_sec_offset] ([[OFFSET4:(0x[0-9a-f]+)]]) + +# CHECK_DSYM: [[OFFSET1]]: 00 DW_LNE_set_address +# CHECK_DSYM: [[OFFSET2]]: 00 DW_LNE_set_address +# CHECK_DSYM: [[OFFSET3]]: 00 DW_LNE_set_address +# CHECK_DSYM: [[OFFSET4]]: 00 DW_LNE_set_address + + +######## Generate stmt_seq_macho.exe & stmt_seq_macho.o via script: ########## +# ------------------------------------------------------------------------------ +#!/bin/bash +TOOLCHAIN=/path/to/llvm/bin + +# ------------------------------------------------------------------------------ +# Create the stmt_seq_macho.cpp source file +# ------------------------------------------------------------------------------ +cat > stmt_seq_macho.cpp << 'EOF' +#define ATTRIB extern "C" __attribute__((noinline)) + +ATTRIB int function3_copy1(int a) { + int b = a + 3; + return b + 1; +} + +ATTRIB int function2_copy1(int a) { + return a - 22; +} + +ATTRIB int function3_copy2(int a) { + int b = a + 3; + return b + 1; +} + +ATTRIB int function2_copy2(int a) { + int result = a - 22; + return result; +} + +int main() { + int sum = 0; + sum += function2_copy2(3); + sum += function3_copy2(41); + sum += function2_copy1(11); + return sum; +} +EOF + +"$TOOLCHAIN/clang" \ + --target=arm64-apple-macos11 \ + -c \ + -g \ + -gdwarf-4 \ + -fno-unwind-tables \ + -mllvm -emit-func-debug-line-table-offsets \ + -fno-exceptions \ + -mno-outline \ + -Oz \ + stmt_seq_macho.cpp \ + -o stmt_seq_macho.o + +"$TOOLCHAIN/ld64.lld" \ + -arch arm64 \ + -platform_version macos 11.0.0 11.0.0 \ + -o stmt_seq_macho.exe \ + stmt_seq_macho.o \ + -dead_strip \ + --icf=all \ + --keep-icf-stabs diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/stmt_seq/stmt_seq_macho.exe b/llvm/test/tools/dsymutil/Inputs/private/tmp/stmt_seq/stmt_seq_macho.exe new file mode 100755 index 0000000000000..138c418aa37b2 Binary files /dev/null and b/llvm/test/tools/dsymutil/Inputs/private/tmp/stmt_seq/stmt_seq_macho.exe differ diff --git a/llvm/test/tools/dsymutil/Inputs/private/tmp/stmt_seq/stmt_seq_macho.o b/llvm/test/tools/dsymutil/Inputs/private/tmp/stmt_seq/stmt_seq_macho.o new file mode 100644 index 0000000000000..0da06940a023c Binary files /dev/null and b/llvm/test/tools/dsymutil/Inputs/private/tmp/stmt_seq/stmt_seq_macho.o differ