/// 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.
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);
#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>
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()) +
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.
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;
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 {
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.
}
}
-// 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) {
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,
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);
// 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() {
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();
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);
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); }
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.
Prologue.dump(*OS, DumpOptions);
}
+ if (PrologueErr)
+ return PrologueErr;
+
const uint32_t EndOffset =
DebugLineOffset + Prologue.TotalLength + Prologue.sizeofTotalLength();
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)
}
// 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);
}
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()) {
// rudimentary sequences for address ranges [0x0, 0xsomething).
}
- return EndOffset;
+ return Error::success();
}
uint32_t
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()); });
+}
# 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
--- /dev/null
+.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:
--- /dev/null
+.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:
--- /dev/null
+# 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:
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;
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)
return nullptr;
}
- std::unique_ptr<dwarfgen::Generator> Generator;
+ DWARFDebugLine::SectionParser setupParser() {
+ LineTable < = 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 <2 = 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;
+};
+
+// 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;
};
-TEST(DWARFDebugLine, GetLineTableAtInvalidOffset) {
- DebugLineGenerator LineGen;
- if (!LineGen.init())
+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();
- 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);
+ 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;
+
+ LineTable < = Gen->addLineTable();
+ LT.setCustomPrologue({{0, LineTable::Byte}});
+
+ 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 < = 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 <2 = 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 < = 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 < = 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 < = 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 < = 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 < = 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 < = 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 < = 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 < = 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 < = 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 < = 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 < = Gen->addLineTable(DWARF32);
+ LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
+ LineTable <2 = 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 < = Gen->addLineTable(DWARF32);
+ LT.addExtendedOpcode(0x42, DW_LNE_end_sequence, {});
+ LineTable <2 = 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 < = Gen->addLineTable(DWARF32);
+ LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
+ LineTable <2 = 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 < = Gen->addLineTable(DWARF32);
+ LT.addExtendedOpcode(42, DW_LNE_end_sequence, {});
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.skip(RecordError);
- EXPECT_EQ(Line.getOrParseLineTable(LineData, 0, *Context, nullptr), nullptr);
+ EXPECT_TRUE(Parser.done());
+ EXPECT_TRUE(!FoundError);
}
} // end anonymous namespace
}
//===----------------------------------------------------------------------===//
+/// 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.
//===----------------------------------------------------------------------===//
Asm->emitDwarfDIE(*CU->getUnitDIE().Die);
}
+ MS->SwitchSection(MOFI->getDwarfLineSection());
+ for (auto < : LineTables)
+ LT->generate(*MC, *Asm);
+
MS->Finish();
if (FileBytes.empty())
return StringRef();
}
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();
+}
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
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;
///
/// \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(); }