diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinker.h b/llvm/include/llvm/DWARFLinker/DWARFLinker.h --- a/llvm/include/llvm/DWARFLinker/DWARFLinker.h +++ b/llvm/include/llvm/DWARFLinker/DWARFLinker.h @@ -25,7 +25,6 @@ class DWARFUnit; class DataExtractor; class DeclContextTree; -struct MCDwarfLineTableParams; template class SmallVectorImpl; enum class DwarfLinkerClient { Dsymutil, LLD, General }; @@ -100,9 +99,12 @@ emitAbbrevs(const std::vector> &Abbrevs, unsigned DwarfVersion) = 0; - /// Emit the string table described by \p Pool. + /// Emit the string table described by \p Pool into .debug_str table. virtual void emitStrings(const NonRelocatableStringpool &Pool) = 0; + /// Emit the string table described by \p Pool into .debug_line_str table. + virtual void emitLineStrings(const NonRelocatableStringpool &Pool) = 0; + /// Emit DWARF debug names. virtual void emitDebugNames(AccelTable &Table) = 0; @@ -154,16 +156,11 @@ emitDwarfDebugArangesTable(const CompileUnit &Unit, const AddressRanges &LinkedRanges) = 0; - /// Copy the .debug_line over to the updated binary while unobfuscating the - /// file names and directories. - virtual void translateLineTable(DataExtractor LineData, uint64_t Offset) = 0; - - /// Emit the line table described in \p Rows into the .debug_line section. - virtual void emitLineTableForUnit(MCDwarfLineTableParams Params, - StringRef PrologueBytes, - unsigned MinInstLength, - std::vector &Rows, - unsigned AdddressSize) = 0; + /// Emit specified \p LineTable into .debug_line table. + virtual void emitLineTableForUnit(const DWARFDebugLine::LineTable &LineTable, + const CompileUnit &Unit, + OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool) = 0; /// Emit the .debug_pubnames contribution for \p Unit. virtual void emitPubNamesForUnit(const CompileUnit &Unit) = 0; @@ -559,7 +556,8 @@ /// Clone specified Clang module unit \p Unit. Error cloneModuleUnit(LinkContext &Context, RefModuleUnit &Unit, DeclContextTree &ODRContexts, - OffsetsStringPool &OffsetsStringPool, + OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool, unsigned Indent = 0); /// Mark the passed DIE as well as all the ones it depends on as kept. @@ -605,6 +603,8 @@ DWARFLinker &Linker; DwarfEmitter *Emitter; DWARFFile &ObjFile; + OffsetsStringPool &DebugStrPool; + OffsetsStringPool &DebugLineStrPool; /// Allocator used for all the DIEValue objects. BumpPtrAllocator &DIEAlloc; @@ -621,8 +621,10 @@ DIECloner(DWARFLinker &Linker, DwarfEmitter *Emitter, DWARFFile &ObjFile, BumpPtrAllocator &DIEAlloc, std::vector> &CompileUnits, - bool Update) + bool Update, OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool) : Linker(Linker), Emitter(Emitter), ObjFile(ObjFile), + DebugStrPool(DebugStrPool), DebugLineStrPool(DebugLineStrPool), DIEAlloc(DIEAlloc), CompileUnits(CompileUnits), Update(Update) {} /// Recursively clone \p InputDIE into an tree of DIE objects @@ -637,17 +639,14 @@ /// \param Die the output DIE to use, pass NULL to create one. /// \returns the root of the cloned tree or null if nothing was selected. DIE *cloneDIE(const DWARFDie &InputDIE, const DWARFFile &File, - CompileUnit &U, OffsetsStringPool &StringPool, - int64_t PCOffset, uint32_t OutOffset, unsigned Flags, - bool IsLittleEndian, DIE *Die = nullptr); + CompileUnit &U, int64_t PCOffset, uint32_t OutOffset, + unsigned Flags, bool IsLittleEndian, DIE *Die = nullptr); /// Construct the output DIE tree by cloning the DIEs we /// chose to keep above. If there are no valid relocs, then there's /// nothing to clone/emit. uint64_t cloneAllCompileUnits(DWARFContext &DwarfContext, - const DWARFFile &File, - OffsetsStringPool &StringPool, - bool IsLittleEndian); + const DWARFFile &File, bool IsLittleEndian); private: using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec; @@ -680,7 +679,6 @@ /// Helper for cloneDIE. unsigned cloneAttribute(DIE &Die, const DWARFDie &InputDIE, const DWARFFile &File, CompileUnit &U, - OffsetsStringPool &StringPool, const DWARFFormValue &Val, const AttributeSpec AttrSpec, unsigned AttrSize, AttributesInfo &AttrInfo, bool IsLittleEndian); @@ -690,7 +688,6 @@ /// \returns the size of the new attribute. unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, const DWARFUnit &U, - OffsetsStringPool &StringPool, AttributesInfo &Info); /// Clone an attribute referencing another DIE and add @@ -750,6 +747,11 @@ OffsetsStringPool &StringPool, bool SkipPubSection); void rememberUnitForMacroOffset(CompileUnit &Unit); + + /// Clone and emit the line table for the specified \p Unit. + /// Translate directories and file names if necessary. + /// Relocate address ranges. + void generateLineTableForUnit(CompileUnit &Unit); }; /// Assign an abbreviation number to \p Abbrev @@ -767,12 +769,6 @@ void generateUnitLocations(CompileUnit &Unit, const DWARFFile &File, ExpressionHandlerRef ExprHandler) const; - /// Extract the line tables from the original dwarf, extract the relevant - /// parts according to the linked function ranges and emit the result in the - /// .debug_line section. - void patchLineTableForUnit(CompileUnit &Unit, DWARFContext &OrigDwarf, - const DWARFFile &File); - /// Emit the accelerator entries for \p Unit. void emitAcceleratorEntriesForUnit(CompileUnit &Unit); diff --git a/llvm/include/llvm/DWARFLinker/DWARFStreamer.h b/llvm/include/llvm/DWARFLinker/DWARFStreamer.h --- a/llvm/include/llvm/DWARFLinker/DWARFStreamer.h +++ b/llvm/include/llvm/DWARFLinker/DWARFStreamer.h @@ -82,9 +82,12 @@ /// Emit contents of section SecName From Obj. void emitSectionContents(StringRef SecData, StringRef SecName) override; - /// Emit the string table described by \p Pool. + /// Emit the string table described by \p Pool into .debug_str table. void emitStrings(const NonRelocatableStringpool &Pool) override; + /// Emit the string table described by \p Pool into .debug_line_str table. + void emitLineStrings(const NonRelocatableStringpool &Pool) override; + /// Emit the swift_ast section stored in \p Buffer. void emitSwiftAST(StringRef Buffer); @@ -128,15 +131,11 @@ return RngListsSectionSize; } - /// Emit the line table described in \p Rows into the debug_line section. - void emitLineTableForUnit(MCDwarfLineTableParams Params, - StringRef PrologueBytes, unsigned MinInstLength, - std::vector &Rows, - unsigned AdddressSize) override; - - /// Copy the debug_line over to the updated binary while unobfuscating the - /// file names and directories. - void translateLineTable(DataExtractor LineData, uint64_t Offset) override; + /// Emit .debug_line table entry for specified \p LineTable + void emitLineTableForUnit(const DWARFDebugLine::LineTable &LineTable, + const CompileUnit &Unit, + OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool) override; uint64_t getLineSectionSize() const override { return LineSectionSize; } @@ -231,6 +230,32 @@ const DWARFLocationExpressionsVector &LinkedLocationExpression, PatchLocation Patch); + /// \defgroup Line table emission + /// @{ + void emitLineTablePrologue(const DWARFDebugLine::Prologue &P, + OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool); + void emitLineTableString(const DWARFDebugLine::Prologue &P, + const DWARFFormValue &String, + OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool); + void emitLineTableProloguePayload(const DWARFDebugLine::Prologue &P, + OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool); + void emitLineTablePrologueV2IncludeAndFileTable( + const DWARFDebugLine::Prologue &P, OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool); + void emitLineTablePrologueV5IncludeAndFileTable( + const DWARFDebugLine::Prologue &P, OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool); + void emitLineTableRows(const DWARFDebugLine::LineTable &LineTable, + MCSymbol *LineEndSym, unsigned AddressByteSize); + void emitIntOffset(uint64_t Offset, dwarf::DwarfFormat Format, + uint64_t &SectionSize); + void emitLabelDifference(const MCSymbol *Hi, const MCSymbol *Lo, + dwarf::DwarfFormat Format, uint64_t &SectionSize); + /// @} + /// \defgroup MCObjects MC layer objects constructed by the streamer /// @{ std::unique_ptr MRI; diff --git a/llvm/lib/DWARFLinker/DWARFLinker.cpp b/llvm/lib/DWARFLinker/DWARFLinker.cpp --- a/llvm/lib/DWARFLinker/DWARFLinker.cpp +++ b/llvm/lib/DWARFLinker/DWARFLinker.cpp @@ -948,24 +948,33 @@ } } -unsigned DWARFLinker::DIECloner::cloneStringAttribute( - DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, - const DWARFUnit &, OffsetsStringPool &StringPool, AttributesInfo &Info) { +unsigned DWARFLinker::DIECloner::cloneStringAttribute(DIE &Die, + AttributeSpec AttrSpec, + const DWARFFormValue &Val, + const DWARFUnit &, + AttributesInfo &Info) { std::optional String = dwarf::toString(Val); if (!String) return 0; - // Switch everything to out of line strings. - auto StringEntry = StringPool.getEntry(*String); + DwarfStringPoolEntryRef StringEntry; + if (AttrSpec.Form == dwarf::DW_FORM_line_strp) { + StringEntry = DebugLineStrPool.getEntry(*String); + } else { + StringEntry = DebugStrPool.getEntry(*String); + + // Update attributes info. + if (AttrSpec.Attr == dwarf::DW_AT_name) + Info.Name = StringEntry; + else if (AttrSpec.Attr == dwarf::DW_AT_MIPS_linkage_name || + AttrSpec.Attr == dwarf::DW_AT_linkage_name) + Info.MangledName = StringEntry; - // Update attributes info. - if (AttrSpec.Attr == dwarf::DW_AT_name) - Info.Name = StringEntry; - else if (AttrSpec.Attr == dwarf::DW_AT_MIPS_linkage_name || - AttrSpec.Attr == dwarf::DW_AT_linkage_name) - Info.MangledName = StringEntry; + // Switch everything to out of line strings. + AttrSpec.Form = dwarf::DW_FORM_strp; + } - Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp, + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), AttrSpec.Form, DIEInteger(StringEntry.getOffset())); return 4; @@ -1373,20 +1382,20 @@ /// \returns the size of the cloned attribute. unsigned DWARFLinker::DIECloner::cloneAttribute( DIE &Die, const DWARFDie &InputDIE, const DWARFFile &File, - CompileUnit &Unit, OffsetsStringPool &StringPool, const DWARFFormValue &Val, - const AttributeSpec AttrSpec, unsigned AttrSize, AttributesInfo &Info, - bool IsLittleEndian) { + CompileUnit &Unit, const DWARFFormValue &Val, const AttributeSpec AttrSpec, + unsigned AttrSize, AttributesInfo &Info, bool IsLittleEndian) { const DWARFUnit &U = Unit.getOrigUnit(); switch (AttrSpec.Form) { case dwarf::DW_FORM_strp: + case dwarf::DW_FORM_line_strp: case dwarf::DW_FORM_string: case dwarf::DW_FORM_strx: case dwarf::DW_FORM_strx1: case dwarf::DW_FORM_strx2: case dwarf::DW_FORM_strx3: case dwarf::DW_FORM_strx4: - return cloneStringAttribute(Die, AttrSpec, Val, U, StringPool, Info); + return cloneStringAttribute(Die, AttrSpec, Val, U, Info); case dwarf::DW_FORM_ref_addr: case dwarf::DW_FORM_ref1: case dwarf::DW_FORM_ref2: @@ -1524,7 +1533,6 @@ DIE *DWARFLinker::DIECloner::cloneDIE(const DWARFDie &InputDIE, const DWARFFile &File, CompileUnit &Unit, - OffsetsStringPool &StringPool, int64_t PCOffset, uint32_t OutOffset, unsigned Flags, bool IsLittleEndian, DIE *Die) { @@ -1614,8 +1622,8 @@ Val.extractValue(Data, &Offset, U.getFormParams(), &U); AttrSize = Offset - AttrSize; - OutOffset += cloneAttribute(*Die, InputDIE, File, Unit, StringPool, Val, - AttrSpec, AttrSize, AttrInfo, IsLittleEndian); + OutOffset += cloneAttribute(*Die, InputDIE, File, Unit, Val, AttrSpec, + AttrSize, AttrInfo, IsLittleEndian); } // Look for accelerator entries. @@ -1625,7 +1633,7 @@ // accelerator tables too. For now stick with dsymutil's behavior. if ((Info.InDebugMap || AttrInfo.HasLowPc || AttrInfo.HasRanges) && Tag != dwarf::DW_TAG_compile_unit && - getDIENames(InputDIE, AttrInfo, StringPool, + getDIENames(InputDIE, AttrInfo, DebugStrPool, Tag != dwarf::DW_TAG_inlined_subroutine)) { if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name) Unit.addNameAccelerator(Die, AttrInfo.MangledName, @@ -1638,17 +1646,17 @@ Tag == dwarf::DW_TAG_inlined_subroutine); } if (AttrInfo.Name && isObjCSelector(AttrInfo.Name.getString())) - addObjCAccelerator(Unit, Die, AttrInfo.Name, StringPool, + addObjCAccelerator(Unit, Die, AttrInfo.Name, DebugStrPool, /* SkipPubSection =*/true); } else if (Tag == dwarf::DW_TAG_namespace) { if (!AttrInfo.Name) - AttrInfo.Name = StringPool.getEntry("(anonymous namespace)"); + AttrInfo.Name = DebugStrPool.getEntry("(anonymous namespace)"); Unit.addNamespaceAccelerator(Die, AttrInfo.Name); } else if (Tag == dwarf::DW_TAG_imported_declaration && AttrInfo.Name) { Unit.addNamespaceAccelerator(Die, AttrInfo.Name); } else if (isTypeTag(Tag) && !AttrInfo.IsDeclaration && - getDIENames(InputDIE, AttrInfo, StringPool) && AttrInfo.Name && + getDIENames(InputDIE, AttrInfo, DebugStrPool) && AttrInfo.Name && AttrInfo.Name.getString()[0]) { uint32_t Hash = hashFullyQualifiedName(InputDIE, Unit, File); uint64_t RuntimeLang = @@ -1691,8 +1699,8 @@ // Recursively clone children. for (auto Child : InputDIE.children()) { - if (DIE *Clone = cloneDIE(Child, File, Unit, StringPool, PCOffset, - OutOffset, Flags, IsLittleEndian)) { + if (DIE *Clone = cloneDIE(Child, File, Unit, PCOffset, OutOffset, Flags, + IsLittleEndian)) { Die->addChild(Clone); OutOffset = Clone->getOffset() + Clone->getSize(); } @@ -1850,7 +1858,7 @@ // FIXME: this only removes the unneeded end_sequence if the // sequences have been inserted in order. Using a global sort like - // described in patchLineTableForUnit() and delaying the end_sequene + // described in generateLineTableForUnit() and delaying the end_sequene // elimination to emitLineTableForUnit() we can get rid of all of them. if (InsertPoint != Rows.end() && InsertPoint->Address == Front && InsertPoint->EndSequence) { @@ -1873,142 +1881,6 @@ llvm_unreachable("Didn't find DW_AT_stmt_list in cloned DIE!"); } -/// Extract the line table for \p Unit from \p OrigDwarf, and -/// recreate a relocated version of these for the address ranges that -/// are present in the binary. -void DWARFLinker::patchLineTableForUnit(CompileUnit &Unit, - DWARFContext &OrigDwarf, - const DWARFFile &File) { - DWARFDie CUDie = Unit.getOrigUnit().getUnitDIE(); - auto StmtList = dwarf::toSectionOffset(CUDie.find(dwarf::DW_AT_stmt_list)); - if (!StmtList) - return; - - // Update the cloned DW_AT_stmt_list with the correct debug_line offset. - if (auto *OutputDIE = Unit.getOutputUnitDIE()) - patchStmtList(*OutputDIE, - DIEInteger(TheDwarfEmitter->getLineSectionSize())); - - RangesTy &Ranges = File.Addresses->getValidAddressRanges(); - - // Parse the original line info for the unit. - DWARFDebugLine::LineTable LineTable; - uint64_t StmtOffset = *StmtList; - DWARFDataExtractor LineExtractor( - OrigDwarf.getDWARFObj(), OrigDwarf.getDWARFObj().getLineSection(), - OrigDwarf.isLittleEndian(), Unit.getOrigUnit().getAddressByteSize()); - if (needToTranslateStrings()) - return TheDwarfEmitter->translateLineTable(LineExtractor, StmtOffset); - - if (Error Err = - LineTable.parse(LineExtractor, &StmtOffset, OrigDwarf, - &Unit.getOrigUnit(), OrigDwarf.getWarningHandler())) - OrigDwarf.getWarningHandler()(std::move(Err)); - - // This vector is the output line table. - std::vector NewRows; - NewRows.reserve(LineTable.Rows.size()); - - // Current sequence of rows being extracted, before being inserted - // in NewRows. - std::vector Seq; - 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 - // 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 - // this way. Revisit that once initial validation is finished. - - // Iterate over the object file line info and extract the sequences - // that correspond to linked functions. - for (auto &Row : LineTable.Rows) { - // Check whether we stepped out of the range. The range is - // half-open, but consider accept 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) || - (Row.Address.Address == CurrRange->Range.end() && !Row.EndSequence)) { - // We just stepped out of a known range. Insert a 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); - if (!CurrRange) { - if (StopAddress != -1ULL) { - // Try harder by looking in the Address ranges map. - // There are corner cases where this finds a - // valid entry. It's unclear if this is right or wrong, but - // for now do as dsymutil. - // FIXME: Understand exactly what cases this addresses and - // potentially remove it along with the Ranges map. - if (std::optional Range = - Ranges.getRangeThatContains(Row.Address.Address)) - StopAddress = Row.Address.Address + (*Range).Value; - } - } - 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; - Seq.push_back(NextLine); - insertLineSequence(Seq, NewRows); - } - - if (!CurrRange) - continue; - } - - // Ignore empty sequences. - if (Row.EndSequence && Seq.empty()) - continue; - - // Relocate row address and add it to the current sequence. - Row.Address.Address += CurrRange->Value; - Seq.emplace_back(Row); - - if (Row.EndSequence) - insertLineSequence(Seq, NewRows); - } - - // Finished extracting, now emit the line tables. - // FIXME: LLVM hard-codes its prologue values. We just copy the - // prologue over and that works because we act as both producer and - // consumer. It would be nicer to have a real configurable line - // table emitter. - if (LineTable.Prologue.getVersion() < 2 || - LineTable.Prologue.getVersion() > 5 || - LineTable.Prologue.DefaultIsStmt != DWARF2_LINE_DEFAULT_IS_STMT || - LineTable.Prologue.OpcodeBase > 13) - reportWarning("line table parameters mismatch. Cannot emit.", File); - else { - uint32_t PrologueEnd = *StmtList + 10 + LineTable.Prologue.PrologueLength; - // DWARF v5 has an extra 2 bytes of information before the header_length - // field. - if (LineTable.Prologue.getVersion() == 5) - PrologueEnd += 2; - StringRef LineData = OrigDwarf.getDWARFObj().getLineSection().Data; - MCDwarfLineTableParams Params; - Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase; - Params.DWARF2LineBase = LineTable.Prologue.LineBase; - Params.DWARF2LineRange = LineTable.Prologue.LineRange; - TheDwarfEmitter->emitLineTableForUnit( - Params, LineData.slice(*StmtList + 4, PrologueEnd), - LineTable.Prologue.MinInstLength, NewRows, - Unit.getOrigUnit().getAddressByteSize()); - } -} - void DWARFLinker::DIECloner::rememberUnitForMacroOffset(CompileUnit &Unit) { DWARFUnit &OrigUnit = Unit.getOrigUnit(); DWARFDie OrigUnitDie = OrigUnit.getUnitDIE(); @@ -2026,6 +1898,123 @@ } } +void DWARFLinker::DIECloner::generateLineTableForUnit(CompileUnit &Unit) { + if (LLVM_UNLIKELY(Linker.Options.NoOutput)) + return; + + // Check whether DW_AT_stmt_list attribute is presented. + DWARFDie CUDie = Unit.getOrigUnit().getUnitDIE(); + auto StmtList = dwarf::toSectionOffset(CUDie.find(dwarf::DW_AT_stmt_list)); + if (!StmtList) + return; + + // Update the cloned DW_AT_stmt_list with the correct debug_line offset. + if (auto *OutputDIE = Unit.getOutputUnitDIE()) + patchStmtList(*OutputDIE, DIEInteger(Emitter->getLineSectionSize())); + + if (const DWARFDebugLine::LineTable *LT = + ObjFile.Dwarf->getLineTableForUnit(&Unit.getOrigUnit())) { + + DWARFDebugLine::LineTable LineTable; + + // Set Line Table header. + LineTable.Prologue = LT->Prologue; + + // Set Line Table Rows. + if (Linker.Options.Update) { + LineTable.Rows = LT->Rows; + + LineTable.Sequences = LT->Sequences; + } else { + RangesTy &Ranges = ObjFile.Addresses->getValidAddressRanges(); + + // This vector is the output line table. + std::vector NewRows; + NewRows.reserve(LT->Rows.size()); + + // Current sequence of rows being extracted, before being inserted + // in NewRows. + std::vector Seq; + + 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 + // 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 + // this way. Revisit that once initial validation is finished. + + // Iterate over the object file line info and extract the sequences + // that correspond to linked functions. + for (DWARFDebugLine::Row Row : LT->Rows) { + // Check whether we stepped out of the range. The range is + // half-open, but consider accept 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) || + (Row.Address.Address == CurrRange->Range.end() && + !Row.EndSequence)) { + // We just stepped out of a known range. Insert a 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); + if (!CurrRange) { + if (StopAddress != -1ULL) { + // Try harder by looking in the Address ranges map. + // There are corner cases where this finds a + // valid entry. It's unclear if this is right or wrong, but + // for now do as dsymutil. + // FIXME: Understand exactly what cases this addresses and + // potentially remove it along with the Ranges map. + if (std::optional Range = + Ranges.getRangeThatContains(Row.Address.Address)) + StopAddress = Row.Address.Address + (*Range).Value; + } + } + 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; + Seq.push_back(NextLine); + insertLineSequence(Seq, NewRows); + } + + if (!CurrRange) + continue; + } + + // Ignore empty sequences. + if (Row.EndSequence && Seq.empty()) + continue; + + // Relocate row address and add it to the current sequence. + Row.Address.Address += CurrRange->Value; + Seq.emplace_back(Row); + + if (Row.EndSequence) + insertLineSequence(Seq, NewRows); + } + + LineTable.Rows = std::move(NewRows); + } + + Emitter->emitLineTableForUnit(LineTable, Unit, DebugStrPool, + DebugLineStrPool); + } else + Linker.reportWarning("Cann't load line table.", ObjFile); +} + void DWARFLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) { for (DwarfLinkerAccelTableKind AccelTableKind : Options.AccelTables) { switch (AccelTableKind) { @@ -2372,8 +2361,7 @@ } uint64_t DWARFLinker::DIECloner::cloneAllCompileUnits( - DWARFContext &DwarfContext, const DWARFFile &File, - OffsetsStringPool &StringPool, bool IsLittleEndian) { + DWARFContext &DwarfContext, const DWARFFile &File, bool IsLittleEndian) { uint64_t OutputDebugInfoSize = Linker.Options.NoOutput ? 0 : Emitter->getDebugInfoSectionSize(); const uint64_t StartOutputDebugInfoSize = OutputDebugInfoSize; @@ -2392,9 +2380,8 @@ // already has a DIE inside of it. CurrentUnit->createOutputDIE(); rememberUnitForMacroOffset(*CurrentUnit); - cloneDIE(InputDIE, File, *CurrentUnit, StringPool, 0 /* PC offset */, - UnitHeaderSize, 0, IsLittleEndian, - CurrentUnit->getOutputUnitDIE()); + cloneDIE(InputDIE, File, *CurrentUnit, 0 /* PC offset */, UnitHeaderSize, + 0, IsLittleEndian, CurrentUnit->getOutputUnitDIE()); } OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(DwarfVersion); @@ -2402,9 +2389,7 @@ if (!Linker.Options.NoOutput) { assert(Emitter); - if (LLVM_LIKELY(!Linker.Options.Update) || - Linker.needToTranslateStrings()) - Linker.patchLineTableForUnit(*CurrentUnit, DwarfContext, File); + generateLineTableForUnit(*CurrentUnit); Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); @@ -2430,7 +2415,7 @@ if (!Linker.Options.NoOutput) { assert(Emitter); // Emit macro tables. - Emitter->emitMacroTables(File.Dwarf, UnitMacroMap, StringPool); + Emitter->emitMacroTables(File.Dwarf, UnitMacroMap, DebugStrPool); // Emit all the compile unit's debug information. for (auto &CurrentUnit : CompileUnits) { @@ -2517,9 +2502,6 @@ } void DWARFLinker::copyInvariantDebugSection(DWARFContext &Dwarf) { - if (!needToTranslateStrings()) - TheDwarfEmitter->emitSectionContents( - Dwarf.getDWARFObj().getLineSection().Data, "debug_line"); TheDwarfEmitter->emitSectionContents(Dwarf.getDWARFObj().getLocSection().Data, "debug_loc"); TheDwarfEmitter->emitSectionContents( @@ -2569,7 +2551,8 @@ // This Dwarf string pool which is used for emission. It must be used // serially as the order of calling getStringOffset matters for // reproducibility. - OffsetsStringPool OffsetsStringPool(StringsTranslator, true); + OffsetsStringPool DebugStrPool(StringsTranslator, true); + OffsetsStringPool DebugLineStrPool(StringsTranslator, false); // ODR Contexts for the optimize. DeclContextTree ODRContexts; @@ -2582,7 +2565,7 @@ outs() << "OBJECT FILE: " << OptContext.File.FileName << "\n"; } - if (emitPaperTrailWarnings(OptContext.File, OffsetsStringPool)) + if (emitPaperTrailWarnings(OptContext.File, DebugStrPool)) continue; if (!OptContext.File.Dwarf) @@ -2635,8 +2618,8 @@ } for (auto &CU : OptContext.ModuleUnits) { - if (Error Err = - cloneModuleUnit(OptContext, CU, ODRContexts, OffsetsStringPool)) + if (Error Err = cloneModuleUnit(OptContext, CU, ODRContexts, DebugStrPool, + DebugLineStrPool)) reportWarning(toString(std::move(Err)), CU.File); } } @@ -2733,9 +2716,9 @@ getDebugInfoSize(*OptContext.File.Dwarf); SizeByObject[OptContext.File.FileName].Output = DIECloner(*this, TheDwarfEmitter, OptContext.File, DIEAlloc, - OptContext.CompileUnits, Options.Update) + OptContext.CompileUnits, Options.Update, DebugStrPool, + DebugLineStrPool) .cloneAllCompileUnits(*OptContext.File.Dwarf, OptContext.File, - OffsetsStringPool, OptContext.File.Dwarf->isLittleEndian()); } if (!Options.NoOutput && !OptContext.CompileUnits.empty() && @@ -2753,7 +2736,8 @@ // Emit everything that's global. if (!Options.NoOutput) { TheDwarfEmitter->emitAbbrevs(Abbreviations, Options.TargetDWARFVersion); - TheDwarfEmitter->emitStrings(OffsetsStringPool); + TheDwarfEmitter->emitStrings(DebugStrPool); + TheDwarfEmitter->emitLineStrings(DebugLineStrPool); for (DwarfLinkerAccelTableKind TableKind : Options.AccelTables) { switch (TableKind) { case DwarfLinkerAccelTableKind::Apple: @@ -2867,7 +2851,8 @@ Error DWARFLinker::cloneModuleUnit(LinkContext &Context, RefModuleUnit &Unit, DeclContextTree &ODRContexts, - OffsetsStringPool &OffsetsStringPool, + OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool, unsigned Indent) { assert(Unit.Unit.get() != nullptr); @@ -2894,8 +2879,8 @@ CompileUnits.emplace_back(std::move(Unit.Unit)); assert(TheDwarfEmitter); DIECloner(*this, TheDwarfEmitter, Unit.File, DIEAlloc, CompileUnits, - Options.Update) - .cloneAllCompileUnits(*Unit.File.Dwarf, Unit.File, OffsetsStringPool, + Options.Update, DebugStrPool, DebugLineStrPool) + .cloneAllCompileUnits(*Unit.File.Dwarf, Unit.File, Unit.File.Dwarf->isLittleEndian()); return Error::success(); } diff --git a/llvm/lib/DWARFLinker/DWARFStreamer.cpp b/llvm/lib/DWARFLinker/DWARFStreamer.cpp --- a/llvm/lib/DWARFLinker/DWARFStreamer.cpp +++ b/llvm/lib/DWARFLinker/DWARFStreamer.cpp @@ -240,16 +240,18 @@ // Emit a null terminator. Asm->emitInt8(0); } +} -#if 0 - if (DwarfVersion >= 5) { - // Emit an empty string offset section. - Asm->OutStreamer->switchSection(MOFI->getDwarfStrOffSection()); - Asm->emitDwarfUnitLength(4, "Length of String Offsets Set"); - Asm->emitInt16(DwarfVersion); - Asm->emitInt16(0); +/// Emit the debug_line_str section stored in \p Pool. +void DwarfStreamer::emitLineStrings(const NonRelocatableStringpool &Pool) { + Asm->OutStreamer->switchSection(MOFI->getDwarfLineStrSection()); + std::vector Entries = Pool.getEntriesForEmission(); + for (auto Entry : Entries) { + // Emit the string itself. + Asm->OutStreamer->emitBytes(Entry.getString()); + // Emit a null terminator. + Asm->emitInt8(0); } -#endif } void DwarfStreamer::emitDebugNames( @@ -631,27 +633,225 @@ LocListsSectionSize += 1; } -void DwarfStreamer::emitLineTableForUnit(MCDwarfLineTableParams Params, - StringRef PrologueBytes, - unsigned MinInstLength, - std::vector &Rows, - unsigned PointerSize) { +void DwarfStreamer::emitLineTableForUnit( + const DWARFDebugLine::LineTable &LineTable, const CompileUnit &Unit, + OffsetsStringPool &DebugStrPool, OffsetsStringPool &DebugLineStrPool) { // Switch to the section where the table will be emitted into. MS->switchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + MCSymbol *LineStartSym = MC->createTempSymbol(); MCSymbol *LineEndSym = MC->createTempSymbol(); - // The first 4 bytes is the total length of the information for this - // compilation unit (not including these 4 bytes for the length). - Asm->emitLabelDifference(LineEndSym, LineStartSym, 4); + // unit_length. + if (LineTable.Prologue.FormParams.Format == dwarf::DwarfFormat::DWARF64) { + MS->emitInt32(dwarf::DW_LENGTH_DWARF64); + LineSectionSize += 4; + } + emitLabelDifference(LineEndSym, LineStartSym, + LineTable.Prologue.FormParams.Format, LineSectionSize); Asm->OutStreamer->emitLabel(LineStartSym); - // Copy Prologue. - MS->emitBytes(PrologueBytes); - LineSectionSize += PrologueBytes.size() + 4; + + // Emit prologue. + emitLineTablePrologue(LineTable.Prologue, DebugStrPool, DebugLineStrPool); + + // Emit rows. + emitLineTableRows(LineTable, LineEndSym, + Unit.getOrigUnit().getAddressByteSize()); +} + +void DwarfStreamer::emitLineTablePrologue(const DWARFDebugLine::Prologue &P, + OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool) { + MCSymbol *PrologueStartSym = MC->createTempSymbol(); + MCSymbol *PrologueEndSym = MC->createTempSymbol(); + + // version (uhalf). + MS->emitInt16(P.getVersion()); + LineSectionSize += 2; + if (P.getVersion() == 5) { + // address_size (ubyte). + MS->emitInt8(P.getAddressSize()); + LineSectionSize += 1; + + // segment_selector_size (ubyte). + MS->emitInt8(P.SegSelectorSize); + LineSectionSize += 1; + } + + // header_length. + emitLabelDifference(PrologueEndSym, PrologueStartSym, P.FormParams.Format, + LineSectionSize); + + Asm->OutStreamer->emitLabel(PrologueStartSym); + emitLineTableProloguePayload(P, DebugStrPool, DebugLineStrPool); + Asm->OutStreamer->emitLabel(PrologueEndSym); +} + +void DwarfStreamer::emitLineTablePrologueV2IncludeAndFileTable( + const DWARFDebugLine::Prologue &P, OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool) { + // include_directories (sequence of path names). + for (const DWARFFormValue &Include : P.IncludeDirectories) + emitLineTableString(P, Include, DebugStrPool, DebugLineStrPool); + // The last entry is followed by a single null byte. + MS->emitInt8(0); + LineSectionSize += 1; + + // file_names (sequence of file entries). + for (const DWARFDebugLine::FileNameEntry &File : P.FileNames) { + // A null-terminated string containing the full or relative path name of a + // source file. + emitLineTableString(P, File.Name, DebugStrPool, DebugLineStrPool); + // An unsigned LEB128 number representing the directory index of a directory + // in the include_directories section. + LineSectionSize += MS->emitULEB128IntValue(File.DirIdx); + // An unsigned LEB128 number representing the (implementation-defined) time + // of last modification for the file, or 0 if not available. + LineSectionSize += MS->emitULEB128IntValue(File.ModTime); + // An unsigned LEB128 number representing the length in bytes of the file, + // or 0 if not available. + LineSectionSize += MS->emitULEB128IntValue(File.Length); + } + // The last entry is followed by a single null byte. + MS->emitInt8(0); + LineSectionSize += 1; +} + +void DwarfStreamer::emitLineTablePrologueV5IncludeAndFileTable( + const DWARFDebugLine::Prologue &P, OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool) { + if (P.IncludeDirectories.empty()) { + // directory_entry_format_count(ubyte). + MS->emitInt8(0); + LineSectionSize += 1; + } else { + // directory_entry_format_count(ubyte). + MS->emitInt8(1); + LineSectionSize += 1; + + // directory_entry_format (sequence of ULEB128 pairs). + LineSectionSize += MS->emitULEB128IntValue(dwarf::DW_LNCT_path); + LineSectionSize += + MS->emitULEB128IntValue(P.IncludeDirectories[0].getForm()); + } + + // directories_count (ULEB128). + LineSectionSize += MS->emitULEB128IntValue(P.IncludeDirectories.size()); + // directories (sequence of directory names). + for (auto Include : P.IncludeDirectories) + emitLineTableString(P, Include, DebugStrPool, DebugLineStrPool); + + if (P.FileNames.empty()) { + // file_name_entry_format_count (ubyte). + MS->emitInt8(0); + LineSectionSize += 1; + } else { + // file_name_entry_format_count (ubyte). + MS->emitInt8(2); + LineSectionSize += 1; + + // file_name_entry_format (sequence of ULEB128 pairs). + LineSectionSize += MS->emitULEB128IntValue(dwarf::DW_LNCT_path); + LineSectionSize += MS->emitULEB128IntValue(P.FileNames[0].Name.getForm()); + + LineSectionSize += MS->emitULEB128IntValue(dwarf::DW_LNCT_directory_index); + LineSectionSize += MS->emitULEB128IntValue(dwarf::DW_FORM_data1); + } + + // file_names_count (ULEB128). + LineSectionSize += MS->emitULEB128IntValue(P.FileNames.size()); + + // file_names (sequence of file name entries). + for (auto File : P.FileNames) { + emitLineTableString(P, File.Name, DebugStrPool, DebugLineStrPool); + MS->emitInt8(File.DirIdx); + LineSectionSize += 1; + } +} + +void DwarfStreamer::emitLineTableString(const DWARFDebugLine::Prologue &P, + const DWARFFormValue &String, + OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool) { + std::optional StringVal = dwarf::toString(String); + if (!StringVal) { + warn("Cann't read string from line table."); + return; + } + + switch (String.getForm()) { + case dwarf::DW_FORM_string: { + StringRef TranslatedString = + (Translator) ? Translator(*StringVal) : *StringVal; + Asm->OutStreamer->emitBytes(TranslatedString.data()); + Asm->emitInt8(0); + LineSectionSize += TranslatedString.size() + 1; + } break; + case dwarf::DW_FORM_strp: + case dwarf::DW_FORM_line_strp: { + DwarfStringPoolEntryRef StringRef = + String.getForm() == dwarf::DW_FORM_strp + ? DebugStrPool.getEntry(*StringVal) + : DebugLineStrPool.getEntry(*StringVal); + + emitIntOffset(StringRef.getOffset(), P.FormParams.Format, LineSectionSize); + } break; + default: + warn("Unsupported string form inside line table."); + break; + }; +} + +void DwarfStreamer::emitLineTableProloguePayload( + const DWARFDebugLine::Prologue &P, OffsetsStringPool &DebugStrPool, + OffsetsStringPool &DebugLineStrPool) { + // minimum_instruction_length (ubyte). + MS->emitInt8(P.MinInstLength); + LineSectionSize += 1; + if (P.FormParams.Version >= 4) { + // maximum_operations_per_instruction (ubyte). + MS->emitInt8(P.MaxOpsPerInst); + LineSectionSize += 1; + } + // default_is_stmt (ubyte). + MS->emitInt8(P.DefaultIsStmt); + LineSectionSize += 1; + // line_base (sbyte). + MS->emitInt8(P.LineBase); + LineSectionSize += 1; + // line_range (ubyte). + MS->emitInt8(P.LineRange); + LineSectionSize += 1; + // opcode_base (ubyte). + MS->emitInt8(P.OpcodeBase); + LineSectionSize += 1; + + // standard_opcode_lengths (array of ubyte). + for (auto Length : P.StandardOpcodeLengths) { + MS->emitInt8(Length); + LineSectionSize += 1; + } + + if (P.FormParams.Version < 5) + emitLineTablePrologueV2IncludeAndFileTable(P, DebugStrPool, + DebugLineStrPool); + else + emitLineTablePrologueV5IncludeAndFileTable(P, DebugStrPool, + DebugLineStrPool); +} + +void DwarfStreamer::emitLineTableRows( + const DWARFDebugLine::LineTable &LineTable, MCSymbol *LineEndSym, + unsigned AddressByteSize) { + + MCDwarfLineTableParams Params; + Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase; + Params.DWARF2LineBase = LineTable.Prologue.LineBase; + Params.DWARF2LineRange = LineTable.Prologue.LineRange; SmallString<128> EncodingBuffer; - if (Rows.empty()) { + if (LineTable.Rows.empty()) { // We only have the dummy entry, dsymutil emits an entry with a 0 // address in that case. MCDwarfLineAddr::encode(*MC, Params, std::numeric_limits::max(), 0, @@ -672,17 +872,19 @@ unsigned RowsSinceLastSequence = 0; - for (DWARFDebugLine::Row &Row : Rows) { + for (const DWARFDebugLine::Row &Row : LineTable.Rows) { int64_t AddressDelta; if (Address == -1ULL) { MS->emitIntValue(dwarf::DW_LNS_extended_op, 1); - MS->emitULEB128IntValue(PointerSize + 1); + MS->emitULEB128IntValue(AddressByteSize + 1); MS->emitIntValue(dwarf::DW_LNE_set_address, 1); - MS->emitIntValue(Row.Address.Address, PointerSize); - LineSectionSize += 2 + PointerSize + getULEB128Size(PointerSize + 1); + MS->emitIntValue(Row.Address.Address, AddressByteSize); + LineSectionSize += + 2 + AddressByteSize + getULEB128Size(AddressByteSize + 1); AddressDelta = 0; } else { - AddressDelta = (Row.Address.Address - Address) / MinInstLength; + AddressDelta = + (Row.Address.Address - Address) / LineTable.Prologue.MinInstLength; } // FIXME: code copied and transformed from MCDwarf.cpp::EmitDwarfLineTable. @@ -734,7 +936,8 @@ int64_t LineDelta = int64_t(Row.Line) - LastLine; if (!Row.EndSequence) { - MCDwarfLineAddr::encode(*MC, Params, LineDelta, AddressDelta, EncodingBuffer); + MCDwarfLineAddr::encode(*MC, Params, LineDelta, AddressDelta, + EncodingBuffer); MS->emitBytes(EncodingBuffer); LineSectionSize += EncodingBuffer.size(); EncodingBuffer.resize(0); @@ -774,86 +977,19 @@ MS->emitLabel(LineEndSym); } -/// Copy the debug_line over to the updated binary while unobfuscating the file -/// names and directories. -void DwarfStreamer::translateLineTable(DataExtractor Data, uint64_t Offset) { - MS->switchSection(MC->getObjectFileInfo()->getDwarfLineSection()); - StringRef Contents = Data.getData(); - - // We have to deconstruct the line table header, because it contains to - // length fields that will need to be updated when we change the length of - // the files and directories in there. - unsigned UnitLength = Data.getU32(&Offset); - uint64_t UnitEnd = Offset + UnitLength; - MCSymbol *BeginLabel = MC->createTempSymbol(); - MCSymbol *EndLabel = MC->createTempSymbol(); - unsigned Version = Data.getU16(&Offset); - - if (Version > 5) { - warn("Unsupported line table version: dropping contents and not " - "unobfsucating line table."); - return; - } - - Asm->emitLabelDifference(EndLabel, BeginLabel, 4); - Asm->OutStreamer->emitLabel(BeginLabel); - Asm->emitInt16(Version); - LineSectionSize += 6; - - MCSymbol *HeaderBeginLabel = MC->createTempSymbol(); - MCSymbol *HeaderEndLabel = MC->createTempSymbol(); - Asm->emitLabelDifference(HeaderEndLabel, HeaderBeginLabel, 4); - Asm->OutStreamer->emitLabel(HeaderBeginLabel); - Offset += 4; - LineSectionSize += 4; - - uint64_t AfterHeaderLengthOffset = Offset; - // Skip to the directories. - Offset += (Version >= 4) ? 5 : 4; - unsigned OpcodeBase = Data.getU8(&Offset); - Offset += OpcodeBase - 1; - Asm->OutStreamer->emitBytes(Contents.slice(AfterHeaderLengthOffset, Offset)); - LineSectionSize += Offset - AfterHeaderLengthOffset; - - // Offset points to the first directory. - while (const char *Dir = Data.getCStr(&Offset)) { - if (Dir[0] == 0) - break; - - StringRef Translated = Translator(Dir); - Asm->OutStreamer->emitBytes(Translated); - Asm->emitInt8(0); - LineSectionSize += Translated.size() + 1; - } - Asm->emitInt8(0); - LineSectionSize += 1; - - while (const char *File = Data.getCStr(&Offset)) { - if (File[0] == 0) - break; - - StringRef Translated = Translator(File); - Asm->OutStreamer->emitBytes(Translated); - Asm->emitInt8(0); - LineSectionSize += Translated.size() + 1; - - uint64_t OffsetBeforeLEBs = Offset; - Asm->emitULEB128(Data.getULEB128(&Offset)); - Asm->emitULEB128(Data.getULEB128(&Offset)); - Asm->emitULEB128(Data.getULEB128(&Offset)); - LineSectionSize += Offset - OffsetBeforeLEBs; - } - Asm->emitInt8(0); - LineSectionSize += 1; - - Asm->OutStreamer->emitLabel(HeaderEndLabel); - - // Copy the actual line table program over. - Asm->OutStreamer->emitBytes(Contents.slice(Offset, UnitEnd)); - LineSectionSize += UnitEnd - Offset; +void DwarfStreamer::emitIntOffset(uint64_t Offset, dwarf::DwarfFormat Format, + uint64_t &SectionSize) { + uint8_t Size = dwarf::getDwarfOffsetByteSize(Format); + MS->emitIntValue(Offset, Size); + SectionSize += Size; +} - Asm->OutStreamer->emitLabel(EndLabel); - Offset = UnitEnd; +void DwarfStreamer::emitLabelDifference(const MCSymbol *Hi, const MCSymbol *Lo, + dwarf::DwarfFormat Format, + uint64_t &SectionSize) { + uint8_t Size = dwarf::getDwarfOffsetByteSize(Format); + Asm->emitLabelDifference(Hi, Lo, Size); + SectionSize += Size; } /// Emit the pubnames or pubtypes section contribution for \p diff --git a/llvm/test/tools/dsymutil/Inputs/dwarf5-linetable.o b/llvm/test/tools/dsymutil/Inputs/dwarf5-linetable.o new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@