blob: f1fcd91753900df1c8acfa731cf7bd5c1db33cf4 [file] [log] [blame]
//===- 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/ADT/DenseMap.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<const char *> 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<const char *> 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<const char *> 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<const char *> 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<int64_t>::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<int64_t>::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<int64_t>::max(),
0, EncodingBuffer);
Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size());
EncodingBuffer.resize(0);
}
}
Triple TheTriple;
DwarfUnit &U;
std::unique_ptr<MCRegisterInfo> MRI;
std::unique_ptr<MCAsmInfo> MAI;
std::unique_ptr<MCContext> MC;
std::unique_ptr<MCSubtargetInfo> MSTI;
};
} // end of namespace dwarflinker_parallel
} // end namespace llvm
#endif // LLVM_LIB_DWARFLINKERPARALLEL_DEBUGLINESECTIONEMITTER_H