Index: include/llvm/CodeGen/AsmPrinter.h =================================================================== --- include/llvm/CodeGen/AsmPrinter.h +++ include/llvm/CodeGen/AsmPrinter.h @@ -463,6 +463,9 @@ /// 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. Index: include/llvm/DebugInfo/DWARF/DWARFContext.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFContext.h +++ include/llvm/DebugInfo/DWARF/DWARFContext.h @@ -259,7 +259,14 @@ 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 + getLineTableForUnit(DWARFUnit *U, + std::function WarnCallback); DataExtractor getStringExtractor() const { return DataExtractor(DObj->getStringSection(), false, 0); Index: include/llvm/DebugInfo/DWARF/DWARFDebugLine.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ include/llvm/DebugInfo/DWARF/DWARFDebugLine.h @@ -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 #include @@ -103,6 +105,8 @@ 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 @@ 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 @@ 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 WarnCallback = warn, + raw_ostream *OS = nullptr); using RowVector = std::vector; using RowIter = RowVector::const_iterator; @@ -259,14 +264,81 @@ private: uint32_t findRowInSeq(const DWARFDebugLine::Sequence &Seq, uint64_t Address) const; - Optional getSourceByIndex(uint64_t FileIndex, - DILineInfoSpecifier::FileLineInfoKind Kind) const; + Optional + 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 + getOrParseLineTable(DWARFDataExtractor &DebugLineData, uint32_t Offset, + const DWARFContext &Ctx, const DWARFUnit *U, + std::function WarnCallback = warn); + + /// Helper to allow for parsing of an entire .debug_line section in sequence. + class SectionParser { + public: + using cu_range = DWARFUnitSection::iterator_range; + using tu_range = + iterator_range>::iterator>; + using LineToUnitMap = std::map; + + 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 StringCallback = warn, + function_ref 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 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 { Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -1967,6 +1967,11 @@ 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. Index: lib/DebugInfo/DWARF/DWARFContext.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFContext.cpp +++ lib/DebugInfo/DWARF/DWARFContext.cpp @@ -244,28 +244,6 @@ } } -// 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 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, DIDT_ID_Count> DumpOffsets) { @@ -372,63 +350,40 @@ 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 @@ DObj->getStringSection(), isLittleEndian()); } -const DWARFLineTable * +const DWARFDebugLine::LineTable * DWARFContext::getLineTableForUnit(DWARFUnit *U) { + Expected ExpectedLineTable = + getLineTableForUnit(U, DWARFDebugLine::warn); + if (!ExpectedLineTable) { + DWARFDebugLine::warnForError(ExpectedLineTable.takeError()); + return nullptr; + } + return *ExpectedLineTable; +} + +Expected +DWARFContext::getLineTableForUnit(DWARFUnit *U, + std::function WarnCallback) { if (!Line) Line.reset(new DWARFDebugLine); @@ -811,7 +778,8 @@ // 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() { Index: lib/DebugInfo/DWARF/DWARFDebugLine.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -273,10 +273,24 @@ return true; } -bool DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, - uint32_t *OffsetPtr, - const DWARFContext &Ctx, - const DWARFUnit *U) { +template +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 +static Error createError(char const *Fmt, const Ts &... Vals) { + return make_error(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 @@ 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 @@ 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 @@ return nullptr; } -const DWARFDebugLine::LineTable * -DWARFDebugLine::getOrParseLineTable(DWARFDataExtractor &DebugLineData, - uint32_t Offset, const DWARFContext &Ctx, - const DWARFUnit *U) { +Expected DWARFDebugLine::getOrParseLineTable( + DWARFDataExtractor &DebugLineData, uint32_t Offset, const DWARFContext &Ctx, + const DWARFUnit *U, std::function 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 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 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 @@ Prologue.dump(*OS, DumpOptions); } + if (PrologueErr) + return PrologueErr; + const uint32_t EndOffset = DebugLineOffset + Prologue.TotalLength + Prologue.sizeofTotalLength(); @@ -554,13 +570,10 @@ 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 @@ } // 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 @@ } 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 @@ // rudimentary sequences for address ranges [0x0, 0xsomething). } - return EndOffset; + return Error::success(); } uint32_t @@ -1027,3 +1034,96 @@ 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 StringCallback, + function_ref 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 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()); }); +} Index: test/DebugInfo/X86/dwarfdump-bogus-LNE.s =================================================================== --- test/DebugInfo/X86/dwarfdump-bogus-LNE.s +++ test/DebugInfo/X86/dwarfdump-bogus-LNE.s @@ -150,100 +150,3 @@ # 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 Index: test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s =================================================================== --- test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s +++ test/tools/llvm-dwarfdump/X86/Inputs/debug_line_malformed.s @@ -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: Index: test/tools/llvm-dwarfdump/X86/Inputs/debug_line_reserved_length.s =================================================================== --- test/tools/llvm-dwarfdump/X86/Inputs/debug_line_reserved_length.s +++ test/tools/llvm-dwarfdump/X86/Inputs/debug_line_reserved_length.s @@ -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: Index: test/tools/llvm-dwarfdump/X86/debug_line_invalid.test =================================================================== --- test/tools/llvm-dwarfdump/X86/debug_line_invalid.test +++ test/tools/llvm-dwarfdump/X86/debug_line_invalid.test @@ -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: Index: tools/dsymutil/DwarfLinker.cpp =================================================================== --- tools/dsymutil/DwarfLinker.cpp +++ tools/dsymutil/DwarfLinker.cpp @@ -3603,7 +3603,10 @@ 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 NewRows; Index: unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp =================================================================== --- unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp +++ unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp @@ -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 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 @@ return nullptr; } - std::unique_ptr 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 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{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 Gen; + std::unique_ptr Context; + DWARFDataExtractor LineData; + DWARFDebugLine Line; + std::string IssueMessage; + std::function RecordIssue; + Error FoundError; + std::function RecordError; + + SmallVector, 2> CUs; + std::deque> 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>, + 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 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 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 Index: unittests/DebugInfo/DWARF/DwarfGenerator.h =================================================================== --- unittests/DebugInfo/DWARF/DwarfGenerator.h +++ unittests/DebugInfo/DWARF/DwarfGenerator.h @@ -153,6 +153,74 @@ 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 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 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 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 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 Prologue; + std::vector CustomPrologue; + std::vector 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 @@ BumpPtrAllocator Allocator; std::unique_ptr StringPool; // Entries owned by Allocator. std::vector> CompileUnits; + std::vector> LineTables; DIEAbbrevSet Abbreviations; SmallString<4096> FileBytes; @@ -210,9 +279,17 @@ /// /// \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(); } Index: unittests/DebugInfo/DWARF/DwarfGenerator.cpp =================================================================== --- unittests/DebugInfo/DWARF/DwarfGenerator.cpp +++ unittests/DebugInfo/DWARF/DwarfGenerator.cpp @@ -106,6 +106,230 @@ } //===----------------------------------------------------------------------===// +/// 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 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 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 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 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 @@ Asm->emitDwarfDIE(*CU->getUnitDIE().Die); } + MS->SwitchSection(MOFI->getDwarfLineSection()); + for (auto < : LineTables) + LT->generate(*MC, *Asm); + MS->Finish(); if (FileBytes.empty()) return StringRef(); @@ -263,7 +491,13 @@ } dwarfgen::CompileUnit &dwarfgen::Generator::addCompileUnit() { - CompileUnits.push_back(std::unique_ptr( - new CompileUnit(*this, Version, Asm->getPointerSize()))); + CompileUnits.push_back( + make_unique(*this, Version, Asm->getPointerSize())); return *CompileUnits.back(); } + +dwarfgen::LineTable &dwarfgen::Generator::addLineTable(DwarfFormat Format) { + LineTables.push_back( + make_unique(*this, Version, Format, Asm->getPointerSize())); + return *LineTables.back(); +}