//===- DebugLineSectionEmitter.h --------------------------------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_DWARFLINKERPARALLEL_DEBUGLINESECTIONEMITTER_H #define LLVM_LIB_DWARFLINKERPARALLEL_DEBUGLINESECTIONEMITTER_H #include "DWARFEmitterImpl.h" #include "llvm/DWARFLinkerParallel/AddressesMap.h" #include "llvm/DWARFLinkerParallel/DWARFLinker.h" #include "llvm/DebugInfo/DWARF/DWARFObject.h" #include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/MC/TargetRegistry.h" namespace llvm { namespace dwarflinker_parallel { /// This class emits specified line table into the .debug_line section. class DebugLineSectionEmitter { public: DebugLineSectionEmitter(const Triple &TheTriple, DwarfUnit &U) : TheTriple(TheTriple), U(U) {} Error emit(const DWARFDebugLine::LineTable &LineTable) { // FIXME: remove dependence on MCDwarfLineAddr::encode. // As we reuse MCDwarfLineAddr::encode, we need to create/initialize // some MC* classes. if (Error Err = init(TheTriple)) return Err; // Get descriptor for output .debug_line section. SectionDescriptor &OutSection = U.getOrCreateSectionDescriptor(DebugSectionKind::DebugLine); // unit_length. OutSection.emitUnitLength(0xBADDEF); uint64_t OffsetAfterUnitLength = OutSection.OS.tell(); // Emit prologue. emitLineTablePrologue(LineTable.Prologue, OutSection); // Emit rows. emitLineTableRows(LineTable, OutSection); uint64_t OffsetAfterEnd = OutSection.OS.tell(); // Update unit length field with actual length value. assert(OffsetAfterUnitLength - OutSection.getFormParams().getDwarfOffsetByteSize() < OffsetAfterUnitLength); OutSection.apply(OffsetAfterUnitLength - OutSection.getFormParams().getDwarfOffsetByteSize(), dwarf::DW_FORM_sec_offset, OffsetAfterEnd - OffsetAfterUnitLength); return Error::success(); } private: Error init(Triple TheTriple) { std::string ErrorStr; std::string TripleName; // Get the target. const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr); if (!TheTarget) return createStringError(std::errc::invalid_argument, ErrorStr.c_str()); TripleName = TheTriple.getTriple(); // Create all the MC Objects. MRI.reset(TheTarget->createMCRegInfo(TripleName)); if (!MRI) return createStringError(std::errc::invalid_argument, "no register info for target %s", TripleName.c_str()); MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags(); MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); if (!MAI) return createStringError(std::errc::invalid_argument, "no asm info for target %s", TripleName.c_str()); MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", "")); if (!MSTI) return createStringError(std::errc::invalid_argument, "no subtarget info for target %s", TripleName.c_str()); MC.reset(new MCContext(TheTriple, MAI.get(), MRI.get(), MSTI.get(), nullptr, nullptr, true, "__DWARF")); return Error::success(); } void emitLineTablePrologue(const DWARFDebugLine::Prologue &P, SectionDescriptor &Section) { // version (uhalf). Section.emitIntVal(P.getVersion(), 2); if (P.getVersion() == 5) { // address_size (ubyte). Section.emitIntVal(P.getAddressSize(), 1); // segment_selector_size (ubyte). Section.emitIntVal(P.SegSelectorSize, 1); } // header_length. Section.emitOffset(0xBADDEF); uint64_t OffsetAfterPrologueLength = Section.OS.tell(); emitLineTableProloguePayload(P, Section); uint64_t OffsetAfterPrologueEnd = Section.OS.tell(); // Update prologue length field with actual length value. Section.apply(OffsetAfterPrologueLength - Section.getFormParams().getDwarfOffsetByteSize(), dwarf::DW_FORM_sec_offset, OffsetAfterPrologueEnd - OffsetAfterPrologueLength); } void emitLineTablePrologueV2IncludeAndFileTable(const DWARFDebugLine::Prologue &P, SectionDescriptor &Section) { // include_directories (sequence of path names). for (const DWARFFormValue &Include : P.IncludeDirectories) { std::optional IncludeStr = dwarf::toString(Include); if (!IncludeStr) { U.warn("cann't read string from line table."); return; } Section.emitString(Include.getForm(), *IncludeStr); } // The last entry is followed by a single null byte. Section.emitIntVal(0, 1); // file_names (sequence of file entries). for (const DWARFDebugLine::FileNameEntry &File : P.FileNames) { std::optional FileNameStr = dwarf::toString(File.Name); if (!FileNameStr) { U.warn("cann't read string from line table."); return; } // A null-terminated string containing the full or relative path name of a // source file. Section.emitString(File.Name.getForm(), *FileNameStr); // An unsigned LEB128 number representing the directory index of a // directory in the include_directories section. encodeULEB128(File.DirIdx, Section.OS); // An unsigned LEB128 number representing the (implementation-defined) // time of last modification for the file, or 0 if not available. encodeULEB128(File.ModTime, Section.OS); // An unsigned LEB128 number representing the length in bytes of the file, // or 0 if not available. encodeULEB128(File.Length, Section.OS); } // The last entry is followed by a single null byte. Section.emitIntVal(0, 1); } void emitLineTablePrologueV5IncludeAndFileTable(const DWARFDebugLine::Prologue &P, SectionDescriptor &Section) { if (P.IncludeDirectories.empty()) { // directory_entry_format_count(ubyte). Section.emitIntVal(0, 1); } else { // directory_entry_format_count(ubyte). Section.emitIntVal(1, 1); // directory_entry_format (sequence of ULEB128 pairs). encodeULEB128(dwarf::DW_LNCT_path, Section.OS); encodeULEB128(P.IncludeDirectories[0].getForm(), Section.OS); } // directories_count (ULEB128). encodeULEB128(P.IncludeDirectories.size(), Section.OS); // directories (sequence of directory names). for (auto Include : P.IncludeDirectories) { std::optional IncludeStr = dwarf::toString(Include); if (!IncludeStr) { U.warn("cann't read string from line table."); return; } Section.emitString(Include.getForm(), *IncludeStr); } if (P.FileNames.empty()) { // file_name_entry_format_count (ubyte). Section.emitIntVal(0, 1); } else { // file_name_entry_format_count (ubyte). Section.emitIntVal(2, 1); // file_name_entry_format (sequence of ULEB128 pairs). encodeULEB128(dwarf::DW_LNCT_path, Section.OS); encodeULEB128(P.FileNames[0].Name.getForm(), Section.OS); encodeULEB128(dwarf::DW_LNCT_directory_index, Section.OS); encodeULEB128(dwarf::DW_FORM_data1, Section.OS); } // file_names_count (ULEB128). encodeULEB128(P.FileNames.size(), Section.OS); // file_names (sequence of file name entries). for (auto File : P.FileNames) { std::optional FileNameStr = dwarf::toString(File.Name); if (!FileNameStr) { U.warn("cann't read string from line table."); return; } // A null-terminated string containing the full or relative path name of a // source file. Section.emitString(File.Name.getForm(), *FileNameStr); Section.emitIntVal(File.DirIdx, 1); } } void emitLineTableProloguePayload(const DWARFDebugLine::Prologue &P, SectionDescriptor &Section) { // minimum_instruction_length (ubyte). Section.emitIntVal(P.MinInstLength, 1); if (P.FormParams.Version >= 4) { // maximum_operations_per_instruction (ubyte). Section.emitIntVal(P.MaxOpsPerInst, 1); } // default_is_stmt (ubyte). Section.emitIntVal(P.DefaultIsStmt, 1); // line_base (sbyte). Section.emitIntVal(P.LineBase, 1); // line_range (ubyte). Section.emitIntVal(P.LineRange, 1); // opcode_base (ubyte). Section.emitIntVal(P.OpcodeBase, 1); // standard_opcode_lengths (array of ubyte). for (auto Length : P.StandardOpcodeLengths) Section.emitIntVal(Length, 1); if (P.FormParams.Version < 5) emitLineTablePrologueV2IncludeAndFileTable(P, Section); else emitLineTablePrologueV5IncludeAndFileTable(P, Section); } void emitLineTableRows(const DWARFDebugLine::LineTable &LineTable, SectionDescriptor &Section) { MCDwarfLineTableParams Params; Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase; Params.DWARF2LineBase = LineTable.Prologue.LineBase; Params.DWARF2LineRange = LineTable.Prologue.LineRange; SmallString<128> EncodingBuffer; 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, EncodingBuffer); Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size()); return; } // Line table state machine fields unsigned FileNum = 1; unsigned LastLine = 1; unsigned Column = 0; unsigned IsStatement = 1; unsigned Isa = 0; uint64_t Address = -1ULL; unsigned RowsSinceLastSequence = 0; for (const DWARFDebugLine::Row &Row : LineTable.Rows) { int64_t AddressDelta; if (Address == -1ULL) { Section.emitIntVal(dwarf::DW_LNS_extended_op, 1); encodeULEB128(Section.getFormParams().AddrSize + 1, Section.OS); Section.emitIntVal(dwarf::DW_LNE_set_address, 1); Section.emitIntVal(Row.Address.Address, Section.getFormParams().AddrSize); AddressDelta = 0; } else { AddressDelta = (Row.Address.Address - Address) / LineTable.Prologue.MinInstLength; } // FIXME: code copied and transformed from // MCDwarf.cpp::EmitDwarfLineTable. We should find a way to share this // code, but the current compatibility requirement with classic dsymutil // makes it hard. Revisit that once this requirement is dropped. if (FileNum != Row.File) { FileNum = Row.File; Section.emitIntVal(dwarf::DW_LNS_set_file, 1); encodeULEB128(FileNum, Section.OS); } if (Column != Row.Column) { Column = Row.Column; Section.emitIntVal(dwarf::DW_LNS_set_column, 1); encodeULEB128(Column, Section.OS); } // FIXME: We should handle the discriminator here, but dsymutil doesn't // consider it, thus ignore it for now. if (Isa != Row.Isa) { Isa = Row.Isa; Section.emitIntVal(dwarf::DW_LNS_set_isa, 1); encodeULEB128(Isa, Section.OS); } if (IsStatement != Row.IsStmt) { IsStatement = Row.IsStmt; Section.emitIntVal(dwarf::DW_LNS_negate_stmt, 1); } if (Row.BasicBlock) Section.emitIntVal(dwarf::DW_LNS_set_basic_block, 1); if (Row.PrologueEnd) Section.emitIntVal(dwarf::DW_LNS_set_prologue_end, 1); if (Row.EpilogueBegin) Section.emitIntVal(dwarf::DW_LNS_set_epilogue_begin, 1); int64_t LineDelta = int64_t(Row.Line) - LastLine; if (!Row.EndSequence) { MCDwarfLineAddr::encode(*MC, Params, LineDelta, AddressDelta, EncodingBuffer); Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size()); EncodingBuffer.resize(0); Address = Row.Address.Address; LastLine = Row.Line; RowsSinceLastSequence++; } else { if (LineDelta) { Section.emitIntVal(dwarf::DW_LNS_advance_line, 1); encodeSLEB128(LineDelta, Section.OS); } if (AddressDelta) { Section.emitIntVal(dwarf::DW_LNS_advance_pc, 1); encodeULEB128(AddressDelta, Section.OS); } MCDwarfLineAddr::encode(*MC, Params, std::numeric_limits::max(), 0, EncodingBuffer); Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size()); EncodingBuffer.resize(0); Address = -1ULL; LastLine = FileNum = IsStatement = 1; RowsSinceLastSequence = Column = Isa = 0; } } if (RowsSinceLastSequence) { MCDwarfLineAddr::encode(*MC, Params, std::numeric_limits::max(), 0, EncodingBuffer); Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size()); EncodingBuffer.resize(0); } } Triple TheTriple; DwarfUnit &U; std::unique_ptr MRI; std::unique_ptr MAI; std::unique_ptr MC; std::unique_ptr MSTI; }; } // end of namespace dwarflinker_parallel } // end namespace llvm #endif // LLVM_LIB_DWARFLINKERPARALLEL_DEBUGLINESECTIONEMITTER_H