[DWARF] Rework debug line parsing to use llvm::Error and callbacks

Reviewed by: dblaikie, JDevlieghere, espindola

Differential Revision: https://reviews.llvm.org/D44560

Summary:
The .debug_line parser previously reported errors by printing to stderr and
return false. This is not particularly helpful for clients of the library code,
as it prevents them from handling the errors in a manner based on the calling
context. This change switches to using llvm::Error and callbacks to indicate
what problems were detected during parsing, and has updated clients to handle
the errors in a location-specific manner. In general, this means that they
continue to do the same thing to external users. Below, I have outlined what
the known behaviour changes are, relating to this change.

There are two levels of "errors" in the new error mechanism, to broadly
distinguish between different fail states of the parser, since not every
failure will prevent parsing of the unit, or of subsequent unit. Malformed
table errors that prevent reading the remainder of the table (reported by
returning them) and other minor issues representing problems with parsing that
do not prevent attempting to continue reading the table (reported by calling a
specified callback funciton). The only example of this currently is when the
last sequence of a unit is unterminated. However, I think it would be good to
change the handling of unrecognised opcodes to report as minor issues as well,
rather than just printing to the stream if --verbose is used (this would be a
subsequent change however).

I have substantially extended the DwarfGenerator to be able to handle
custom-crafted .debug_line sections, allowing for comprehensive unit-testing
of the parser code. For now, I am just adding unit tests to cover the basic
error reporting, and positive cases, and do not currently intend to test every
part of the parser, although the framework should be sufficient to do so at a
later point.

Known behaviour changes:
  - The dump function in DWARFContext now does not attempt to read subsequent
  tables when searching for a specific offset, if the unit length field of a
  table before the specified offset is a reserved value.
  - getOrParseLineTable now returns a useful Error if an invalid offset is
  encountered, rather than simply a nullptr.
  - The parse functions no longer use `WithColor::warning` directly to report
  errors, allowing LLD to call its own warning function.
  - The existing parse error messages have been updated to not specifically
  include "warning" in their message, allowing consumers to determine what
  severity the problem is.
  - If the line table version field appears to have a value less than 2, an
  informative error is returned, instead of just false.
  - If the line table unit length field uses a reserved value, an informative
  error is returned, instead of just false.
  - Dumping of .debug_line.dwo sections is now implemented the same as regular
  .debug_line sections.
  - Verbose dumping of .debug_line[.dwo] sections now prints the prologue, if
  there is a prologue error, just like non-verbose dumping.

As a helper for the generator code, I have re-added emitInt64 to the
AsmPrinter code. This previously existed, but was removed way back in r100296,
presumably because it was dead at the time.

This change also requires a change to LLD, which will be committed separately.

llvm-svn: 331971
This commit is contained in:
James Henderson 2018-05-10 10:51:33 +00:00
parent 19dd1a0ea6
commit a3acf99e59
14 changed files with 1556 additions and 250 deletions

View File

@ -463,6 +463,9 @@ public:
/// Emit a long directive and value.
void emitInt32(int Value) const;
/// Emit a long long directive and value.
void emitInt64(uint64_t Value) const;
/// Emit something like ".long Hi-Lo" where the size in bytes of the directive
/// is specified by Size and Hi/Lo specify the labels. This implicitly uses
/// .set if it is available.

View File

@ -259,7 +259,14 @@ public:
const AppleAcceleratorTable &getAppleObjC();
/// Get a pointer to a parsed line table corresponding to a compile unit.
const DWARFDebugLine::LineTable *getLineTableForUnit(DWARFUnit *cu);
/// Report any parsing issues as warnings on stderr.
const DWARFDebugLine::LineTable *getLineTableForUnit(DWARFUnit *U);
/// Get a pointer to a parsed line table corresponding to a compile unit.
/// Report any parsing warnings using the callback.
Expected<const DWARFDebugLine::LineTable *>
getLineTableForUnit(DWARFUnit *U,
std::function<void(StringRef)> WarnCallback);
DataExtractor getStringExtractor() const {
return DataExtractor(DObj->getStringSection(), false, 0);

View File

@ -13,9 +13,11 @@
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"
#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h"
#include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h"
#include "llvm/Support/MD5.h"
#include <cstdint>
#include <map>
@ -103,6 +105,8 @@ public:
uint32_t sizeofPrologueLength() const { return isDWARF64() ? 8 : 4; }
bool totalLengthIsValid() const;
/// Length of the prologue in bytes.
uint32_t getLength() const {
return PrologueLength + sizeofTotalLength() + sizeof(getVersion()) +
@ -120,8 +124,8 @@ public:
void clear();
void dump(raw_ostream &OS, DIDumpOptions DumpOptions) const;
bool parse(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr,
const DWARFContext &Ctx, const DWARFUnit *U = nullptr);
Error parse(const DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr,
const DWARFContext &Ctx, const DWARFUnit *U = nullptr);
};
/// Standard .debug_line state machine structure.
@ -243,9 +247,10 @@ public:
void clear();
/// Parse prologue and all rows.
bool parse(DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr,
const DWARFContext &Ctx, const DWARFUnit *U,
raw_ostream *OS = nullptr);
Error parse(DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr,
const DWARFContext &Ctx, const DWARFUnit *U,
std::function<void(StringRef)> WarnCallback = warn,
raw_ostream *OS = nullptr);
using RowVector = std::vector<Row>;
using RowIter = RowVector::const_iterator;
@ -259,14 +264,81 @@ public:
private:
uint32_t findRowInSeq(const DWARFDebugLine::Sequence &Seq,
uint64_t Address) const;
Optional<StringRef> getSourceByIndex(uint64_t FileIndex,
DILineInfoSpecifier::FileLineInfoKind Kind) const;
Optional<StringRef>
getSourceByIndex(uint64_t FileIndex,
DILineInfoSpecifier::FileLineInfoKind Kind) const;
};
const LineTable *getLineTable(uint32_t Offset) const;
const LineTable *getOrParseLineTable(DWARFDataExtractor &DebugLineData,
uint32_t Offset, const DWARFContext &C,
const DWARFUnit *U);
Expected<const LineTable *>
getOrParseLineTable(DWARFDataExtractor &DebugLineData, uint32_t Offset,
const DWARFContext &Ctx, const DWARFUnit *U,
std::function<void(StringRef)> WarnCallback = warn);
/// Helper to allow for parsing of an entire .debug_line section in sequence.
class SectionParser {
public:
using cu_range = DWARFUnitSection<DWARFCompileUnit>::iterator_range;
using tu_range =
iterator_range<std::deque<DWARFUnitSection<DWARFTypeUnit>>::iterator>;
using LineToUnitMap = std::map<uint64_t, DWARFUnit *>;
SectionParser(DWARFDataExtractor &Data, const DWARFContext &C, cu_range CUs,
tu_range TUs);
/// Get the next line table from the section. Report any issues via the
/// callbacks.
///
/// \param StringCallback - any issues that don't indicate that the line
/// table is invalid are reported using this function.
/// \param ErrorCallback - any issues that mean that the line table is
/// invalid are reported using this callback.
/// \param OS - if not null, the parser will print information about the
/// table as it parses it.
LineTable parseNext(
function_ref<void(StringRef)> StringCallback = warn,
function_ref<void(Error)> ErrorCallback = warnForError,
raw_ostream *OS = nullptr);
/// Skip the current line table and go to the following line table (if
/// present) immediately.
///
/// \param ErrorCallback - report any prologue parsing issues via this
/// callback.
void skip(function_ref<void(Error)> ErrorCallback = warnForError);
/// Indicates if the parser has parsed as much as possible.
///
/// \note Certain problems with the line table structure might mean that
/// parsing stops before the end of the section is reached.
bool done() const { return Done; }
/// Get the offset the parser has reached.
uint32_t getOffset() const { return Offset; }
private:
DWARFUnit *prepareToParse(uint32_t Offset);
void moveToNextTable(uint32_t OldOffset, const Prologue &P);
LineToUnitMap LineToUnit;
DWARFDataExtractor &DebugLineData;
const DWARFContext &Context;
uint32_t Offset = 0;
bool Done = false;
};
/// Helper function for DWARFDebugLine parse functions, to report issues that
/// don't prevent parsing the remainder of the table as warnings.
///
/// \param Message The message to report.
static void warn(StringRef Message);
/// Helper function for DWARFDebugLine parse functions, to report issues that
/// prevent parsing the remainder of the table as warnings.
///
/// \param Error The Error to report.
static void warnForError(Error Err);
private:
struct ParsingState {

View File

@ -1967,6 +1967,11 @@ void AsmPrinter::emitInt32(int Value) const {
OutStreamer->EmitIntValue(Value, 4);
}
/// Emit a long long directive and value.
void AsmPrinter::emitInt64(uint64_t Value) const {
OutStreamer->EmitIntValue(Value, 8);
}
/// Emit something like ".long Hi-Lo" where the size in bytes of the directive
/// is specified by Size and Hi/Lo specify the labels. This implicitly uses
/// .set if it avoids relocations.

View File

@ -244,28 +244,6 @@ static void dumpStringOffsetsSection(
}
}
// We want to supply the Unit associated with a .debug_line[.dwo] table when
// we dump it, if possible, but still dump the table even if there isn't a Unit.
// Therefore, collect up handles on all the Units that point into the
// line-table section.
typedef std::map<uint64_t, DWARFUnit *> LineToUnitMap;
static LineToUnitMap
buildLineToUnitMap(DWARFContext::cu_iterator_range CUs,
DWARFContext::tu_section_iterator_range TUSections) {
LineToUnitMap LineToUnit;
for (const auto &CU : CUs)
if (auto CUDIE = CU->getUnitDIE())
if (auto StmtOffset = toSectionOffset(CUDIE.find(DW_AT_stmt_list)))
LineToUnit.insert(std::make_pair(*StmtOffset, &*CU));
for (const auto &TUS : TUSections)
for (const auto &TU : TUS)
if (auto TUDIE = TU->getUnitDIE())
if (auto StmtOffset = toSectionOffset(TUDIE.find(DW_AT_stmt_list)))
LineToUnit.insert(std::make_pair(*StmtOffset, &*TU));
return LineToUnit;
}
void DWARFContext::dump(
raw_ostream &OS, DIDumpOptions DumpOpts,
std::array<Optional<uint64_t>, DIDT_ID_Count> DumpOffsets) {
@ -372,63 +350,40 @@ void DWARFContext::dump(
set.dump(OS);
}
if (shouldDump(Explicit, ".debug_line", DIDT_ID_DebugLine,
DObj->getLineSection().Data)) {
LineToUnitMap LineToUnit =
buildLineToUnitMap(compile_units(), type_unit_sections());
unsigned Offset = 0;
DWARFDataExtractor LineData(*DObj, DObj->getLineSection(), isLittleEndian(),
0);
while (Offset < LineData.getData().size()) {
DWARFUnit *U = nullptr;
auto It = LineToUnit.find(Offset);
if (It != LineToUnit.end())
U = It->second;
LineData.setAddressSize(U ? U->getAddressByteSize() : 0);
DWARFDebugLine::LineTable LineTable;
if (DumpOffset && Offset != *DumpOffset) {
// Find the size of this part of the line table section and skip it.
unsigned OldOffset = Offset;
LineTable.Prologue.parse(LineData, &Offset, *this, U);
Offset = OldOffset + LineTable.Prologue.TotalLength +
LineTable.Prologue.sizeofTotalLength();
auto DumpLineSection = [&](DWARFDebugLine::SectionParser Parser,
DIDumpOptions DumpOpts) {
while (!Parser.done()) {
if (DumpOffset && Parser.getOffset() != *DumpOffset) {
Parser.skip();
continue;
}
// Verbose dumping is done during parsing and not on the intermediate
// representation.
OS << "debug_line[" << format("0x%8.8x", Offset) << "]\n";
unsigned OldOffset = Offset;
OS << "debug_line[" << format("0x%8.8x", Parser.getOffset()) << "]\n";
if (DumpOpts.Verbose) {
LineTable.parse(LineData, &Offset, *this, U, &OS);
Parser.parseNext(DWARFDebugLine::warn, DWARFDebugLine::warnForError,
&OS);
} else {
LineTable.parse(LineData, &Offset, *this, U);
LineTable.dump(OS, DIDumpOptions());
DWARFDebugLine::LineTable LineTable = Parser.parseNext();
LineTable.dump(OS, DumpOpts);
}
// Check for unparseable prologue, to avoid infinite loops.
if (OldOffset == Offset)
break;
}
};
if (shouldDump(Explicit, ".debug_line", DIDT_ID_DebugLine,
DObj->getLineSection().Data)) {
DWARFDataExtractor LineData(*DObj, DObj->getLineSection(), isLittleEndian(),
0);
DWARFDebugLine::SectionParser Parser(LineData, *this, compile_units(),
type_unit_sections());
DumpLineSection(Parser, DumpOpts);
}
if (shouldDump(ExplicitDWO, ".debug_line.dwo", DIDT_ID_DebugLine,
DObj->getLineDWOSection().Data)) {
LineToUnitMap LineToUnit =
buildLineToUnitMap(dwo_compile_units(), dwo_type_unit_sections());
unsigned Offset = 0;
DWARFDataExtractor LineData(*DObj, DObj->getLineDWOSection(),
isLittleEndian(), 0);
while (Offset < LineData.getData().size()) {
DWARFUnit *U = nullptr;
auto It = LineToUnit.find(Offset);
if (It != LineToUnit.end())
U = It->second;
DWARFDebugLine::LineTable LineTable;
unsigned OldOffset = Offset;
if (!LineTable.Prologue.parse(LineData, &Offset, *this, U))
break;
if (!DumpOffset || OldOffset == *DumpOffset)
LineTable.dump(OS, DumpOpts);
}
DWARFDebugLine::SectionParser Parser(LineData, *this, dwo_compile_units(),
dwo_type_unit_sections());
DumpLineSection(Parser, DumpOpts);
}
if (shouldDump(Explicit, ".debug_cu_index", DIDT_ID_DebugCUIndex,
@ -786,8 +741,20 @@ const AppleAcceleratorTable &DWARFContext::getAppleObjC() {
DObj->getStringSection(), isLittleEndian());
}
const DWARFLineTable *
const DWARFDebugLine::LineTable *
DWARFContext::getLineTableForUnit(DWARFUnit *U) {
Expected<const DWARFDebugLine::LineTable *> ExpectedLineTable =
getLineTableForUnit(U, DWARFDebugLine::warn);
if (!ExpectedLineTable) {
DWARFDebugLine::warnForError(ExpectedLineTable.takeError());
return nullptr;
}
return *ExpectedLineTable;
}
Expected<const DWARFDebugLine::LineTable *>
DWARFContext::getLineTableForUnit(DWARFUnit *U,
std::function<void(StringRef)> WarnCallback) {
if (!Line)
Line.reset(new DWARFDebugLine);
@ -811,7 +778,8 @@ DWARFContext::getLineTableForUnit(DWARFUnit *U) {
// We have to parse it first.
DWARFDataExtractor lineData(*DObj, U->getLineSection(), isLittleEndian(),
U->getAddressByteSize());
return Line->getOrParseLineTable(lineData, stmtOffset, *this, U);
return Line->getOrParseLineTable(lineData, stmtOffset, *this, U,
WarnCallback);
}
void DWARFContext::parseCompileUnits() {

View File

@ -273,10 +273,24 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData,
return true;
}
bool DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
uint32_t *OffsetPtr,
const DWARFContext &Ctx,
const DWARFUnit *U) {
template <typename... Ts>
static std::string formatErrorString(char const *Fmt, const Ts &... Vals) {
std::string Buffer;
raw_string_ostream Stream(Buffer);
Stream << format(Fmt, Vals...);
return Stream.str();
}
template <typename... Ts>
static Error createError(char const *Fmt, const Ts &... Vals) {
return make_error<StringError>(formatErrorString(Fmt, Vals...),
inconvertibleErrorCode());
}
Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
uint32_t *OffsetPtr,
const DWARFContext &Ctx,
const DWARFUnit *U) {
const uint64_t PrologueOffset = *OffsetPtr;
clear();
@ -285,11 +299,16 @@ bool DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
FormParams.Format = dwarf::DWARF64;
TotalLength = DebugLineData.getU64(OffsetPtr);
} else if (TotalLength >= 0xffffff00) {
return false;
return createError(
"parsing line table prologue at offset 0x%8.8" PRIx64
" unsupported reserved unit length found of value 0x%8.8" PRIx64,
PrologueOffset, TotalLength);
}
FormParams.Version = DebugLineData.getU16(OffsetPtr);
if (getVersion() < 2)
return false;
return createError("parsing line table prologue at offset 0x%8.8" PRIx64
" found unsupported version 0x%2.2" PRIx16,
PrologueOffset, getVersion());
if (getVersion() >= 5) {
FormParams.AddrSize = DebugLineData.getU8(OffsetPtr);
@ -319,26 +338,22 @@ bool DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData,
if (!parseV5DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset,
FormParams, Ctx, U, ContentTypes,
IncludeDirectories, FileNames)) {
WithColor::warning() << format(
return createError(
"parsing line table prologue at 0x%8.8" PRIx64
" found an invalid directory or file table description at"
" 0x%8.8" PRIx64 "\n",
" 0x%8.8" PRIx64,
PrologueOffset, (uint64_t)*OffsetPtr);
return false;
}
} else
parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset,
ContentTypes, IncludeDirectories, FileNames);
if (*OffsetPtr != EndPrologueOffset) {
WithColor::warning() << format(
"parsing line table prologue at 0x%8.8" PRIx64
" should have ended at 0x%8.8" PRIx64 " but it ended at 0x%8.8" PRIx64
"\n",
PrologueOffset, EndPrologueOffset, (uint64_t)*OffsetPtr);
return false;
}
return true;
if (*OffsetPtr != EndPrologueOffset)
return createError("parsing line table prologue at 0x%8.8" PRIx64
" should have ended at 0x%8.8" PRIx64
" but it ended at 0x%8.8" PRIx64,
PrologueOffset, EndPrologueOffset, (uint64_t)*OffsetPtr);
return Error::success();
}
DWARFDebugLine::Row::Row(bool DefaultIsStmt) { reset(DefaultIsStmt); }
@ -447,36 +462,34 @@ DWARFDebugLine::getLineTable(uint32_t Offset) const {
return nullptr;
}
const DWARFDebugLine::LineTable *
DWARFDebugLine::getOrParseLineTable(DWARFDataExtractor &DebugLineData,
uint32_t Offset, const DWARFContext &Ctx,
const DWARFUnit *U) {
Expected<const DWARFDebugLine::LineTable *> DWARFDebugLine::getOrParseLineTable(
DWARFDataExtractor &DebugLineData, uint32_t Offset, const DWARFContext &Ctx,
const DWARFUnit *U, std::function<void(StringRef)> WarnCallback) {
if (!DebugLineData.isValidOffset(Offset))
return nullptr;
return createError("offset 0x%8.8" PRIx64
" is not a valid debug line section offset",
Offset);
std::pair<LineTableIter, bool> Pos =
LineTableMap.insert(LineTableMapTy::value_type(Offset, LineTable()));
LineTable *LT = &Pos.first->second;
if (Pos.second) {
if (!LT->parse(DebugLineData, &Offset, Ctx, U))
return nullptr;
if (Error Err = LT->parse(DebugLineData, &Offset, Ctx, U, WarnCallback))
return std::move(Err);
return LT;
}
return LT;
}
bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData,
uint32_t *OffsetPtr,
const DWARFContext &Ctx,
const DWARFUnit *U, raw_ostream *OS) {
Error DWARFDebugLine::LineTable::parse(
DWARFDataExtractor &DebugLineData, uint32_t *OffsetPtr,
const DWARFContext &Ctx, const DWARFUnit *U,
std::function<void(StringRef)> WarnCallback, raw_ostream *OS) {
const uint32_t DebugLineOffset = *OffsetPtr;
clear();
if (!Prologue.parse(DebugLineData, OffsetPtr, Ctx, U)) {
// Restore our offset and return false to indicate failure!
*OffsetPtr = DebugLineOffset;
return false;
}
Error PrologueErr = Prologue.parse(DebugLineData, OffsetPtr, Ctx, U);
if (OS) {
// The presence of OS signals verbose dumping.
@ -485,6 +498,9 @@ bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData,
Prologue.dump(*OS, DumpOptions);
}
if (PrologueErr)
return PrologueErr;
const uint32_t EndOffset =
DebugLineOffset + Prologue.TotalLength + Prologue.sizeofTotalLength();
@ -554,13 +570,10 @@ bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData,
if (DebugLineData.getAddressSize() == 0)
DebugLineData.setAddressSize(Len - 1);
else if (DebugLineData.getAddressSize() != Len - 1) {
WithColor::warning()
<< format("mismatching address size at offset 0x%8.8" PRIx32
" expected 0x%2.2" PRIx8 " found 0x%2.2" PRIx64 "\n",
ExtOffset, DebugLineData.getAddressSize(), Len - 1);
// Skip the rest of the line-number program.
*OffsetPtr = EndOffset;
return false;
return createError("mismatching address size at offset 0x%8.8" PRIx32
" expected 0x%2.2" PRIx8 " found 0x%2.2" PRIx64,
ExtOffset, DebugLineData.getAddressSize(),
Len - 1);
}
State.Row.Address = DebugLineData.getRelocatedAddress(OffsetPtr);
if (OS)
@ -621,15 +634,10 @@ bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData,
}
// Make sure the stated and parsed lengths are the same.
// Otherwise we have an unparseable line-number program.
if (*OffsetPtr - ExtOffset != Len) {
WithColor::warning()
<< format("unexpected line op length at offset 0x%8.8" PRIx32
" expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx32 "\n",
ExtOffset, Len, *OffsetPtr - ExtOffset);
// Skip the rest of the line-number program.
*OffsetPtr = EndOffset;
return false;
}
if (*OffsetPtr - ExtOffset != Len)
return createError("unexpected line op length at offset 0x%8.8" PRIx32
" expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx32,
ExtOffset, Len, *OffsetPtr - ExtOffset);
} else if (Opcode < Prologue.OpcodeBase) {
if (OS)
*OS << LNStandardString(Opcode);
@ -833,8 +841,7 @@ bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData,
}
if (!State.Sequence.Empty)
WithColor::warning() << "last sequence in debug line table is not"
"terminated!\n";
WarnCallback("last sequence in debug line table is not terminated!");
// Sort all sequences so that address lookup will work faster.
if (!Sequences.empty()) {
@ -847,7 +854,7 @@ bool DWARFDebugLine::LineTable::parse(DWARFDataExtractor &DebugLineData,
// rudimentary sequences for address ranges [0x0, 0xsomething).
}
return EndOffset;
return Error::success();
}
uint32_t
@ -1027,3 +1034,96 @@ bool DWARFDebugLine::LineTable::getFileLineInfoForAddress(
Result.Source = getSourceByIndex(Row.File, Kind);
return true;
}
// We want to supply the Unit associated with a .debug_line[.dwo] table when
// we dump it, if possible, but still dump the table even if there isn't a Unit.
// Therefore, collect up handles on all the Units that point into the
// line-table section.
static DWARFDebugLine::SectionParser::LineToUnitMap
buildLineToUnitMap(DWARFDebugLine::SectionParser::cu_range CUs,
DWARFDebugLine::SectionParser::tu_range TUSections) {
DWARFDebugLine::SectionParser::LineToUnitMap LineToUnit;
for (const auto &CU : CUs)
if (auto CUDIE = CU->getUnitDIE())
if (auto StmtOffset = toSectionOffset(CUDIE.find(DW_AT_stmt_list)))
LineToUnit.insert(std::make_pair(*StmtOffset, &*CU));
for (const auto &TUS : TUSections)
for (const auto &TU : TUS)
if (auto TUDIE = TU->getUnitDIE())
if (auto StmtOffset = toSectionOffset(TUDIE.find(DW_AT_stmt_list)))
LineToUnit.insert(std::make_pair(*StmtOffset, &*TU));
return LineToUnit;
}
DWARFDebugLine::SectionParser::SectionParser(DWARFDataExtractor &Data,
const DWARFContext &C,
cu_range CUs, tu_range TUs)
: DebugLineData(Data), Context(C) {
LineToUnit = buildLineToUnitMap(CUs, TUs);
if (!DebugLineData.isValidOffset(Offset))
Done = true;
}
bool DWARFDebugLine::Prologue::totalLengthIsValid() const {
return TotalLength == 0xffffffff || TotalLength < 0xffffff00;
}
DWARFDebugLine::LineTable DWARFDebugLine::SectionParser::parseNext(
function_ref<void(StringRef)> StringCallback,
function_ref<void(Error)> ErrorCallback, raw_ostream *OS) {
assert(DebugLineData.isValidOffset(Offset) &&
"parsing should have terminated");
DWARFUnit *U = prepareToParse(Offset);
uint32_t OldOffset = Offset;
LineTable LT;
Error Err = LT.parse(DebugLineData, &Offset, Context, U, StringCallback, OS);
ErrorCallback(std::move(Err));
moveToNextTable(OldOffset, LT.Prologue);
return LT;
}
void DWARFDebugLine::SectionParser::skip(
function_ref<void(Error)> ErrorCallback) {
assert(DebugLineData.isValidOffset(Offset) &&
"parsing should have terminated");
DWARFUnit *U = prepareToParse(Offset);
uint32_t OldOffset = Offset;
LineTable LT;
Error Err = LT.Prologue.parse(DebugLineData, &Offset, Context, U);
ErrorCallback(std::move(Err));
moveToNextTable(OldOffset, LT.Prologue);
}
DWARFUnit *DWARFDebugLine::SectionParser::prepareToParse(uint32_t Offset) {
DWARFUnit *U = nullptr;
auto It = LineToUnit.find(Offset);
if (It != LineToUnit.end())
U = It->second;
DebugLineData.setAddressSize(U ? U->getAddressByteSize() : 0);
return U;
}
void DWARFDebugLine::SectionParser::moveToNextTable(uint32_t OldOffset,
const Prologue &P) {
// If the length field is not valid, we don't know where the next table is, so
// cannot continue to parse. Mark the parser as done, and leave the Offset
// value as it currently is. This will be the end of the bad length field.
if (!P.totalLengthIsValid()) {
Done = true;
return;
}
Offset = OldOffset + P.TotalLength + P.sizeofTotalLength();
if (!DebugLineData.isValidOffset(Offset)) {
Done = true;
}
}
void DWARFDebugLine::warn(StringRef Message) {
WithColor::warning() << Message << '\n';
}
void DWARFDebugLine::warnForError(Error Err) {
handleAllErrors(std::move(Err),
[](ErrorInfoBase &Info) { warn(Info.message()); });
}

View File

@ -150,100 +150,3 @@ LT2_end:
# ERR: warning: unexpected line op length at offset 0x0000005e
# ERR-SAME: expected 0x02 found 0x01
# The above parsing errors still let us move to the next unit.
# If the prologue is bogus, we need to bail out because we can't
# even find the next unit.
# DWARF v4 line-table header #3.
LT3_start:
.long LT3_end-LT3_version # Length of Unit (DWARF-32 format)
LT3_version:
.short 4 # DWARF version number
.long LT3_header_end-LT3_params # Length of Prologue
LT3_params:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0 # Standard Opcode Lengths
.byte 1
.byte 1
.byte 1
.byte 1
.byte 0
.byte 0
.byte 0
.byte 1
.byte 0
.byte 0
.byte 1
# No directories.
.byte 0
# No files.
.byte 0
# Extra junk at the end of the prologue, so the length isn't right.
.long 0
LT3_header_end:
# Real opcode and operand.
.byte 0
.byte 9
.byte 2 # DW_LNE_set_address
.quad .text
# Real opcode with incorrect length.
.byte 0
.byte 2 # Wrong length, should be 1.
.byte 1 # DW_LNE_end_sequence
LT3_end:
# We should have bailed out above, so never see this in the dump.
# DWARF v4 line-table header #4.
LT4_start:
.long LT4_end-LT4_version # Length of Unit (DWARF-32 format)
LT4_version:
.short 4 # DWARF version number
.long LT4_header_end-LT4_params # Length of Prologue
LT4_params:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0 # Standard Opcode Lengths
.byte 1
.byte 1
.byte 1
.byte 1
.byte 0
.byte 0
.byte 0
.byte 1
.byte 0
.byte 0
.byte 1
# No directories.
.byte 0
# No files.
.byte 0
LT4_header_end:
# Real opcode and operand.
.byte 0
.byte 9
.byte 2 # DW_LNE_set_address
.quad .text
# Real opcode with correct length.
.byte 0
.byte 1
.byte 1 # DW_LNE_end_sequence
LT4_end:
# Look for the dump of unit 3, and don't want unit 4.
# CHECK: Line table prologue:
# CHECK-NOT: Line table prologue:
# And look for the error message.
# ERR: warning: parsing line table prologue at 0x0000005f should have
# ERR-SAME: ended at 0x00000081 but it ended at 0x0000007d

View File

@ -0,0 +1,190 @@
.section .debug_line,"",@progbits
# Leading good section
.long .Lunit1_end - .Lunit1_start # Length of Unit (DWARF-32 format)
.Lunit1_start:
.short 4 # DWARF version number
.long .Lprologue1_end-.Lprologue1_start # Length of Prologue
.Lprologue1_start:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths
.asciz "dir1" # Include table
.asciz "dir2"
.byte 0
.asciz "file1" # File table
.byte 0, 0, 0
.asciz "file2"
.byte 1, 0, 0
.byte 0
.Lprologue1_end:
.byte 0, 9, 2 # DW_LNE_set_address
.quad 0x0badbeef
.byte 0, 1, 1 # DW_LNE_end_sequence
.Lunit1_end:
# version 0
.long .Lunit_v0_end - .Lunit_v0_start # unit length
.Lunit_v0_start:
.short 0 # version
.Lunit_v0_end:
# version 1
.long .Lunit_v1_end - .Lunit_v1_start # unit length
.Lunit_v1_start:
.short 1 # version
.Lunit_v1_end:
# version 5 malformed line/include table
.long .Lunit_v5_end - .Lunit_v5_start # unit length
.Lunit_v5_start:
.short 5 # version
.byte 8 # address size
.byte 8 # segment selector
.long .Lprologue_v5_end-.Lprologue_v5_start # Length of Prologue
.Lprologue_v5_start:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths
.byte 0 # directory table (invalid)
.Lprologue_v5_end:
.Lunit_v5_end:
# Short prologue
.long .Lunit_short_prologue_end - .Lunit_short_prologue_start # unit length
.Lunit_short_prologue_start:
.short 4 # version
.long .Lprologue_short_prologue_end-.Lprologue_short_prologue_start - 2 # Length of Prologue
.Lprologue_short_prologue_start:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths
.asciz "dir1" # Include table
.asciz "dir2"
.byte 0
.asciz "file1" # File table
.byte 0, 0, 0
.asciz "file2"
.byte 1, 0, 0
.byte 0
.Lprologue_short_prologue_end:
.Lunit_short_prologue_end:
# Over-long prologue
.long .Lunit_long_prologue_end - .Lunit_long_prologue_start # unit length
.Lunit_long_prologue_start:
.short 4 # version
.long .Lprologue_long_prologue_end-.Lprologue_long_prologue_start + 1 # Length of Prologue
.Lprologue_long_prologue_start:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths
.asciz "dir1" # Include table
.asciz "dir2"
.byte 0
.asciz "file1" # File table
.byte 0, 0, 0
.asciz "file2"
.byte 1, 0, 0
.byte 0
.Lprologue_long_prologue_end:
.Lunit_long_prologue_end:
# Over-long extended opcode
.long .Lunit_long_opcode_end - .Lunit_long_opcode_start # unit length
.Lunit_long_opcode_start:
.short 4 # version
.long .Lprologue_long_opcode_end-.Lprologue_long_opcode_start # Length of Prologue
.Lprologue_long_opcode_start:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths
.asciz "dir1" # Include table
.asciz "dir2"
.byte 0
.asciz "file1" # File table
.byte 0, 0, 0
.asciz "file2"
.byte 1, 0, 0
.byte 0
.Lprologue_long_opcode_end:
.byte 0, 9, 2 # DW_LNE_set_address
.quad 0xabbadaba
.byte 0, 2, 1 # DW_LNE_end_sequence (wrong length)
.byte 0, 9, 2 # DW_LNE_set_address
.quad 0xbabb1e45
.byte 0, 1, 1 # DW_LNE_end_sequence (wrong length)
.Lunit_long_opcode_end:
# No end of sequence
.long .Lunit_no_eos_end - .Lunit_no_eos_start # unit length
.Lunit_no_eos_start:
.short 4 # version
.long .Lprologue_no_eos_end-.Lprologue_no_eos_start # Length of Prologue
.Lprologue_no_eos_start:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths
.asciz "dir1" # Include table
.asciz "dir2"
.byte 0
.asciz "file1" # File table
.byte 0, 0, 0
.asciz "file2"
.byte 1, 0, 0
.byte 0
.Lprologue_no_eos_end:
.byte 0, 9, 2 # DW_LNE_set_address
.quad 0xdeadfade
.byte 1 # DW_LNS_copy
.Lunit_no_eos_end:
# Trailing good section
.long .Lunit_good_end - .Lunit_good_start # Length of Unit (DWARF-32 format)
.Lunit_good_start:
.short 4 # DWARF version number
.long .Lprologue_good_end-.Lprologue_good_start # Length of Prologue
.Lprologue_good_start:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths
.asciz "dir1" # Include table
.asciz "dir2"
.byte 0
.asciz "file1" # File table
.byte 0, 0, 0
.asciz "file2"
.byte 1, 0, 0
.byte 0
.Lprologue_good_end:
.byte 0, 9, 2 # DW_LNE_set_address
.quad 0xcafebabe
.byte 0, 1, 1 # DW_LNE_end_sequence
.Lunit_good_end:

View File

@ -0,0 +1,57 @@
.section .debug_line,"",@progbits
# Leading good section
.long .Lunit1_end - .Lunit1_start # Length of Unit (DWARF-32 format)
.Lunit1_start:
.short 4 # DWARF version number
.long .Lprologue1_end-.Lprologue1_start # Length of Prologue
.Lprologue1_start:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths
.asciz "dir1" # Include table
.asciz "dir2"
.byte 0
.asciz "file1" # File table
.byte 0, 0, 0
.asciz "file2"
.byte 1, 0, 0
.byte 0
.Lprologue1_end:
.byte 0, 9, 2 # DW_LNE_set_address
.quad 0x0badbeef
.byte 0, 1, 1 # DW_LNE_end_sequence
.Lunit1_end:
# Malformed section
.long 0xfffffffe # reserved unit length
# Trailing good section
.long .Lunit3_end - .Lunit3_start # Length of Unit (DWARF-32 format)
.Lunit3_start:
.short 4 # DWARF version number
.long .Lprologue3_end-.Lprologue3_start # Length of Prologue
.Lprologue3_start:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths
.asciz "dir1" # Include table
.asciz "dir2"
.byte 0
.asciz "file1" # File table
.byte 0, 0, 0
.asciz "file2"
.byte 1, 0, 0
.byte 0
.Lprologue3_end:
.byte 0, 9, 2 # DW_LNE_set_address
.quad 0xcafebabe
.byte 0, 1, 1 # DW_LNE_end_sequence
.Lunit3_end:

View File

@ -0,0 +1,91 @@
# Test the different error cases in the debug line parsing and how they prevent
# or don't prevent further dumping of section contents.
# RUN: llvm-mc -triple x86_64-pc-linux %S/Inputs/debug_line_reserved_length.s -filetype=obj -o %t-reserved.o
# RUN: llvm-dwarfdump -debug-line %t-reserved.o 2> %t-reserved.err | FileCheck %s --check-prefixes=FIRST,FATAL
# RUN: FileCheck %s --input-file=%t-reserved.err --check-prefix=RESERVED
# RUN: llvm-dwarfdump -debug-line %t-reserved.o -verbose 2> %t-reserved-verbose.err | FileCheck %s --check-prefixes=FIRST,FATAL
# RUN: FileCheck %s --input-file=%t-reserved-verbose.err --check-prefix=RESERVED
# We should still produce warnings for malformed tables after the specified unit.
# RUN: llvm-dwarfdump -debug-line=0 %t-reserved.o 2> %t-reserved-off-first.err | FileCheck %s --check-prefixes=FIRST,NOLATER
# RUN: FileCheck %s --input-file=%t-reserved-off-first.err --check-prefix=RESERVED
# Stop looking for the specified unit, if a fatally-bad prologue is detected.
# RUN: llvm-dwarfdump -debug-line=0x4b %t-reserved.o 2> %t-reserved-off-last.err | FileCheck %s --check-prefixes=NOFIRST,NOLATER
# RUN: FileCheck %s --input-file=%t-reserved-off-last.err --check-prefix=RESERVED
# RUN: llvm-mc -triple x86_64-pc-linux %S/Inputs/debug_line_malformed.s -filetype=obj -o %t-malformed.o
# RUN: llvm-dwarfdump -debug-line %t-malformed.o 2> %t-malformed.err | FileCheck %s --check-prefixes=FIRST,NONFATAL
# RUN: FileCheck %s --input-file=%t-malformed.err --check-prefixes=ALL,OTHER
# RUN: llvm-dwarfdump -debug-line %t-malformed.o -verbose 2> %t-malformed-verbose.err | FileCheck %s --check-prefixes=FIRST,NONFATAL
# RUN: FileCheck %s --input-file=%t-malformed-verbose.err --check-prefixes=ALL,OTHER
# RUN: llvm-dwarfdump -debug-line=0 %t-malformed.o 2> %t-malformed-off-first.err | FileCheck %s --check-prefixes=FIRST,NOLATER
# RUN: FileCheck %s --input-file=%t-malformed-off-first.err --check-prefix=ALL
# Don't stop looking for the later unit if non-fatal issues are found.
# RUN: llvm-dwarfdump -debug-line=0x183 %t-malformed.o 2> %t-malformed-off-last.err | FileCheck %s --check-prefixes=LASTONLY
# RUN: FileCheck %s --input-file=%t-malformed-off-last.err --check-prefix=ALL
# FIRST: debug_line[0x00000000]
# FIRST: 0x000000000badbeef {{.*}} end_sequence
# NOFIRST-NOT: debug_line[0x00000000]
# NOFIRST-NOT: 0x000000000badbeef {{.*}} end_sequence
# NOLATER-NOT: debug_line[{{.*}}]
# NOLATER-NOT: end_sequence
# For fatal issues, the following table(s) should not be dumped.
# FATAL: debug_line[0x00000048]
# FATAL-NEXT: Line table prologue
# FATAL-NEXT: total_length: 0xfffffffe
# FATAL-NOT: debug_line
# For non-fatal prologue issues, the table prologue should be dumped, and any subsequent tables should also be.
# NONFATAL: debug_line[0x00000048]
# NONFATAL-NEXT: Line table prologue
# NONFATAL-NOT: Address
# NONFATAL: debug_line[0x0000004e]
# NONFATAL-NEXT: Line table prologue
# NONFATAL-NOT: Address
# NONFATAL: debug_line[0x00000054]
# NONFATAL-NEXT: Line table prologue
# NONFATAL-NOT: Address
# NONFATAL: debug_line[0x00000073]
# NONFATAL-NEXT: Line table prologue
# NONFATAL-NOT: Address
# NONFATAL: debug_line[0x000000ad]
# NONFATAL-NEXT: Line table prologue
# NONFATAL-NOT: Address
# NONFATAL: debug_line[0x000000e7]
# Dumping prints the line table prologue and any valid operations up to the point causing the problem.
# NONFATAL-NEXT: Line table prologue
# NONFATAL: 0x00000000abbadaba {{.*}} end_sequence
# NONFATAL-NOT: is_stmt
# For minor issues, we can dump the table.
# NONFATAL: debug_line[0x0000013d]
# NONFATAL-NEXT: Line table prologue
# NONFATAL-NOT: debug_line[{{.*}}]
# NONFATAL: 0x00000000deadfade {{.*}}
# NONFATAL: debug_line[0x00000183]
# NONFATAL-NOT: debug_line[{{.*}}]
# NONFATAL: 0x00000000cafebabe {{.*}} end_sequence
# NONFATAL-NOT: debug_line[{{.*}}]
# LASTONLY-NOT: debug_line[{{.*}}]
# LASTONLY: debug_line[0x00000183]
# LASTONLY: 0x00000000cafebabe {{.*}} end_sequence
# RESERVED: warning: parsing line table prologue at offset 0x00000048 unsupported reserved unit length found of value 0xfffffffe
# ALL-NOT: warning:
# ALL: warning: parsing line table prologue at offset 0x00000048 found unsupported version 0x00
# ALL-NEXT: warning: parsing line table prologue at offset 0x0000004e found unsupported version 0x01
# ALL-NEXT: warning: parsing line table prologue at 0x00000054 found an invalid directory or file table description at 0x00000073
# FIXME - The latter offset in the next line should be 0xad. The filename parsing code does not notice a missing terminating byte.
# ALL-NEXT: warning: parsing line table prologue at 0x00000073 should have ended at 0x000000ab but it ended at 0x000000ac
# ALL-NEXT: warning: parsing line table prologue at 0x000000ad should have ended at 0x000000e8 but it ended at 0x000000e7
# OTHER-NEXT: warning: unexpected line op length at offset 0x0000012e expected 0x02 found 0x01
# OTHER-NEXT: warning: last sequence in debug line table is not terminated!
# ALL-NOT: warning:

View File

@ -3603,7 +3603,10 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit,
DWARFDataExtractor LineExtractor(
OrigDwarf.getDWARFObj(), OrigDwarf.getDWARFObj().getLineSection(),
OrigDwarf.isLittleEndian(), Unit.getOrigUnit().getAddressByteSize());
LineTable.parse(LineExtractor, &StmtOffset, OrigDwarf, &Unit.getOrigUnit());
Error Err = LineTable.parse(LineExtractor, &StmtOffset, OrigDwarf,
&Unit.getOrigUnit());
DWARFDebugLine::warnForError(std::move(Err));
// This vector is the output line table.
std::vector<DWARFDebugLine::Row> NewRows;

View File

@ -17,26 +17,44 @@
using namespace llvm;
using namespace dwarf;
using namespace dwarfgen;
using namespace object;
using namespace utils;
using namespace testing;
namespace {
struct CommonFixture {
CommonFixture()
: LineData("", true, 0),
RecordIssue(std::bind(&CommonFixture::recordIssue, this,
std::placeholders::_1)),
FoundError(Error::success()),
RecordError(std::bind(&CommonFixture::recordError, this,
std::placeholders::_1)){};
struct DebugLineGenerator {
bool init() {
~CommonFixture() { EXPECT_FALSE(FoundError); }
bool setupGenerator(uint16_t Version = 4) {
Triple T = getHostTripleForAddrSize(8);
if (!isConfigurationSupported(T))
return false;
auto ExpectedGenerator = dwarfgen::Generator::create(T, 4);
auto ExpectedGenerator = Generator::create(T, Version);
if (ExpectedGenerator)
Generator.reset(ExpectedGenerator->release());
Gen.reset(ExpectedGenerator->release());
return true;
}
void generate() {
Context = createContext();
assert(Context != nullptr && "test state is not valid");
const DWARFObject &Obj = Context->getDWARFObj();
LineData = DWARFDataExtractor(Obj, Obj.getLineSection(), true, 8);
}
std::unique_ptr<DWARFContext> createContext() {
if (!Generator)
if (!Gen)
return nullptr;
StringRef FileBytes = Generator->generate();
StringRef FileBytes = Gen->generate();
MemoryBufferRef FileBuffer(FileBytes, "dwarf");
auto Obj = object::ObjectFile::createObjectFile(FileBuffer);
if (Obj)
@ -44,21 +62,599 @@ struct DebugLineGenerator {
return nullptr;
}
std::unique_ptr<dwarfgen::Generator> Generator;
DWARFDebugLine::SectionParser setupParser() {
LineTable &LT = Gen->addLineTable(DWARF32);
LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addByte(0xaa);
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
LineTable &LT2 = Gen->addLineTable(DWARF64);
LT2.addExtendedOpcode(9, DW_LNE_set_address,
{{0x11223344, LineTable::Quad}});
LT2.addStandardOpcode(DW_LNS_copy, {});
LT2.addByte(0xbb);
LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
return DWARFDebugLine::SectionParser(LineData, *Context, CUs, TUs);
}
void recordIssue(StringRef Message) { IssueMessage = Message; }
void recordError(Error Err) {
FoundError = joinErrors(std::move(FoundError), std::move(Err));
}
void checkError(ArrayRef<StringRef> ExpectedMsgs, Error Err) {
ASSERT_TRUE(Err.operator bool());
size_t WhichMsg = 0;
Error Remaining =
handleErrors(std::move(Err), [&](const ErrorInfoBase &Actual) {
ASSERT_LT(WhichMsg, ExpectedMsgs.size());
// Use .str(), because googletest doesn't visualise a StringRef
// properly.
EXPECT_EQ(Actual.message(), ExpectedMsgs[WhichMsg++].str());
});
EXPECT_EQ(WhichMsg, ExpectedMsgs.size());
EXPECT_FALSE(Remaining);
}
void checkError(StringRef ExpectedMsg, Error Err) {
checkError(ArrayRef<StringRef>{ExpectedMsg}, std::move(Err));
}
void checkGetOrParseLineTableEmitsError(StringRef ExpectedMsg,
uint64_t Offset = 0) {
auto ExpectedLineTable = Line.getOrParseLineTable(
LineData, Offset, *Context, nullptr, RecordIssue);
EXPECT_FALSE(ExpectedLineTable);
EXPECT_TRUE(IssueMessage.empty());
checkError(ExpectedMsg, ExpectedLineTable.takeError());
}
std::unique_ptr<Generator> Gen;
std::unique_ptr<DWARFContext> Context;
DWARFDataExtractor LineData;
DWARFDebugLine Line;
std::string IssueMessage;
std::function<void(StringRef)> RecordIssue;
Error FoundError;
std::function<void(Error)> RecordError;
SmallVector<std::unique_ptr<DWARFCompileUnit>, 2> CUs;
std::deque<DWARFUnitSection<DWARFTypeUnit>> TUs;
};
TEST(DWARFDebugLine, GetLineTableAtInvalidOffset) {
DebugLineGenerator LineGen;
if (!LineGen.init())
// Fixtures must derive from "Test", but parameterised fixtures from
// "TestWithParam". It does not seem possible to inherit from both, so we share
// the common state in a separate class, inherited by the two fixture classes.
struct DebugLineBasicFixture : public Test, public CommonFixture {};
struct DebugLineParameterisedFixture
: public TestWithParam<std::pair<uint16_t, DwarfFormat>>,
public CommonFixture {
void SetUp() { std::tie(Version, Format) = GetParam(); }
uint16_t Version;
DwarfFormat Format;
};
void checkDefaultPrologue(uint16_t Version, DwarfFormat Format,
DWARFDebugLine::Prologue Prologue,
uint64_t BodyLength) {
// Check version specific fields and values.
uint64_t UnitLength;
uint64_t PrologueLength;
switch (Version) {
case 4:
PrologueLength = 36;
UnitLength = PrologueLength + 2;
EXPECT_EQ(Prologue.MaxOpsPerInst, 1);
break;
case 2:
case 3:
PrologueLength = 35;
UnitLength = PrologueLength + 2;
break;
case 5:
PrologueLength = 39;
UnitLength = PrologueLength + 4;
EXPECT_EQ(Prologue.getAddressSize(), 8);
EXPECT_EQ(Prologue.SegSelectorSize, 0);
break;
default:
llvm_unreachable("unsupported DWARF version");
}
UnitLength += BodyLength + (Format == DWARF32 ? 4 : 8);
EXPECT_EQ(Prologue.TotalLength, UnitLength);
EXPECT_EQ(Prologue.PrologueLength, PrologueLength);
EXPECT_EQ(Prologue.MinInstLength, 1);
EXPECT_EQ(Prologue.DefaultIsStmt, 1);
EXPECT_EQ(Prologue.LineBase, -5);
EXPECT_EQ(Prologue.LineRange, 14);
EXPECT_EQ(Prologue.OpcodeBase, 13);
std::vector<uint8_t> ExpectedLengths = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1};
EXPECT_EQ(Prologue.StandardOpcodeLengths, ExpectedLengths);
ASSERT_EQ(Prologue.IncludeDirectories.size(), 1);
ASSERT_EQ(Prologue.IncludeDirectories[0].getForm(), DW_FORM_string);
EXPECT_STREQ(*Prologue.IncludeDirectories[0].getAsCString(), "a dir");
ASSERT_EQ(Prologue.FileNames.size(), 1);
ASSERT_EQ(Prologue.FileNames[0].Name.getForm(), DW_FORM_string);
EXPECT_STREQ(*Prologue.FileNames[0].Name.getAsCString(), "a file");
}
TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) {
if (!setupGenerator())
return;
generate();
checkGetOrParseLineTableEmitsError(
"offset 0x00000000 is not a valid debug line section offset", 0);
// Repeat to show that an error is reported each time.
checkGetOrParseLineTableEmitsError(
"offset 0x00000000 is not a valid debug line section offset", 0);
// Show that an error is reported for later offsets too.
checkGetOrParseLineTableEmitsError(
"offset 0x00000001 is not a valid debug line section offset", 1);
}
TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) {
if (!setupGenerator())
return;
DWARFDebugLine Line;
std::unique_ptr<DWARFContext> Context = LineGen.createContext();
ASSERT_TRUE(Context != nullptr);
const DWARFObject &Obj = Context->getDWARFObj();
DWARFDataExtractor LineData(Obj, Obj.getLineSection(), true, 8);
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue({{0, LineTable::Byte}});
EXPECT_EQ(Line.getOrParseLineTable(LineData, 0, *Context, nullptr), nullptr);
generate();
checkGetOrParseLineTableEmitsError(
"offset 0x00000001 is not a valid debug line section offset", 1);
}
TEST_P(DebugLineParameterisedFixture, GetOrParseLineTableValidTable) {
if (!setupGenerator(Version))
return;
SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
(Format == DWARF64 ? "DWARF64" : "DWARF32"));
LineTable &LT = Gen->addLineTable(Format);
LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addByte(0xaa);
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
LineTable &LT2 = Gen->addLineTable(Format);
LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x11223344, LineTable::Quad}});
LT2.addStandardOpcode(DW_LNS_copy, {});
LT2.addByte(0xbb);
LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x55667788, LineTable::Quad}});
LT2.addStandardOpcode(DW_LNS_copy, {});
LT2.addByte(0xcc);
LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
auto ExpectedLineTable =
Line.getOrParseLineTable(LineData, 0, *Context, nullptr, RecordIssue);
ASSERT_TRUE(ExpectedLineTable.operator bool());
EXPECT_TRUE(IssueMessage.empty());
const DWARFDebugLine::LineTable *Expected = *ExpectedLineTable;
checkDefaultPrologue(Version, Format, Expected->Prologue, 16);
EXPECT_EQ(Expected->Sequences.size(), 1);
uint64_t SecondOffset =
Expected->Prologue.sizeofTotalLength() + Expected->Prologue.TotalLength;
IssueMessage.clear();
auto ExpectedLineTable2 = Line.getOrParseLineTable(
LineData, SecondOffset, *Context, nullptr, RecordIssue);
ASSERT_TRUE(ExpectedLineTable2.operator bool());
EXPECT_TRUE(IssueMessage.empty());
const DWARFDebugLine::LineTable *Expected2 = *ExpectedLineTable2;
checkDefaultPrologue(Version, Format, Expected2->Prologue, 32);
EXPECT_EQ(Expected2->Sequences.size(), 2);
EXPECT_NE(Expected, Expected2);
// Check that if the same offset is requested, the exact same pointer is
// returned.
IssueMessage.clear();
auto ExpectedLineTable3 =
Line.getOrParseLineTable(LineData, 0, *Context, nullptr, RecordIssue);
ASSERT_TRUE(ExpectedLineTable3.operator bool());
EXPECT_TRUE(IssueMessage.empty());
EXPECT_EQ(Expected, *ExpectedLineTable3);
IssueMessage.clear();
auto ExpectedLineTable4 = Line.getOrParseLineTable(
LineData, SecondOffset, *Context, nullptr, RecordIssue);
ASSERT_TRUE(ExpectedLineTable4.operator bool());
EXPECT_TRUE(IssueMessage.empty());
EXPECT_EQ(Expected2, *ExpectedLineTable4);
// TODO: Add tests that show that the body of the programs have been read
// correctly.
}
TEST_F(DebugLineBasicFixture, ErrorForReservedLength) {
if (!setupGenerator())
return;
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue({{0xffffff00, LineTable::Long}});
generate();
checkGetOrParseLineTableEmitsError(
"parsing line table prologue at offset 0x00000000 unsupported reserved "
"unit length found of value 0xffffff00");
}
TEST_F(DebugLineBasicFixture, ErrorForLowVersion) {
if (!setupGenerator())
return;
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue(
{{LineTable::Half, LineTable::Long}, {1, LineTable::Half}});
generate();
checkGetOrParseLineTableEmitsError("parsing line table prologue at offset "
"0x00000000 found unsupported version "
"0x01");
}
TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) {
if (!setupGenerator(5))
return;
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue({
{19, LineTable::Long}, // unit length
{5, LineTable::Half}, // version
{8, LineTable::Byte}, // addr size
{0, LineTable::Byte}, // segment selector size
{11, LineTable::Long}, // prologue length
{1, LineTable::Byte}, // min instruction length
{1, LineTable::Byte}, // max ops per instruction
{1, LineTable::Byte}, // default is_stmt
{0, LineTable::Byte}, // line base
{14, LineTable::Byte}, // line range
{2, LineTable::Byte}, // opcode base (small to reduce the amount of
// setup required).
{0, LineTable::Byte}, // standard opcode lengths
{0, LineTable::Byte}, // directory entry format count (should not be
// zero).
{0, LineTable::ULEB}, // directories count
{0, LineTable::Byte}, // file name entry format count
{0, LineTable::ULEB} // file name entry count
});
generate();
checkGetOrParseLineTableEmitsError(
"parsing line table prologue at 0x00000000 found an invalid directory or "
"file table description at 0x00000014");
}
TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) {
if (!setupGenerator(Version))
return;
SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
(Format == DWARF64 ? "DWARF64" : "DWARF32"));
LineTable &LT = Gen->addLineTable(Format);
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
++Prologue.PrologueLength;
LT.setPrologue(Prologue);
generate();
uint64_t ExpectedEnd =
Prologue.TotalLength + 1 + Prologue.sizeofTotalLength();
checkGetOrParseLineTableEmitsError(
(Twine("parsing line table prologue at 0x00000000 should have ended at "
"0x000000") +
Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" +
Twine::utohexstr(ExpectedEnd - 1))
.str());
}
TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) {
if (!setupGenerator(Version))
return;
SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
(Format == DWARF64 ? "DWARF64" : "DWARF32"));
LineTable &LT = Gen->addLineTable(Format);
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
// FIXME: Ideally, we'd test for 1 less than expected, but the code does not
// currently fail if missing only the terminator of a v2-4 file table.
if (Version < 5)
Prologue.PrologueLength -= 2;
else
Prologue.PrologueLength -= 1;
LT.setPrologue(Prologue);
generate();
uint64_t ExpectedEnd =
Prologue.TotalLength - 1 + Prologue.sizeofTotalLength();
if (Version < 5)
--ExpectedEnd;
checkGetOrParseLineTableEmitsError(
(Twine("parsing line table prologue at 0x00000000 should have ended at "
"0x000000") +
Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" +
Twine::utohexstr(ExpectedEnd + 1))
.str());
}
INSTANTIATE_TEST_CASE_P(
LineTableTestParams, DebugLineParameterisedFixture,
Values(std::make_pair(
2, DWARF32), // Test lower-bound of v2-3 fields and DWARF32.
std::make_pair(3, DWARF32), // Test upper-bound of v2-3 fields.
std::make_pair(4, DWARF64), // Test v4 fields and DWARF64.
std::make_pair(5, DWARF32), std::make_pair(5, DWARF64)),);
TEST_F(DebugLineBasicFixture, ErrorForInvalidExtendedOpcodeLength) {
if (!setupGenerator())
return;
LineTable &LT = Gen->addLineTable();
// The Length should be 1 for an end sequence opcode.
LT.addExtendedOpcode(2, DW_LNE_end_sequence, {});
generate();
checkGetOrParseLineTableEmitsError("unexpected line op length at offset "
"0x00000030 expected 0x02 found 0x01");
}
TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) {
if (!setupGenerator())
return;
LineTable &LT = Gen->addLineTable();
// The line data extractor expects size 8 (Quad) addresses.
LT.addExtendedOpcode(5, DW_LNE_set_address, {{0x11223344, LineTable::Long}});
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addByte(0xaa);
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
checkGetOrParseLineTableEmitsError(
"mismatching address size at offset 0x00000030 expected 0x08 found 0x04");
}
TEST_F(DebugLineBasicFixture, CallbackUsedForUnterminatedSequence) {
if (!setupGenerator())
return;
LineTable &LT = Gen->addLineTable();
LT.addExtendedOpcode(9, DW_LNE_set_address,
{{0x1122334455667788, LineTable::Quad}});
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addByte(0xaa);
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
LT.addExtendedOpcode(9, DW_LNE_set_address,
{{0x99aabbccddeeff00, LineTable::Quad}});
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addByte(0xbb);
LT.addByte(0xcc);
generate();
auto ExpectedLineTable =
Line.getOrParseLineTable(LineData, 0, *Context, nullptr, RecordIssue);
EXPECT_EQ(IssueMessage,
"last sequence in debug line table is not terminated!");
ASSERT_TRUE(ExpectedLineTable.operator bool());
EXPECT_EQ((*ExpectedLineTable)->Rows.size(), 6);
// The unterminated sequence is not added to the sequence list.
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1);
}
TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) {
if (!setupGenerator())
return;
DWARFDebugLine::SectionParser Parser = setupParser();
EXPECT_EQ(Parser.getOffset(), 0);
ASSERT_FALSE(Parser.done());
DWARFDebugLine::LineTable Parsed = Parser.parseNext(RecordIssue, RecordError);
checkDefaultPrologue(4, DWARF32, Parsed.Prologue, 16);
EXPECT_EQ(Parsed.Sequences.size(), 1);
EXPECT_EQ(Parser.getOffset(), 62);
ASSERT_FALSE(Parser.done());
DWARFDebugLine::LineTable Parsed2 =
Parser.parseNext(RecordIssue, RecordError);
checkDefaultPrologue(4, DWARF64, Parsed2.Prologue, 16);
EXPECT_EQ(Parsed2.Sequences.size(), 1);
EXPECT_EQ(Parser.getOffset(), 136);
EXPECT_TRUE(Parser.done());
EXPECT_TRUE(IssueMessage.empty());
EXPECT_FALSE(FoundError);
}
TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) {
if (!setupGenerator())
return;
DWARFDebugLine::SectionParser Parser = setupParser();
EXPECT_EQ(Parser.getOffset(), 0);
ASSERT_FALSE(Parser.done());
Parser.skip(RecordError);
EXPECT_EQ(Parser.getOffset(), 62);
ASSERT_FALSE(Parser.done());
Parser.skip(RecordError);
EXPECT_EQ(Parser.getOffset(), 136);
EXPECT_TRUE(Parser.done());
EXPECT_FALSE(FoundError);
}
TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) {
if (!setupGenerator())
return;
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
EXPECT_TRUE(Parser.done());
}
TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenParsing) {
if (!setupGenerator())
return;
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue({{0xffffff00, LineTable::Long}});
Gen->addLineTable();
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
Parser.parseNext(RecordIssue, RecordError);
EXPECT_EQ(Parser.getOffset(), 4);
EXPECT_TRUE(Parser.done());
EXPECT_TRUE(IssueMessage.empty());
checkError("parsing line table prologue at offset 0x00000000 unsupported "
"reserved unit length found of value 0xffffff00",
std::move(FoundError));
}
TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenSkipping) {
if (!setupGenerator())
return;
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue({{0xffffff00, LineTable::Long}});
Gen->addLineTable();
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
Parser.skip(RecordError);
EXPECT_EQ(Parser.getOffset(), 4);
EXPECT_TRUE(Parser.done());
checkError("parsing line table prologue at offset 0x00000000 unsupported "
"reserved unit length found of value 0xffffff00",
std::move(FoundError));
}
TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) {
if (!setupGenerator())
return;
LineTable &LT = Gen->addLineTable(DWARF32);
LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
LineTable &LT2 = Gen->addLineTable(DWARF32);
LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
Parser.parseNext(RecordIssue, RecordError);
ASSERT_FALSE(Parser.done());
Parser.parseNext(RecordIssue, RecordError);
EXPECT_TRUE(Parser.done());
EXPECT_TRUE(IssueMessage.empty());
checkError({"parsing line table prologue at offset 0x00000000 found "
"unsupported version 0x00",
"parsing line table prologue at offset 0x00000006 found "
"unsupported version 0x01"},
std::move(FoundError));
}
TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) {
if (!setupGenerator())
return;
LineTable &LT = Gen->addLineTable(DWARF32);
LT.addExtendedOpcode(0x42, DW_LNE_end_sequence, {});
LineTable &LT2 = Gen->addLineTable(DWARF32);
LT2.addExtendedOpcode(9, DW_LNE_set_address,
{{0x1234567890abcdef, LineTable::Quad}});
LT2.addStandardOpcode(DW_LNS_copy, {});
LT2.addByte(0xbb);
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
Parser.parseNext(RecordIssue, RecordError);
EXPECT_TRUE(IssueMessage.empty());
ASSERT_FALSE(Parser.done());
checkError(
"unexpected line op length at offset 0x00000030 expected 0x42 found 0x01",
std::move(FoundError));
// Reset the error state so that it does not confuse the next set of checks.
FoundError = Error::success();
Parser.parseNext(RecordIssue, RecordError);
EXPECT_TRUE(Parser.done());
EXPECT_EQ(IssueMessage,
"last sequence in debug line table is not terminated!");
EXPECT_TRUE(!FoundError);
}
TEST_F(DebugLineBasicFixture,
ParserReportsPrologueErrorsInEachTableWhenSkipping) {
if (!setupGenerator())
return;
LineTable &LT = Gen->addLineTable(DWARF32);
LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
LineTable &LT2 = Gen->addLineTable(DWARF32);
LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
Parser.skip(RecordError);
ASSERT_FALSE(Parser.done());
Parser.skip(RecordError);
EXPECT_TRUE(Parser.done());
checkError({"parsing line table prologue at offset 0x00000000 found "
"unsupported version 0x00",
"parsing line table prologue at offset 0x00000006 found "
"unsupported version 0x01"},
std::move(FoundError));
}
TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) {
if (!setupGenerator())
return;
LineTable &LT = Gen->addLineTable(DWARF32);
LT.addExtendedOpcode(42, DW_LNE_end_sequence, {});
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
Parser.skip(RecordError);
EXPECT_TRUE(Parser.done());
EXPECT_TRUE(!FoundError);
}
} // end anonymous namespace

View File

@ -105,6 +105,230 @@ dwarfgen::DIE dwarfgen::CompileUnit::getUnitDIE() {
return dwarfgen::DIE(this, &DU.getUnitDie());
}
//===----------------------------------------------------------------------===//
/// dwarfgen::LineTable implementation.
//===----------------------------------------------------------------------===//
DWARFDebugLine::Prologue dwarfgen::LineTable::createBasicPrologue() const {
DWARFDebugLine::Prologue P;
switch (Version) {
case 2:
case 3:
P.TotalLength = 41;
P.PrologueLength = 35;
break;
case 4:
P.TotalLength = 42;
P.PrologueLength = 36;
break;
case 5:
P.TotalLength = 47;
P.PrologueLength = 39;
P.FormParams.AddrSize = AddrSize;
break;
default:
llvm_unreachable("unsupported version");
}
if (Format == DWARF64) {
P.TotalLength += 4;
P.FormParams.Format = DWARF64;
}
P.FormParams.Version = Version;
P.MinInstLength = 1;
P.MaxOpsPerInst = 1;
P.DefaultIsStmt = 1;
P.LineBase = -5;
P.LineRange = 14;
P.OpcodeBase = 13;
P.StandardOpcodeLengths = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1};
P.IncludeDirectories.push_back(DWARFFormValue(DW_FORM_string));
P.IncludeDirectories.back().setPValue("a dir");
P.FileNames.push_back(DWARFDebugLine::FileNameEntry());
P.FileNames.back().Name.setPValue("a file");
P.FileNames.back().Name.setForm(DW_FORM_string);
return P;
}
void dwarfgen::LineTable::setPrologue(DWARFDebugLine::Prologue NewPrologue) {
Prologue = NewPrologue;
CustomPrologue.clear();
}
void dwarfgen::LineTable::setCustomPrologue(
ArrayRef<ValueAndLength> NewPrologue) {
Prologue.reset();
CustomPrologue = NewPrologue;
}
void dwarfgen::LineTable::addByte(uint8_t Value) {
Contents.push_back({Value, Byte});
}
void dwarfgen::LineTable::addStandardOpcode(uint8_t Opcode,
ArrayRef<ValueAndLength> Operands) {
Contents.push_back({Opcode, Byte});
Contents.insert(Contents.end(), Operands.begin(), Operands.end());
}
void dwarfgen::LineTable::addExtendedOpcode(uint64_t Length, uint8_t Opcode,
ArrayRef<ValueAndLength> Operands) {
Contents.push_back({0, Byte});
Contents.push_back({Length, ULEB});
Contents.push_back({Opcode, Byte});
Contents.insert(Contents.end(), Operands.begin(), Operands.end());
}
void dwarfgen::LineTable::generate(MCContext &MC, AsmPrinter &Asm) const {
MC.setDwarfVersion(Version);
MCSymbol *EndSymbol = nullptr;
if (!CustomPrologue.empty()) {
writeData(CustomPrologue, Asm);
} else if (!Prologue) {
EndSymbol = writeDefaultPrologue(Asm);
} else {
writePrologue(Asm);
}
writeData(Contents, Asm);
if (EndSymbol != nullptr)
Asm.OutStreamer->EmitLabel(EndSymbol);
}
void dwarfgen::LineTable::writeData(ArrayRef<ValueAndLength> Data,
AsmPrinter &Asm) const {
for (auto Entry : Data) {
switch (Entry.Length) {
case Byte:
case Half:
case Long:
case Quad:
Asm.OutStreamer->EmitIntValue(Entry.Value, Entry.Length);
break;
case ULEB:
Asm.EmitULEB128(Entry.Value);
break;
case SLEB:
Asm.EmitSLEB128(Entry.Value);
break;
default:
llvm_unreachable("unsupported ValueAndLength Length value");
}
}
}
MCSymbol *dwarfgen::LineTable::writeDefaultPrologue(AsmPrinter &Asm) const {
MCSymbol *UnitStart = Asm.createTempSymbol("line_unit_start");
MCSymbol *UnitEnd = Asm.createTempSymbol("line_unit_end");
if (Format == DwarfFormat::DWARF64) {
Asm.emitInt32(0xffffffff);
Asm.EmitLabelDifference(UnitEnd, UnitStart, 8);
} else {
Asm.EmitLabelDifference(UnitEnd, UnitStart, 4);
}
Asm.OutStreamer->EmitLabel(UnitStart);
Asm.emitInt16(Version);
if (Version == 5) {
Asm.emitInt8(AddrSize);
Asm.emitInt8(SegSize);
}
MCSymbol *PrologueStart = Asm.createTempSymbol("line_prologue_start");
MCSymbol *PrologueEnd = Asm.createTempSymbol("line_prologue_end");
Asm.EmitLabelDifference(PrologueEnd, PrologueStart,
Format == DwarfFormat::DWARF64 ? 8 : 4);
Asm.OutStreamer->EmitLabel(PrologueStart);
DWARFDebugLine::Prologue DefaultPrologue = createBasicPrologue();
writeProloguePayload(DefaultPrologue, Asm);
Asm.OutStreamer->EmitLabel(PrologueEnd);
return UnitEnd;
}
void dwarfgen::LineTable::writePrologue(AsmPrinter &Asm) const {
if (Format == DwarfFormat::DWARF64) {
Asm.emitInt32(0xffffffff);
Asm.emitInt64(Prologue->TotalLength);
} else {
Asm.emitInt32(Prologue->TotalLength);
}
Asm.emitInt16(Prologue->getVersion());
if (Version == 5) {
Asm.emitInt8(Prologue->getAddressSize());
Asm.emitInt8(Prologue->SegSelectorSize);
}
if (Format == DwarfFormat::DWARF64)
Asm.emitInt64(Prologue->PrologueLength);
else
Asm.emitInt32(Prologue->PrologueLength);
writeProloguePayload(*Prologue, Asm);
}
static void writeCString(StringRef Str, AsmPrinter &Asm) {
Asm.OutStreamer->EmitBytes(Str);
Asm.emitInt8(0);
}
static void writeV2IncludeAndFileTable(const DWARFDebugLine::Prologue &Prologue,
AsmPrinter &Asm) {
for (auto Include : Prologue.IncludeDirectories) {
assert(Include.getAsCString() && "expected a string form for include dir");
writeCString(*Include.getAsCString(), Asm);
}
Asm.emitInt8(0);
for (auto File : Prologue.FileNames) {
assert(File.Name.getAsCString() && "expected a string form for file name");
writeCString(*File.Name.getAsCString(), Asm);
Asm.EmitULEB128(File.DirIdx);
Asm.EmitULEB128(File.ModTime);
Asm.EmitULEB128(File.Length);
}
Asm.emitInt8(0);
}
static void writeV5IncludeAndFileTable(const DWARFDebugLine::Prologue &Prologue,
AsmPrinter &Asm) {
Asm.emitInt8(1); // directory_entry_format_count.
// TODO: Add support for other content descriptions - we currently only
// support a single DW_LNCT_path/DW_FORM_string.
Asm.EmitULEB128(DW_LNCT_path);
Asm.EmitULEB128(DW_FORM_string);
Asm.EmitULEB128(Prologue.IncludeDirectories.size());
for (auto Include : Prologue.IncludeDirectories) {
assert(Include.getAsCString() && "expected a string form for include dir");
writeCString(*Include.getAsCString(), Asm);
}
Asm.emitInt8(1); // file_name_entry_format_count.
Asm.EmitULEB128(DW_LNCT_path);
Asm.EmitULEB128(DW_FORM_string);
Asm.EmitULEB128(Prologue.FileNames.size());
for (auto File : Prologue.FileNames) {
assert(File.Name.getAsCString() && "expected a string form for file name");
writeCString(*File.Name.getAsCString(), Asm);
}
}
void dwarfgen::LineTable::writeProloguePayload(
const DWARFDebugLine::Prologue &Prologue, AsmPrinter &Asm) const {
Asm.emitInt8(Prologue.MinInstLength);
if (Version >= 4)
Asm.emitInt8(Prologue.MaxOpsPerInst);
Asm.emitInt8(Prologue.DefaultIsStmt);
Asm.emitInt8(Prologue.LineBase);
Asm.emitInt8(Prologue.LineRange);
Asm.emitInt8(Prologue.OpcodeBase);
for (auto Length : Prologue.StandardOpcodeLengths) {
Asm.emitInt8(Length);
}
if (Version < 5)
writeV2IncludeAndFileTable(Prologue, Asm);
else
writeV5IncludeAndFileTable(Prologue, Asm);
}
//===----------------------------------------------------------------------===//
/// dwarfgen::Generator implementation.
//===----------------------------------------------------------------------===//
@ -244,6 +468,10 @@ StringRef dwarfgen::Generator::generate() {
Asm->emitDwarfDIE(*CU->getUnitDIE().Die);
}
MS->SwitchSection(MOFI->getDwarfLineSection());
for (auto &LT : LineTables)
LT->generate(*MC, *Asm);
MS->Finish();
if (FileBytes.empty())
return StringRef();
@ -263,7 +491,13 @@ bool dwarfgen::Generator::saveFile(StringRef Path) {
}
dwarfgen::CompileUnit &dwarfgen::Generator::addCompileUnit() {
CompileUnits.push_back(std::unique_ptr<CompileUnit>(
new CompileUnit(*this, Version, Asm->getPointerSize())));
CompileUnits.push_back(
make_unique<CompileUnit>(*this, Version, Asm->getPointerSize()));
return *CompileUnits.back();
}
dwarfgen::LineTable &dwarfgen::Generator::addLineTable(DwarfFormat Format) {
LineTables.push_back(
make_unique<LineTable>(*this, Version, Format, Asm->getPointerSize()));
return *LineTables.back();
}

View File

@ -153,6 +153,74 @@ public:
void setLength(uint64_t Length) { DU.setLength(Length); }
};
/// A DWARF line unit-like class used to generate DWARF line units.
///
/// Instances of this class are created by instances of the Generator class.
class LineTable {
public:
enum ValueLength { Byte = 1, Half = 2, Long = 4, Quad = 8, ULEB, SLEB };
struct ValueAndLength {
uint64_t Value;
ValueLength Length;
};
LineTable(Generator &DG, uint16_t Version, dwarf::DwarfFormat Format,
uint8_t AddrSize, uint8_t SegSize = 0)
: DG(DG), Version(Version), Format(Format), AddrSize(AddrSize),
SegSize(SegSize) {
assert(Version >= 2 && Version <= 5 && "unsupported version");
}
// Create a Prologue suitable to pass to setPrologue, with a single file and
// include directory entry.
DWARFDebugLine::Prologue createBasicPrologue() const;
// Set or replace the current prologue with the specified prologue. If no
// prologue is set, a default one will be used when generating.
void setPrologue(DWARFDebugLine::Prologue NewPrologue);
// Used to write an arbitrary payload instead of the standard prologue. This
// is useful if you wish to test handling of corrupt .debug_line sections.
void setCustomPrologue(ArrayRef<ValueAndLength> NewPrologue);
// Add a byte to the program, with the given value. This can be used to
// specify a special opcode, or to add arbitrary contents to the section.
void addByte(uint8_t Value);
// Add a standard opcode to the program. The opcode and operands do not have
// to be valid.
void addStandardOpcode(uint8_t Opcode, ArrayRef<ValueAndLength> Operands);
// Add an extended opcode to the program with the specified length, opcode,
// and operands. These values do not have to be valid.
void addExtendedOpcode(uint64_t Length, uint8_t Opcode,
ArrayRef<ValueAndLength> Operands);
// Write the contents of the LineUnit to the current section in the generator.
void generate(MCContext &MC, AsmPrinter &Asm) const;
private:
void writeData(ArrayRef<ValueAndLength> Data, AsmPrinter &Asm) const;
MCSymbol *writeDefaultPrologue(AsmPrinter &Asm) const;
void writePrologue(AsmPrinter &Asm) const;
void writeProloguePayload(const DWARFDebugLine::Prologue &Prologue,
AsmPrinter &Asm) const;
Generator &DG;
llvm::Optional<DWARFDebugLine::Prologue> Prologue;
std::vector<ValueAndLength> CustomPrologue;
std::vector<ValueAndLength> Contents;
// The Version field is used for determining how to write the Prologue, if a
// non-custom prologue is used. The version value actually written, will be
// that specified in the Prologue, if a custom prologue has been passed in.
// Otherwise, it will be this value.
uint16_t Version;
dwarf::DwarfFormat Format;
uint8_t AddrSize;
uint8_t SegSize;
};
/// A DWARF generator.
///
/// Generate DWARF for unit tests by creating any instance of this class and
@ -173,6 +241,7 @@ class Generator {
BumpPtrAllocator Allocator;
std::unique_ptr<DwarfStringPool> StringPool; // Entries owned by Allocator.
std::vector<std::unique_ptr<CompileUnit>> CompileUnits;
std::vector<std::unique_ptr<LineTable>> LineTables;
DIEAbbrevSet Abbreviations;
SmallString<4096> FileBytes;
@ -210,9 +279,17 @@ public:
///
/// \returns a dwarfgen::CompileUnit that can be used to retrieve the compile
/// unit dwarfgen::DIE that can be used to add attributes and add child DIE
/// objedts to.
/// objects to.
dwarfgen::CompileUnit &addCompileUnit();
/// Add a line table unit to be generated.
/// \param Format the DWARF format to use (DWARF32 or DWARF64).
///
/// \returns a dwarfgen::LineTable that can be used to customise the contents
/// of the line table.
LineTable &
addLineTable(dwarf::DwarfFormat DwarfFormat = dwarf::DwarfFormat::DWARF32);
BumpPtrAllocator &getAllocator() { return Allocator; }
AsmPrinter *getAsmPrinter() const { return Asm.get(); }
MCContext *getMCContext() const { return MC.get(); }