summaryrefslogtreecommitdiffstats
path: root/llvm/unittests
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/unittests')
-rw-r--r--llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp628
-rw-r--r--llvm/unittests/DebugInfo/DWARF/DwarfGenerator.cpp238
-rw-r--r--llvm/unittests/DebugInfo/DWARF/DwarfGenerator.h79
3 files changed, 926 insertions, 19 deletions
diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp
index 8073b705dc2..8ace067172e 100644
--- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp
+++ b/llvm/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<DWARFContext> createContext() {
- if (!Generator)
+ if (!Gen)
return nullptr;
- StringRef FileBytes = Generator->generate();
+ StringRef FileBytes = Gen->generate();
MemoryBufferRef FileBuffer(FileBytes, "dwarf");
auto Obj = object::ObjectFile::createObjectFile(FileBuffer);
if (Obj)
@@ -44,21 +62,599 @@ struct DebugLineGenerator {
return nullptr;
}
- std::unique_ptr<dwarfgen::Generator> Generator;
+ DWARFDebugLine::SectionParser setupParser() {
+ LineTable &LT = Gen->addLineTable(DWARF32);
+ LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});
+ LT.addStandardOpcode(DW_LNS_copy, {});
+ LT.addByte(0xaa);
+ LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+ LineTable &LT2 = Gen->addLineTable(DWARF64);
+ LT2.addExtendedOpcode(9, DW_LNE_set_address,
+ {{0x11223344, LineTable::Quad}});
+ LT2.addStandardOpcode(DW_LNS_copy, {});
+ LT2.addByte(0xbb);
+ LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+ generate();
+
+ return DWARFDebugLine::SectionParser(LineData, *Context, CUs, TUs);
+ }
+
+ void recordIssue(StringRef Message) { IssueMessage = Message; }
+ void recordError(Error Err) {
+ FoundError = joinErrors(std::move(FoundError), std::move(Err));
+ }
+
+ void checkError(ArrayRef<StringRef> ExpectedMsgs, Error Err) {
+ ASSERT_TRUE(Err.operator bool());
+ size_t WhichMsg = 0;
+ Error Remaining =
+ handleErrors(std::move(Err), [&](const ErrorInfoBase &Actual) {
+ ASSERT_LT(WhichMsg, ExpectedMsgs.size());
+ // Use .str(), because googletest doesn't visualise a StringRef
+ // properly.
+ EXPECT_EQ(Actual.message(), ExpectedMsgs[WhichMsg++].str());
+ });
+ EXPECT_EQ(WhichMsg, ExpectedMsgs.size());
+ EXPECT_FALSE(Remaining);
+ }
+
+ void checkError(StringRef ExpectedMsg, Error Err) {
+ checkError(ArrayRef<StringRef>{ExpectedMsg}, std::move(Err));
+ }
+
+ void checkGetOrParseLineTableEmitsError(StringRef ExpectedMsg,
+ uint64_t Offset = 0) {
+ auto ExpectedLineTable = Line.getOrParseLineTable(
+ LineData, Offset, *Context, nullptr, RecordIssue);
+ EXPECT_FALSE(ExpectedLineTable);
+ EXPECT_TRUE(IssueMessage.empty());
+
+ checkError(ExpectedMsg, ExpectedLineTable.takeError());
+ }
+
+ std::unique_ptr<Generator> Gen;
+ std::unique_ptr<DWARFContext> Context;
+ DWARFDataExtractor LineData;
+ DWARFDebugLine Line;
+ std::string IssueMessage;
+ std::function<void(StringRef)> RecordIssue;
+ Error FoundError;
+ std::function<void(Error)> RecordError;
+
+ SmallVector<std::unique_ptr<DWARFCompileUnit>, 2> CUs;
+ std::deque<DWARFUnitSection<DWARFTypeUnit>> TUs;
+};
+
+// 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 &LT = 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 &LT = Gen->addLineTable(Format);
+ LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});
+ LT.addStandardOpcode(DW_LNS_copy, {});
+ LT.addByte(0xaa);
+ LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+ LineTable &LT2 = Gen->addLineTable(Format);
+ LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x11223344, LineTable::Quad}});
+ LT2.addStandardOpcode(DW_LNS_copy, {});
+ LT2.addByte(0xbb);
+ LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+ LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x55667788, LineTable::Quad}});
+ LT2.addStandardOpcode(DW_LNS_copy, {});
+ LT2.addByte(0xcc);
+ LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+ generate();
+
+ auto ExpectedLineTable =
+ Line.getOrParseLineTable(LineData, 0, *Context, nullptr, RecordIssue);
+ ASSERT_TRUE(ExpectedLineTable.operator bool());
+ EXPECT_TRUE(IssueMessage.empty());
+ const DWARFDebugLine::LineTable *Expected = *ExpectedLineTable;
+ checkDefaultPrologue(Version, Format, Expected->Prologue, 16);
+ EXPECT_EQ(Expected->Sequences.size(), 1);
+
+ uint64_t SecondOffset =
+ Expected->Prologue.sizeofTotalLength() + Expected->Prologue.TotalLength;
+ IssueMessage.clear();
+ auto ExpectedLineTable2 = Line.getOrParseLineTable(
+ LineData, SecondOffset, *Context, nullptr, RecordIssue);
+ ASSERT_TRUE(ExpectedLineTable2.operator bool());
+ EXPECT_TRUE(IssueMessage.empty());
+ const DWARFDebugLine::LineTable *Expected2 = *ExpectedLineTable2;
+ checkDefaultPrologue(Version, Format, Expected2->Prologue, 32);
+ EXPECT_EQ(Expected2->Sequences.size(), 2);
+
+ EXPECT_NE(Expected, Expected2);
+
+ // Check that if the same offset is requested, the exact same pointer is
+ // returned.
+ IssueMessage.clear();
+ auto ExpectedLineTable3 =
+ Line.getOrParseLineTable(LineData, 0, *Context, nullptr, RecordIssue);
+ ASSERT_TRUE(ExpectedLineTable3.operator bool());
+ EXPECT_TRUE(IssueMessage.empty());
+ EXPECT_EQ(Expected, *ExpectedLineTable3);
+
+ IssueMessage.clear();
+ auto ExpectedLineTable4 = Line.getOrParseLineTable(
+ LineData, SecondOffset, *Context, nullptr, RecordIssue);
+ ASSERT_TRUE(ExpectedLineTable4.operator bool());
+ EXPECT_TRUE(IssueMessage.empty());
+ EXPECT_EQ(Expected2, *ExpectedLineTable4);
+
+ // TODO: Add tests that show that the body of the programs have been read
+ // correctly.
+}
+
+TEST_F(DebugLineBasicFixture, ErrorForReservedLength) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.setCustomPrologue({{0xffffff00, LineTable::Long}});
+
+ generate();
+
+ checkGetOrParseLineTableEmitsError(
+ "parsing line table prologue at offset 0x00000000 unsupported reserved "
+ "unit length found of value 0xffffff00");
+}
+
+TEST_F(DebugLineBasicFixture, ErrorForLowVersion) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.setCustomPrologue(
+ {{LineTable::Half, LineTable::Long}, {1, LineTable::Half}});
+
+ generate();
+
+ checkGetOrParseLineTableEmitsError("parsing line table prologue at offset "
+ "0x00000000 found unsupported version "
+ "0x01");
+}
+
+TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) {
+ if (!setupGenerator(5))
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.setCustomPrologue({
+ {19, LineTable::Long}, // unit length
+ {5, LineTable::Half}, // version
+ {8, LineTable::Byte}, // addr size
+ {0, LineTable::Byte}, // segment selector size
+ {11, LineTable::Long}, // prologue length
+ {1, LineTable::Byte}, // min instruction length
+ {1, LineTable::Byte}, // max ops per instruction
+ {1, LineTable::Byte}, // default is_stmt
+ {0, LineTable::Byte}, // line base
+ {14, LineTable::Byte}, // line range
+ {2, LineTable::Byte}, // opcode base (small to reduce the amount of
+ // setup required).
+ {0, LineTable::Byte}, // standard opcode lengths
+ {0, LineTable::Byte}, // directory entry format count (should not be
+ // zero).
+ {0, LineTable::ULEB}, // directories count
+ {0, LineTable::Byte}, // file name entry format count
+ {0, LineTable::ULEB} // file name entry count
+ });
+
+ generate();
+
+ checkGetOrParseLineTableEmitsError(
+ "parsing line table prologue at 0x00000000 found an invalid directory or "
+ "file table description at 0x00000014");
+}
+
+TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) {
+ if (!setupGenerator(Version))
+ return;
+
+ SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
+ (Format == DWARF64 ? "DWARF64" : "DWARF32"));
+
+ LineTable &LT = Gen->addLineTable(Format);
+ DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
+ ++Prologue.PrologueLength;
+ LT.setPrologue(Prologue);
+
+ generate();
+
+ uint64_t ExpectedEnd =
+ Prologue.TotalLength + 1 + Prologue.sizeofTotalLength();
+ checkGetOrParseLineTableEmitsError(
+ (Twine("parsing line table prologue at 0x00000000 should have ended at "
+ "0x000000") +
+ Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" +
+ Twine::utohexstr(ExpectedEnd - 1))
+ .str());
+}
+
+TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) {
+ if (!setupGenerator(Version))
+ return;
+
+ SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
+ (Format == DWARF64 ? "DWARF64" : "DWARF32"));
+
+ LineTable &LT = Gen->addLineTable(Format);
+ DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
+ // FIXME: Ideally, we'd test for 1 less than expected, but the code does not
+ // currently fail if missing only the terminator of a v2-4 file table.
+ if (Version < 5)
+ Prologue.PrologueLength -= 2;
+ else
+ Prologue.PrologueLength -= 1;
+ LT.setPrologue(Prologue);
+
+ generate();
+
+ uint64_t ExpectedEnd =
+ Prologue.TotalLength - 1 + Prologue.sizeofTotalLength();
+ if (Version < 5)
+ --ExpectedEnd;
+ checkGetOrParseLineTableEmitsError(
+ (Twine("parsing line table prologue at 0x00000000 should have ended at "
+ "0x000000") +
+ Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" +
+ Twine::utohexstr(ExpectedEnd + 1))
+ .str());
+}
+
+INSTANTIATE_TEST_CASE_P(
+ LineTableTestParams, DebugLineParameterisedFixture,
+ Values(std::make_pair(
+ 2, DWARF32), // Test lower-bound of v2-3 fields and DWARF32.
+ std::make_pair(3, DWARF32), // Test upper-bound of v2-3 fields.
+ std::make_pair(4, DWARF64), // Test v4 fields and DWARF64.
+ std::make_pair(5, DWARF32), std::make_pair(5, DWARF64)),);
+
+TEST_F(DebugLineBasicFixture, ErrorForInvalidExtendedOpcodeLength) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ // The Length should be 1 for an end sequence opcode.
+ LT.addExtendedOpcode(2, DW_LNE_end_sequence, {});
+
+ generate();
+
+ checkGetOrParseLineTableEmitsError("unexpected line op length at offset "
+ "0x00000030 expected 0x02 found 0x01");
+}
+
+TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ // The line data extractor expects size 8 (Quad) addresses.
+ LT.addExtendedOpcode(5, DW_LNE_set_address, {{0x11223344, LineTable::Long}});
+ LT.addStandardOpcode(DW_LNS_copy, {});
+ LT.addByte(0xaa);
+ LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+
+ generate();
+
+ checkGetOrParseLineTableEmitsError(
+ "mismatching address size at offset 0x00000030 expected 0x08 found 0x04");
+}
+
+TEST_F(DebugLineBasicFixture, CallbackUsedForUnterminatedSequence) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.addExtendedOpcode(9, DW_LNE_set_address,
+ {{0x1122334455667788, LineTable::Quad}});
+ LT.addStandardOpcode(DW_LNS_copy, {});
+ LT.addByte(0xaa);
+ LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
+ LT.addExtendedOpcode(9, DW_LNE_set_address,
+ {{0x99aabbccddeeff00, LineTable::Quad}});
+ LT.addStandardOpcode(DW_LNS_copy, {});
+ LT.addByte(0xbb);
+ LT.addByte(0xcc);
+
+ generate();
+
+ auto ExpectedLineTable =
+ Line.getOrParseLineTable(LineData, 0, *Context, nullptr, RecordIssue);
+ EXPECT_EQ(IssueMessage,
+ "last sequence in debug line table is not terminated!");
+ ASSERT_TRUE(ExpectedLineTable.operator bool());
+ EXPECT_EQ((*ExpectedLineTable)->Rows.size(), 6);
+ // The unterminated sequence is not added to the sequence list.
+ EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1);
+}
+
+TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) {
+ if (!setupGenerator())
+ return;
+
+ DWARFDebugLine::SectionParser Parser = setupParser();
+
+ EXPECT_EQ(Parser.getOffset(), 0);
+ ASSERT_FALSE(Parser.done());
+
+ DWARFDebugLine::LineTable Parsed = Parser.parseNext(RecordIssue, RecordError);
+ checkDefaultPrologue(4, DWARF32, Parsed.Prologue, 16);
+ EXPECT_EQ(Parsed.Sequences.size(), 1);
+ EXPECT_EQ(Parser.getOffset(), 62);
+ ASSERT_FALSE(Parser.done());
+
+ DWARFDebugLine::LineTable Parsed2 =
+ Parser.parseNext(RecordIssue, RecordError);
+ checkDefaultPrologue(4, DWARF64, Parsed2.Prologue, 16);
+ EXPECT_EQ(Parsed2.Sequences.size(), 1);
+ EXPECT_EQ(Parser.getOffset(), 136);
+ EXPECT_TRUE(Parser.done());
+
+ EXPECT_TRUE(IssueMessage.empty());
+ EXPECT_FALSE(FoundError);
+}
+
+TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) {
+ if (!setupGenerator())
+ return;
+
+ DWARFDebugLine::SectionParser Parser = setupParser();
+
+ EXPECT_EQ(Parser.getOffset(), 0);
+ ASSERT_FALSE(Parser.done());
+
+ Parser.skip(RecordError);
+ EXPECT_EQ(Parser.getOffset(), 62);
+ ASSERT_FALSE(Parser.done());
+
+ Parser.skip(RecordError);
+ EXPECT_EQ(Parser.getOffset(), 136);
+ EXPECT_TRUE(Parser.done());
+
+ EXPECT_FALSE(FoundError);
+}
+
+TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) {
+ if (!setupGenerator())
+ return;
+
+ generate();
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+
+ EXPECT_TRUE(Parser.done());
+}
+
+TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenParsing) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.setCustomPrologue({{0xffffff00, LineTable::Long}});
+ Gen->addLineTable();
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.parseNext(RecordIssue, RecordError);
+
+ EXPECT_EQ(Parser.getOffset(), 4);
+ EXPECT_TRUE(Parser.done());
+ EXPECT_TRUE(IssueMessage.empty());
+
+ checkError("parsing line table prologue at offset 0x00000000 unsupported "
+ "reserved unit length found of value 0xffffff00",
+ std::move(FoundError));
+}
+
+TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenSkipping) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable();
+ LT.setCustomPrologue({{0xffffff00, LineTable::Long}});
+ Gen->addLineTable();
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.skip(RecordError);
+
+ EXPECT_EQ(Parser.getOffset(), 4);
+ EXPECT_TRUE(Parser.done());
+
+ checkError("parsing line table prologue at offset 0x00000000 unsupported "
+ "reserved unit length found of value 0xffffff00",
+ std::move(FoundError));
+}
+
+TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable(DWARF32);
+ LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
+ LineTable &LT2 = Gen->addLineTable(DWARF32);
+ LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.parseNext(RecordIssue, RecordError);
+ ASSERT_FALSE(Parser.done());
+ Parser.parseNext(RecordIssue, RecordError);
+
+ EXPECT_TRUE(Parser.done());
+ EXPECT_TRUE(IssueMessage.empty());
+
+ checkError({"parsing line table prologue at offset 0x00000000 found "
+ "unsupported version 0x00",
+ "parsing line table prologue at offset 0x00000006 found "
+ "unsupported version 0x01"},
+ std::move(FoundError));
+}
+
+TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable(DWARF32);
+ LT.addExtendedOpcode(0x42, DW_LNE_end_sequence, {});
+ LineTable &LT2 = Gen->addLineTable(DWARF32);
+ LT2.addExtendedOpcode(9, DW_LNE_set_address,
+ {{0x1234567890abcdef, LineTable::Quad}});
+ LT2.addStandardOpcode(DW_LNS_copy, {});
+ LT2.addByte(0xbb);
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.parseNext(RecordIssue, RecordError);
+ EXPECT_TRUE(IssueMessage.empty());
+ ASSERT_FALSE(Parser.done());
+ checkError(
+ "unexpected line op length at offset 0x00000030 expected 0x42 found 0x01",
+ std::move(FoundError));
+
+ // Reset the error state so that it does not confuse the next set of checks.
+ FoundError = Error::success();
+ Parser.parseNext(RecordIssue, RecordError);
+
+ EXPECT_TRUE(Parser.done());
+ EXPECT_EQ(IssueMessage,
+ "last sequence in debug line table is not terminated!");
+ EXPECT_TRUE(!FoundError);
+}
+
+TEST_F(DebugLineBasicFixture,
+ ParserReportsPrologueErrorsInEachTableWhenSkipping) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable(DWARF32);
+ LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
+ LineTable &LT2 = Gen->addLineTable(DWARF32);
+ LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.skip(RecordError);
+ ASSERT_FALSE(Parser.done());
+ Parser.skip(RecordError);
+
+ EXPECT_TRUE(Parser.done());
+
+ checkError({"parsing line table prologue at offset 0x00000000 found "
+ "unsupported version 0x00",
+ "parsing line table prologue at offset 0x00000006 found "
+ "unsupported version 0x01"},
+ std::move(FoundError));
+}
+
+TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) {
+ if (!setupGenerator())
+ return;
+
+ LineTable &LT = Gen->addLineTable(DWARF32);
+ LT.addExtendedOpcode(42, DW_LNE_end_sequence, {});
+ generate();
+
+ DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs);
+ Parser.skip(RecordError);
- EXPECT_EQ(Line.getOrParseLineTable(LineData, 0, *Context, nullptr), nullptr);
+ EXPECT_TRUE(Parser.done());
+ EXPECT_TRUE(!FoundError);
}
} // end anonymous namespace
diff --git a/llvm/unittests/DebugInfo/DWARF/DwarfGenerator.cpp b/llvm/unittests/DebugInfo/DWARF/DwarfGenerator.cpp
index dfbdf99195d..a6aa1b79070 100644
--- a/llvm/unittests/DebugInfo/DWARF/DwarfGenerator.cpp
+++ b/llvm/unittests/DebugInfo/DWARF/DwarfGenerator.cpp
@@ -106,6 +106,230 @@ dwarfgen::DIE dwarfgen::CompileUnit::getUnitDIE() {
}
//===----------------------------------------------------------------------===//
+/// dwarfgen::LineTable implementation.
+//===----------------------------------------------------------------------===//
+DWARFDebugLine::Prologue dwarfgen::LineTable::createBasicPrologue() const {
+ DWARFDebugLine::Prologue P;
+ switch (Version) {
+ case 2:
+ case 3:
+ P.TotalLength = 41;
+ P.PrologueLength = 35;
+ break;
+ case 4:
+ P.TotalLength = 42;
+ P.PrologueLength = 36;
+ break;
+ case 5:
+ P.TotalLength = 47;
+ P.PrologueLength = 39;
+ P.FormParams.AddrSize = AddrSize;
+ break;
+ default:
+ llvm_unreachable("unsupported version");
+ }
+ if (Format == DWARF64) {
+ P.TotalLength += 4;
+ P.FormParams.Format = DWARF64;
+ }
+ P.FormParams.Version = Version;
+ P.MinInstLength = 1;
+ P.MaxOpsPerInst = 1;
+ P.DefaultIsStmt = 1;
+ P.LineBase = -5;
+ P.LineRange = 14;
+ P.OpcodeBase = 13;
+ P.StandardOpcodeLengths = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1};
+ P.IncludeDirectories.push_back(DWARFFormValue(DW_FORM_string));
+ P.IncludeDirectories.back().setPValue("a dir");
+ P.FileNames.push_back(DWARFDebugLine::FileNameEntry());
+ P.FileNames.back().Name.setPValue("a file");
+ P.FileNames.back().Name.setForm(DW_FORM_string);
+ return P;
+}
+
+void dwarfgen::LineTable::setPrologue(DWARFDebugLine::Prologue NewPrologue) {
+ Prologue = NewPrologue;
+ CustomPrologue.clear();
+}
+
+void dwarfgen::LineTable::setCustomPrologue(
+ ArrayRef<ValueAndLength> NewPrologue) {
+ Prologue.reset();
+ CustomPrologue = NewPrologue;
+}
+
+void dwarfgen::LineTable::addByte(uint8_t Value) {
+ Contents.push_back({Value, Byte});
+}
+
+void dwarfgen::LineTable::addStandardOpcode(uint8_t Opcode,
+ ArrayRef<ValueAndLength> Operands) {
+ Contents.push_back({Opcode, Byte});
+ Contents.insert(Contents.end(), Operands.begin(), Operands.end());
+}
+
+void dwarfgen::LineTable::addExtendedOpcode(uint64_t Length, uint8_t Opcode,
+ ArrayRef<ValueAndLength> Operands) {
+ Contents.push_back({0, Byte});
+ Contents.push_back({Length, ULEB});
+ Contents.push_back({Opcode, Byte});
+ Contents.insert(Contents.end(), Operands.begin(), Operands.end());
+}
+
+void dwarfgen::LineTable::generate(MCContext &MC, AsmPrinter &Asm) const {
+ MC.setDwarfVersion(Version);
+
+ MCSymbol *EndSymbol = nullptr;
+ if (!CustomPrologue.empty()) {
+ writeData(CustomPrologue, Asm);
+ } else if (!Prologue) {
+ EndSymbol = writeDefaultPrologue(Asm);
+ } else {
+ writePrologue(Asm);
+ }
+
+ writeData(Contents, Asm);
+ if (EndSymbol != nullptr)
+ Asm.OutStreamer->EmitLabel(EndSymbol);
+}
+
+void dwarfgen::LineTable::writeData(ArrayRef<ValueAndLength> Data,
+ AsmPrinter &Asm) const {
+ for (auto Entry : Data) {
+ switch (Entry.Length) {
+ case Byte:
+ case Half:
+ case Long:
+ case Quad:
+ Asm.OutStreamer->EmitIntValue(Entry.Value, Entry.Length);
+ break;
+ case ULEB:
+ Asm.EmitULEB128(Entry.Value);
+ break;
+ case SLEB:
+ Asm.EmitSLEB128(Entry.Value);
+ break;
+ default:
+ llvm_unreachable("unsupported ValueAndLength Length value");
+ }
+ }
+}
+
+MCSymbol *dwarfgen::LineTable::writeDefaultPrologue(AsmPrinter &Asm) const {
+ MCSymbol *UnitStart = Asm.createTempSymbol("line_unit_start");
+ MCSymbol *UnitEnd = Asm.createTempSymbol("line_unit_end");
+ if (Format == DwarfFormat::DWARF64) {
+ Asm.emitInt32(0xffffffff);
+ Asm.EmitLabelDifference(UnitEnd, UnitStart, 8);
+ } else {
+ Asm.EmitLabelDifference(UnitEnd, UnitStart, 4);
+ }
+ Asm.OutStreamer->EmitLabel(UnitStart);
+ Asm.emitInt16(Version);
+ if (Version == 5) {
+ Asm.emitInt8(AddrSize);
+ Asm.emitInt8(SegSize);
+ }
+
+ MCSymbol *PrologueStart = Asm.createTempSymbol("line_prologue_start");
+ MCSymbol *PrologueEnd = Asm.createTempSymbol("line_prologue_end");
+ Asm.EmitLabelDifference(PrologueEnd, PrologueStart,
+ Format == DwarfFormat::DWARF64 ? 8 : 4);
+ Asm.OutStreamer->EmitLabel(PrologueStart);
+
+ DWARFDebugLine::Prologue DefaultPrologue = createBasicPrologue();
+ writeProloguePayload(DefaultPrologue, Asm);
+ Asm.OutStreamer->EmitLabel(PrologueEnd);
+ return UnitEnd;
+}
+
+void dwarfgen::LineTable::writePrologue(AsmPrinter &Asm) const {
+ if (Format == DwarfFormat::DWARF64) {
+ Asm.emitInt32(0xffffffff);
+ Asm.emitInt64(Prologue->TotalLength);
+ } else {
+ Asm.emitInt32(Prologue->TotalLength);
+ }
+ Asm.emitInt16(Prologue->getVersion());
+ if (Version == 5) {
+ Asm.emitInt8(Prologue->getAddressSize());
+ Asm.emitInt8(Prologue->SegSelectorSize);
+ }
+ if (Format == DwarfFormat::DWARF64)
+ Asm.emitInt64(Prologue->PrologueLength);
+ else
+ Asm.emitInt32(Prologue->PrologueLength);
+
+ writeProloguePayload(*Prologue, Asm);
+}
+
+static void writeCString(StringRef Str, AsmPrinter &Asm) {
+ Asm.OutStreamer->EmitBytes(Str);
+ Asm.emitInt8(0);
+}
+
+static void writeV2IncludeAndFileTable(const DWARFDebugLine::Prologue &Prologue,
+ AsmPrinter &Asm) {
+ for (auto Include : Prologue.IncludeDirectories) {
+ assert(Include.getAsCString() && "expected a string form for include dir");
+ writeCString(*Include.getAsCString(), Asm);
+ }
+ Asm.emitInt8(0);
+
+ for (auto File : Prologue.FileNames) {
+ assert(File.Name.getAsCString() && "expected a string form for file name");
+ writeCString(*File.Name.getAsCString(), Asm);
+ Asm.EmitULEB128(File.DirIdx);
+ Asm.EmitULEB128(File.ModTime);
+ Asm.EmitULEB128(File.Length);
+ }
+ Asm.emitInt8(0);
+}
+
+static void writeV5IncludeAndFileTable(const DWARFDebugLine::Prologue &Prologue,
+ AsmPrinter &Asm) {
+ Asm.emitInt8(1); // directory_entry_format_count.
+ // TODO: Add support for other content descriptions - we currently only
+ // support a single DW_LNCT_path/DW_FORM_string.
+ Asm.EmitULEB128(DW_LNCT_path);
+ Asm.EmitULEB128(DW_FORM_string);
+ Asm.EmitULEB128(Prologue.IncludeDirectories.size());
+ for (auto Include : Prologue.IncludeDirectories) {
+ assert(Include.getAsCString() && "expected a string form for include dir");
+ writeCString(*Include.getAsCString(), Asm);
+ }
+
+ Asm.emitInt8(1); // file_name_entry_format_count.
+ Asm.EmitULEB128(DW_LNCT_path);
+ Asm.EmitULEB128(DW_FORM_string);
+ Asm.EmitULEB128(Prologue.FileNames.size());
+ for (auto File : Prologue.FileNames) {
+ assert(File.Name.getAsCString() && "expected a string form for file name");
+ writeCString(*File.Name.getAsCString(), Asm);
+ }
+}
+
+void dwarfgen::LineTable::writeProloguePayload(
+ const DWARFDebugLine::Prologue &Prologue, AsmPrinter &Asm) const {
+ Asm.emitInt8(Prologue.MinInstLength);
+ if (Version >= 4)
+ Asm.emitInt8(Prologue.MaxOpsPerInst);
+ Asm.emitInt8(Prologue.DefaultIsStmt);
+ Asm.emitInt8(Prologue.LineBase);
+ Asm.emitInt8(Prologue.LineRange);
+ Asm.emitInt8(Prologue.OpcodeBase);
+ for (auto Length : Prologue.StandardOpcodeLengths) {
+ Asm.emitInt8(Length);
+ }
+
+ if (Version < 5)
+ writeV2IncludeAndFileTable(Prologue, Asm);
+ else
+ writeV5IncludeAndFileTable(Prologue, Asm);
+}
+
+//===----------------------------------------------------------------------===//
/// dwarfgen::Generator implementation.
//===----------------------------------------------------------------------===//
@@ -244,6 +468,10 @@ StringRef dwarfgen::Generator::generate() {
Asm->emitDwarfDIE(*CU->getUnitDIE().Die);
}
+ MS->SwitchSection(MOFI->getDwarfLineSection());
+ for (auto &LT : LineTables)
+ LT->generate(*MC, *Asm);
+
MS->Finish();
if (FileBytes.empty())
return StringRef();
@@ -263,7 +491,13 @@ bool dwarfgen::Generator::saveFile(StringRef Path) {
}
dwarfgen::CompileUnit &dwarfgen::Generator::addCompileUnit() {
- CompileUnits.push_back(std::unique_ptr<CompileUnit>(
- new CompileUnit(*this, Version, Asm->getPointerSize())));
+ CompileUnits.push_back(
+ make_unique<CompileUnit>(*this, Version, Asm->getPointerSize()));
return *CompileUnits.back();
}
+
+dwarfgen::LineTable &dwarfgen::Generator::addLineTable(DwarfFormat Format) {
+ LineTables.push_back(
+ make_unique<LineTable>(*this, Version, Format, Asm->getPointerSize()));
+ return *LineTables.back();
+}
diff --git a/llvm/unittests/DebugInfo/DWARF/DwarfGenerator.h b/llvm/unittests/DebugInfo/DWARF/DwarfGenerator.h
index dd7e8709638..e575af2c29f 100644
--- a/llvm/unittests/DebugInfo/DWARF/DwarfGenerator.h
+++ b/llvm/unittests/DebugInfo/DWARF/DwarfGenerator.h
@@ -153,6 +153,74 @@ public:
void setLength(uint64_t Length) { DU.setLength(Length); }
};
+/// A DWARF line unit-like class used to generate DWARF line units.
+///
+/// Instances of this class are created by instances of the Generator class.
+class LineTable {
+public:
+ enum ValueLength { Byte = 1, Half = 2, Long = 4, Quad = 8, ULEB, SLEB };
+
+ struct ValueAndLength {
+ uint64_t Value;
+ ValueLength Length;
+ };
+
+ LineTable(Generator &DG, uint16_t Version, dwarf::DwarfFormat Format,
+ uint8_t AddrSize, uint8_t SegSize = 0)
+ : DG(DG), Version(Version), Format(Format), AddrSize(AddrSize),
+ SegSize(SegSize) {
+ assert(Version >= 2 && Version <= 5 && "unsupported version");
+ }
+
+ // Create a Prologue suitable to pass to setPrologue, with a single file and
+ // include directory entry.
+ DWARFDebugLine::Prologue createBasicPrologue() const;
+
+ // Set or replace the current prologue with the specified prologue. If no
+ // prologue is set, a default one will be used when generating.
+ void setPrologue(DWARFDebugLine::Prologue NewPrologue);
+ // Used to write an arbitrary payload instead of the standard prologue. This
+ // is useful if you wish to test handling of corrupt .debug_line sections.
+ void setCustomPrologue(ArrayRef<ValueAndLength> NewPrologue);
+
+ // Add a byte to the program, with the given value. This can be used to
+ // specify a special opcode, or to add arbitrary contents to the section.
+ void addByte(uint8_t Value);
+ // Add a standard opcode to the program. The opcode and operands do not have
+ // to be valid.
+ void addStandardOpcode(uint8_t Opcode, ArrayRef<ValueAndLength> Operands);
+ // Add an extended opcode to the program with the specified length, opcode,
+ // and operands. These values do not have to be valid.
+ void addExtendedOpcode(uint64_t Length, uint8_t Opcode,
+ ArrayRef<ValueAndLength> Operands);
+
+ // Write the contents of the LineUnit to the current section in the generator.
+ void generate(MCContext &MC, AsmPrinter &Asm) const;
+
+private:
+ void writeData(ArrayRef<ValueAndLength> Data, AsmPrinter &Asm) const;
+ MCSymbol *writeDefaultPrologue(AsmPrinter &Asm) const;
+ void writePrologue(AsmPrinter &Asm) const;
+
+ void writeProloguePayload(const DWARFDebugLine::Prologue &Prologue,
+ AsmPrinter &Asm) const;
+
+ Generator &DG;
+ llvm::Optional<DWARFDebugLine::Prologue> Prologue;
+ std::vector<ValueAndLength> CustomPrologue;
+ std::vector<ValueAndLength> Contents;
+
+ // The Version field is used for determining how to write the Prologue, if a
+ // non-custom prologue is used. The version value actually written, will be
+ // that specified in the Prologue, if a custom prologue has been passed in.
+ // Otherwise, it will be this value.
+ uint16_t Version;
+
+ dwarf::DwarfFormat Format;
+ uint8_t AddrSize;
+ uint8_t SegSize;
+};
+
/// A DWARF generator.
///
/// Generate DWARF for unit tests by creating any instance of this class and
@@ -173,6 +241,7 @@ class Generator {
BumpPtrAllocator Allocator;
std::unique_ptr<DwarfStringPool> StringPool; // Entries owned by Allocator.
std::vector<std::unique_ptr<CompileUnit>> CompileUnits;
+ std::vector<std::unique_ptr<LineTable>> LineTables;
DIEAbbrevSet Abbreviations;
SmallString<4096> FileBytes;
@@ -210,9 +279,17 @@ public:
///
/// \returns a dwarfgen::CompileUnit that can be used to retrieve the compile
/// unit dwarfgen::DIE that can be used to add attributes and add child DIE
- /// objedts to.
+ /// objects to.
dwarfgen::CompileUnit &addCompileUnit();
+ /// Add a line table unit to be generated.
+ /// \param Format the DWARF format to use (DWARF32 or DWARF64).
+ ///
+ /// \returns a dwarfgen::LineTable that can be used to customise the contents
+ /// of the line table.
+ LineTable &
+ addLineTable(dwarf::DwarfFormat DwarfFormat = dwarf::DwarfFormat::DWARF32);
+
BumpPtrAllocator &getAllocator() { return Allocator; }
AsmPrinter *getAsmPrinter() const { return Asm.get(); }
MCContext *getMCContext() const { return MC.get(); }
OpenPOWER on IntegriCloud