mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-28 04:16:06 +00:00

This patch backports https://github.com/llvm/llvm-project/pull/77016 into the DWARFLinkerParallel.
426 lines
15 KiB
C++
426 lines
15 KiB
C++
//===- 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_DWARFLINKER_PARALLEL_DEBUGLINESECTIONEMITTER_H
|
|
#define LLVM_LIB_DWARFLINKER_PARALLEL_DEBUGLINESECTIONEMITTER_H
|
|
|
|
#include "DWARFEmitterImpl.h"
|
|
#include "llvm/DWARFLinker/AddressesMap.h"
|
|
#include "llvm/DWARFLinker/Parallel/DWARFLinker.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFObject.h"
|
|
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
|
|
#include "llvm/MC/TargetRegistry.h"
|
|
|
|
namespace llvm {
|
|
namespace dwarf_linker {
|
|
namespace 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);
|
|
}
|
|
|
|
bool HasChecksums = P.ContentTypes.HasMD5;
|
|
bool HasInlineSources = P.ContentTypes.HasSource;
|
|
|
|
dwarf::Form FileNameForm = dwarf::DW_FORM_string;
|
|
dwarf::Form LLVMSourceForm = dwarf::DW_FORM_string;
|
|
|
|
if (P.FileNames.empty()) {
|
|
// file_name_entry_format_count (ubyte).
|
|
Section.emitIntVal(0, 1);
|
|
} else {
|
|
FileNameForm = P.FileNames[0].Name.getForm();
|
|
LLVMSourceForm = P.FileNames[0].Source.getForm();
|
|
|
|
// file_name_entry_format_count (ubyte).
|
|
Section.emitIntVal(
|
|
2 + (HasChecksums ? 1 : 0) + (HasInlineSources ? 1 : 0), 1);
|
|
|
|
// file_name_entry_format (sequence of ULEB128 pairs).
|
|
encodeULEB128(dwarf::DW_LNCT_path, Section.OS);
|
|
encodeULEB128(FileNameForm, Section.OS);
|
|
|
|
encodeULEB128(dwarf::DW_LNCT_directory_index, Section.OS);
|
|
encodeULEB128(dwarf::DW_FORM_data1, Section.OS);
|
|
|
|
if (HasChecksums) {
|
|
encodeULEB128(dwarf::DW_LNCT_MD5, Section.OS);
|
|
encodeULEB128(dwarf::DW_FORM_data16, Section.OS);
|
|
}
|
|
|
|
if (HasInlineSources) {
|
|
encodeULEB128(dwarf::DW_LNCT_LLVM_source, Section.OS);
|
|
encodeULEB128(LLVMSourceForm, 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(FileNameForm, *FileNameStr);
|
|
Section.emitIntVal(File.DirIdx, 1);
|
|
|
|
if (HasChecksums) {
|
|
assert((File.Checksum.size() == 16) &&
|
|
"checksum size is not equal to 16 bytes.");
|
|
Section.emitBinaryData(
|
|
StringRef(reinterpret_cast<const char *>(File.Checksum.data()),
|
|
File.Checksum.size()));
|
|
}
|
|
|
|
if (HasInlineSources) {
|
|
std::optional<const char *> FileSourceStr =
|
|
dwarf::toString(File.Source);
|
|
if (!FileSourceStr) {
|
|
U.warn("cann't read string from line table.");
|
|
return;
|
|
}
|
|
|
|
Section.emitString(LLVMSourceForm, *FileSourceStr);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 parallel
|
|
} // end of namespace dwarf_linker
|
|
} // end of namespace llvm
|
|
|
|
#endif // LLVM_LIB_DWARFLINKER_PARALLEL_DEBUGLINESECTIONEMITTER_H
|