1763 lines
70 KiB
C++
1763 lines
70 KiB
C++
//===- DWARFDebugLineTest.cpp ---------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "DwarfGenerator.h"
|
|
#include "DwarfUtils.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
|
|
#include "llvm/Object/ObjectFile.h"
|
|
#include "llvm/Testing/Support/Error.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
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), Recoverable(Error::success()),
|
|
RecordRecoverable(std::bind(&CommonFixture::recordRecoverable, this,
|
|
std::placeholders::_1)),
|
|
Unrecoverable(Error::success()),
|
|
RecordUnrecoverable(std::bind(&CommonFixture::recordUnrecoverable, this,
|
|
std::placeholders::_1)){};
|
|
|
|
~CommonFixture() {
|
|
EXPECT_FALSE(Recoverable);
|
|
EXPECT_FALSE(Unrecoverable);
|
|
}
|
|
|
|
bool setupGenerator(uint16_t Version = 4, uint8_t AddrSize = 8) {
|
|
AddressSize = AddrSize;
|
|
Triple T =
|
|
getDefaultTargetTripleForAddrSize(AddressSize == 0 ? 8 : AddressSize);
|
|
if (!isConfigurationSupported(T))
|
|
return false;
|
|
auto ExpectedGenerator = Generator::create(T, Version);
|
|
if (ExpectedGenerator)
|
|
Gen.reset(ExpectedGenerator->release());
|
|
return true;
|
|
}
|
|
|
|
void generate() {
|
|
Context = createContext();
|
|
assert(Context != nullptr && "test state is not valid");
|
|
const DWARFObject &Obj = Context->getDWARFObj();
|
|
uint8_t TargetAddrSize = AddressSize == 0 ? 8 : AddressSize;
|
|
LineData = DWARFDataExtractor(
|
|
Obj, Obj.getLineSection(),
|
|
getDefaultTargetTripleForAddrSize(TargetAddrSize).isLittleEndian(),
|
|
AddressSize);
|
|
}
|
|
|
|
std::unique_ptr<DWARFContext> createContext() {
|
|
if (!Gen)
|
|
return nullptr;
|
|
StringRef FileBytes = Gen->generate();
|
|
MemoryBufferRef FileBuffer(FileBytes, "dwarf");
|
|
auto Obj = object::ObjectFile::createObjectFile(FileBuffer);
|
|
if (Obj)
|
|
return DWARFContext::create(**Obj);
|
|
return nullptr;
|
|
}
|
|
|
|
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, Units);
|
|
}
|
|
|
|
void recordRecoverable(Error Err) {
|
|
Recoverable = joinErrors(std::move(Recoverable), std::move(Err));
|
|
}
|
|
void recordUnrecoverable(Error Err) {
|
|
Unrecoverable = joinErrors(std::move(Unrecoverable), std::move(Err));
|
|
}
|
|
|
|
Expected<const DWARFDebugLine::LineTable *>
|
|
getOrParseLineTableFatalErrors(uint64_t Offset = 0) {
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(
|
|
LineData, Offset, *Context, nullptr, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());
|
|
return ExpectedLineTable;
|
|
}
|
|
|
|
uint8_t AddressSize;
|
|
std::unique_ptr<Generator> Gen;
|
|
std::unique_ptr<DWARFContext> Context;
|
|
DWARFDataExtractor LineData;
|
|
DWARFDebugLine Line;
|
|
Error Recoverable;
|
|
std::function<void(Error)> RecordRecoverable;
|
|
Error Unrecoverable;
|
|
std::function<void(Error)> RecordUnrecoverable;
|
|
|
|
SmallVector<std::unique_ptr<DWARFUnit>, 2> Units;
|
|
};
|
|
|
|
// 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() override { std::tie(Version, Format) = GetParam(); }
|
|
|
|
uint16_t Version;
|
|
DwarfFormat Format;
|
|
};
|
|
|
|
void checkDefaultPrologue(uint16_t Version, DwarfFormat Format,
|
|
DWARFDebugLine::Prologue Prologue,
|
|
uint64_t BodyLength) {
|
|
// Check version specific fields and values.
|
|
uint64_t UnitLength;
|
|
uint64_t PrologueLength;
|
|
switch (Version) {
|
|
case 4:
|
|
PrologueLength = 36;
|
|
UnitLength = PrologueLength + 2;
|
|
EXPECT_EQ(Prologue.MaxOpsPerInst, 1u);
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
PrologueLength = 35;
|
|
UnitLength = PrologueLength + 2;
|
|
break;
|
|
case 5:
|
|
PrologueLength = 42;
|
|
UnitLength = PrologueLength + 4;
|
|
EXPECT_EQ(Prologue.getAddressSize(), 8u);
|
|
EXPECT_EQ(Prologue.SegSelectorSize, 0u);
|
|
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, 1u);
|
|
EXPECT_EQ(Prologue.DefaultIsStmt, 1u);
|
|
EXPECT_EQ(Prologue.LineBase, -5);
|
|
EXPECT_EQ(Prologue.LineRange, 14u);
|
|
EXPECT_EQ(Prologue.OpcodeBase, 13u);
|
|
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(), 1u);
|
|
ASSERT_EQ(Prologue.IncludeDirectories[0].getForm(), DW_FORM_string);
|
|
EXPECT_STREQ(*Prologue.IncludeDirectories[0].getAsCString(), "a dir");
|
|
ASSERT_EQ(Prologue.FileNames.size(), 1u);
|
|
ASSERT_EQ(Prologue.FileNames[0].Name.getForm(), DW_FORM_string);
|
|
ASSERT_EQ(Prologue.FileNames[0].DirIdx, 0u);
|
|
EXPECT_STREQ(*Prologue.FileNames[0].Name.getAsCString(), "a file");
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) {
|
|
if (!setupGenerator())
|
|
return;
|
|
generate();
|
|
|
|
EXPECT_THAT_EXPECTED(
|
|
getOrParseLineTableFatalErrors(0),
|
|
FailedWithMessage(
|
|
"offset 0x00000000 is not a valid debug line section offset"));
|
|
// Repeat to show that an error is reported each time.
|
|
EXPECT_THAT_EXPECTED(
|
|
getOrParseLineTableFatalErrors(0),
|
|
FailedWithMessage(
|
|
"offset 0x00000000 is not a valid debug line section offset"));
|
|
|
|
// Show that an error is reported for later offsets too.
|
|
EXPECT_THAT_EXPECTED(
|
|
getOrParseLineTableFatalErrors(1),
|
|
FailedWithMessage(
|
|
"offset 0x00000001 is not a valid debug line section offset"));
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) {
|
|
if (!setupGenerator())
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
LT.setCustomPrologue({{0, LineTable::Byte}});
|
|
|
|
generate();
|
|
|
|
EXPECT_THAT_EXPECTED(
|
|
getOrParseLineTableFatalErrors(0),
|
|
FailedWithMessage(
|
|
"parsing line table prologue at offset 0x00000000: "
|
|
"unexpected end of data at offset 0x1 while reading [0x0, 0x4)"));
|
|
|
|
EXPECT_THAT_EXPECTED(
|
|
getOrParseLineTableFatalErrors(1),
|
|
FailedWithMessage(
|
|
"offset 0x00000001 is not a valid debug line section offset"));
|
|
}
|
|
|
|
TEST_P(DebugLineParameterisedFixture, PrologueGetLength) {
|
|
if (!setupGenerator(Version))
|
|
return;
|
|
LineTable < = Gen->addLineTable(Format);
|
|
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
|
|
LT.setPrologue(Prologue);
|
|
generate();
|
|
|
|
// + 10 for sizes of DWARF-32 unit length, version, prologue length.
|
|
uint64_t ExpectedLength = Prologue.PrologueLength + 10;
|
|
if (Version == 5)
|
|
// Add address and segment selector size fields.
|
|
ExpectedLength += 2;
|
|
if (Format == DWARF64)
|
|
// Unit length grows by 8, prologue length by 4.
|
|
ExpectedLength += 12;
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
EXPECT_EQ((*ExpectedLineTable)->Prologue.getLength(), ExpectedLength);
|
|
}
|
|
|
|
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, RecordRecoverable);
|
|
ASSERT_TRUE(ExpectedLineTable.operator bool());
|
|
EXPECT_FALSE(Recoverable);
|
|
const DWARFDebugLine::LineTable *Expected = *ExpectedLineTable;
|
|
checkDefaultPrologue(Version, Format, Expected->Prologue, 16);
|
|
EXPECT_EQ(Expected->Sequences.size(), 1u);
|
|
|
|
uint64_t SecondOffset =
|
|
Expected->Prologue.sizeofTotalLength() + Expected->Prologue.TotalLength;
|
|
Recoverable = Error::success();
|
|
auto ExpectedLineTable2 = Line.getOrParseLineTable(
|
|
LineData, SecondOffset, *Context, nullptr, RecordRecoverable);
|
|
ASSERT_TRUE(ExpectedLineTable2.operator bool());
|
|
EXPECT_FALSE(Recoverable);
|
|
const DWARFDebugLine::LineTable *Expected2 = *ExpectedLineTable2;
|
|
checkDefaultPrologue(Version, Format, Expected2->Prologue, 32);
|
|
EXPECT_EQ(Expected2->Sequences.size(), 2u);
|
|
|
|
EXPECT_NE(Expected, Expected2);
|
|
|
|
// Check that if the same offset is requested, the exact same pointer is
|
|
// returned.
|
|
Recoverable = Error::success();
|
|
auto ExpectedLineTable3 = Line.getOrParseLineTable(
|
|
LineData, 0, *Context, nullptr, RecordRecoverable);
|
|
ASSERT_TRUE(ExpectedLineTable3.operator bool());
|
|
EXPECT_FALSE(Recoverable);
|
|
EXPECT_EQ(Expected, *ExpectedLineTable3);
|
|
|
|
Recoverable = Error::success();
|
|
auto ExpectedLineTable4 = Line.getOrParseLineTable(
|
|
LineData, SecondOffset, *Context, nullptr, RecordRecoverable);
|
|
ASSERT_TRUE(ExpectedLineTable4.operator bool());
|
|
EXPECT_FALSE(Recoverable);
|
|
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({{0xfffffff0, LineTable::Long}});
|
|
|
|
generate();
|
|
|
|
EXPECT_THAT_EXPECTED(
|
|
getOrParseLineTableFatalErrors(),
|
|
FailedWithMessage(
|
|
"parsing line table prologue at offset 0x00000000: unsupported "
|
|
"reserved unit length of value 0xfffffff0"));
|
|
}
|
|
|
|
struct DebugLineUnsupportedVersionFixture : public TestWithParam<uint16_t>,
|
|
public CommonFixture {
|
|
void SetUp() override { Version = GetParam(); }
|
|
|
|
uint16_t Version;
|
|
};
|
|
|
|
TEST_P(DebugLineUnsupportedVersionFixture, ErrorForUnsupportedVersion) {
|
|
if (!setupGenerator())
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
LT.setCustomPrologue(
|
|
{{LineTable::Half, LineTable::Long}, {Version, LineTable::Half}});
|
|
|
|
generate();
|
|
|
|
EXPECT_THAT_EXPECTED(
|
|
getOrParseLineTableFatalErrors(),
|
|
FailedWithMessage("parsing line table prologue at offset 0x00000000: "
|
|
"unsupported version " +
|
|
std::to_string(Version)));
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(UnsupportedVersionTestParams,
|
|
DebugLineUnsupportedVersionFixture,
|
|
Values(/*1 below min */ 1, /* 1 above max */ 6,
|
|
/* Maximum possible */ 0xffff), );
|
|
|
|
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();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
EXPECT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
|
|
EXPECT_THAT_ERROR(
|
|
std::move(Recoverable),
|
|
FailedWithMessage(
|
|
"parsing line table prologue at 0x00000000 found an invalid "
|
|
"directory or file table description at 0x00000014",
|
|
"failed to parse entry content descriptions because no path was "
|
|
"found"));
|
|
}
|
|
|
|
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();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
DWARFDebugLine::LineTable Result(**ExpectedLineTable);
|
|
// Undo the earlier modification so that it can be compared against a
|
|
// "default" prologue.
|
|
--Result.Prologue.PrologueLength;
|
|
checkDefaultPrologue(Version, Format, Result.Prologue, 0);
|
|
|
|
uint64_t ExpectedEnd =
|
|
Prologue.TotalLength + 1 + Prologue.sizeofTotalLength();
|
|
EXPECT_THAT_ERROR(
|
|
std::move(Recoverable),
|
|
FailedWithMessage(
|
|
("unknown data in line table prologue at offset 0x00000000: "
|
|
"parsing ended (at offset 0x000000" +
|
|
Twine::utohexstr(ExpectedEnd - 1) +
|
|
") before reaching the prologue end at offset 0x000000" +
|
|
Twine::utohexstr(ExpectedEnd))
|
|
.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();
|
|
Prologue.PrologueLength -= 2;
|
|
LT.setPrologue(Prologue);
|
|
|
|
generate();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
DWARFDebugLine::LineTable Result(**ExpectedLineTable);
|
|
|
|
// Parsing will stop before reading a complete file entry.
|
|
ASSERT_EQ(Result.Prologue.IncludeDirectories.size(), 1u);
|
|
EXPECT_EQ(toStringRef(Result.Prologue.IncludeDirectories[0]), "a dir");
|
|
EXPECT_EQ(Result.Prologue.FileNames.size(), 0u);
|
|
|
|
// The exact place where the parsing will stop depends on the structure of the
|
|
// prologue and the last complete field we are able to read. Before V5 we stop
|
|
// before reading the file length. In V5, we stop before the filename.
|
|
uint64_t ExpectedEnd = Prologue.TotalLength + Prologue.sizeofTotalLength() -
|
|
(Version < 5 ? 2 : 8);
|
|
std::vector<std::string> Errs;
|
|
Errs.emplace_back(
|
|
(Twine("parsing line table prologue at 0x00000000 found an invalid "
|
|
"directory or file table description at 0x000000") +
|
|
Twine::utohexstr(ExpectedEnd))
|
|
.str());
|
|
if (Version < 5) {
|
|
Errs.emplace_back("file names table was not null terminated before the end "
|
|
"of the prologue");
|
|
} else {
|
|
Errs.emplace_back(
|
|
"failed to parse file entry because extracting the form value failed");
|
|
}
|
|
EXPECT_THAT_ERROR(std::move(Recoverable),
|
|
FailedWithMessageArray(testing::ElementsAreArray(Errs)));
|
|
}
|
|
|
|
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, ErrorForExtendedOpcodeLengthSmallerThanExpected) {
|
|
if (!setupGenerator())
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
LT.addByte(0xaa);
|
|
// The Length should be 1 + sizeof(ULEB) for a set discriminator opcode.
|
|
// The operand will be read for both the discriminator opcode and then parsed
|
|
// again as DW_LNS_negate_stmt, to respect the claimed length.
|
|
LT.addExtendedOpcode(1, DW_LNE_set_discriminator,
|
|
{{DW_LNS_negate_stmt, LineTable::ULEB}});
|
|
LT.addByte(0xbb);
|
|
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
|
|
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
|
|
generate();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(std::move(Recoverable),
|
|
FailedWithMessage("unexpected line op length at offset "
|
|
"0x00000031 expected 0x01 found 0x02"));
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Rows[1].IsStmt, 0u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Rows[1].Discriminator, DW_LNS_negate_stmt);
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthLargerThanExpected) {
|
|
if (!setupGenerator())
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
LT.addByte(0xaa);
|
|
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
|
|
// The Length should be 1 for an end sequence opcode.
|
|
LT.addExtendedOpcode(2, DW_LNE_end_sequence, {});
|
|
// The negate statement opcode will be skipped.
|
|
LT.addStandardOpcode(DW_LNS_negate_stmt, {});
|
|
LT.addByte(0xbb);
|
|
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
|
|
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
|
|
generate();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(std::move(Recoverable),
|
|
FailedWithMessage("unexpected line op length at offset "
|
|
"0x00000032 expected 0x02 found 0x01"));
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 4u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 2u);
|
|
ASSERT_EQ((*ExpectedLineTable)->Sequences[1].FirstRowIndex, 2u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Rows[2].IsStmt, 1u);
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, ErrorForUnitLengthTooLarge) {
|
|
if (!setupGenerator())
|
|
return;
|
|
|
|
LineTable &Padding = Gen->addLineTable();
|
|
// Add some padding to show that a non-zero offset is handled correctly.
|
|
Padding.setCustomPrologue({{0, LineTable::Byte}});
|
|
LineTable < = Gen->addLineTable();
|
|
LT.addStandardOpcode(DW_LNS_copy, {});
|
|
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
|
|
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
|
|
// Set the total length to 1 higher than the actual length.
|
|
++Prologue.TotalLength;
|
|
LT.setPrologue(Prologue);
|
|
|
|
generate();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 1, *Context,
|
|
nullptr, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(
|
|
std::move(Recoverable),
|
|
FailedWithMessage("line table program with offset 0x00000001 has length "
|
|
"0x00000034 but only 0x00000033 bytes are available"));
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
EXPECT_EQ((*ExpectedLineTable)->Rows.size(), 2u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) {
|
|
if (!setupGenerator(4, 8))
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
// The line data extractor expects size 8 (Quad) addresses.
|
|
uint64_t Addr1 = 0x11223344;
|
|
LT.addExtendedOpcode(5, DW_LNE_set_address, {{Addr1, LineTable::Long}});
|
|
LT.addStandardOpcode(DW_LNS_copy, {});
|
|
// Show that the expected address size is unchanged, so later valid lines
|
|
// don't cause a problem.
|
|
uint64_t Addr2 = 0x1122334455667788;
|
|
LT.addExtendedOpcode(9, DW_LNE_set_address, {{Addr2, LineTable::Quad}});
|
|
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
|
|
generate();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(std::move(Recoverable),
|
|
FailedWithMessage("mismatching address size at offset "
|
|
"0x00000030 expected 0x08 found 0x04"));
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 2u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, Addr1);
|
|
EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2);
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture,
|
|
ErrorForMismatchedAddressSizeUnsetInitialAddress) {
|
|
if (!setupGenerator(4, 0))
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
uint64_t Addr1 = 0x11223344;
|
|
LT.addExtendedOpcode(5, DW_LNE_set_address, {{Addr1, LineTable::Long}});
|
|
LT.addStandardOpcode(DW_LNS_copy, {});
|
|
uint64_t Addr2 = 0x1122334455667788;
|
|
LT.addExtendedOpcode(9, DW_LNE_set_address, {{Addr2, LineTable::Quad}});
|
|
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
|
|
generate();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(std::move(Recoverable),
|
|
FailedWithMessage("mismatching address size at offset "
|
|
"0x00000038 expected 0x04 found 0x08"));
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 2u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, Addr1);
|
|
EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2);
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture,
|
|
ErrorForUnsupportedAddressSizeInSetAddressLength) {
|
|
// Use DWARF v4, and 0 for data extractor address size so that the address
|
|
// size is derived from the opcode length.
|
|
if (!setupGenerator(4, 0))
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
// 4 == length of the extended opcode, i.e. 1 for the opcode itself and 3 for
|
|
// the Half (2) + Byte (1) operand, representing the unsupported address size.
|
|
LT.addExtendedOpcode(4, DW_LNE_set_address,
|
|
{{0x1234, LineTable::Half}, {0x56, LineTable::Byte}});
|
|
LT.addStandardOpcode(DW_LNS_copy, {});
|
|
// Special opcode to ensure the address has changed between the first and last
|
|
// row in the sequence. Without this, the sequence will not be recorded.
|
|
LT.addByte(0xaa);
|
|
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
|
|
generate();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(
|
|
std::move(Recoverable),
|
|
FailedWithMessage("address size 0x03 of DW_LNE_set_address opcode at "
|
|
"offset 0x00000030 is unsupported"));
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
|
|
// Show that the set address opcode is ignored in this case.
|
|
EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, 0u);
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, ErrorForAddressSizeGreaterThanByteSize) {
|
|
// Use DWARF v4, and 0 for data extractor address size so that the address
|
|
// size is derived from the opcode length.
|
|
if (!setupGenerator(4, 0))
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
// Specifically use an operand size that has a trailing byte of a supported
|
|
// size (8), so that any potential truncation would result in a valid size.
|
|
std::vector<LineTable::ValueAndLength> Operands(0x108);
|
|
LT.addExtendedOpcode(Operands.size() + 1, DW_LNE_set_address, Operands);
|
|
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
|
|
generate();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(
|
|
std::move(Recoverable),
|
|
FailedWithMessage("address size 0x108 of DW_LNE_set_address opcode at "
|
|
"offset 0x00000031 is unsupported"));
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, ErrorForUnsupportedAddressSizeDefinedInHeader) {
|
|
// Use 0 for data extractor address size so that it does not clash with the
|
|
// header address size.
|
|
if (!setupGenerator(5, 0))
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
// AddressSize + 1 == length of the extended opcode, i.e. 1 for the opcode
|
|
// itself and 9 for the Quad (8) + Byte (1) operand representing the
|
|
// unsupported address size.
|
|
uint8_t AddressSize = 9;
|
|
LT.addExtendedOpcode(AddressSize + 1, DW_LNE_set_address,
|
|
{{0x12345678, LineTable::Quad}, {0, LineTable::Byte}});
|
|
LT.addStandardOpcode(DW_LNS_copy, {});
|
|
// Special opcode to ensure the address has changed between the first and last
|
|
// row in the sequence. Without this, the sequence will not be recorded.
|
|
LT.addByte(0xaa);
|
|
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
|
|
Prologue.FormParams.AddrSize = AddressSize;
|
|
LT.setPrologue(Prologue);
|
|
|
|
generate();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(
|
|
std::move(Recoverable),
|
|
FailedWithMessage("address size 0x09 of DW_LNE_set_address opcode at "
|
|
"offset 0x00000038 is unsupported"));
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u);
|
|
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
|
|
// Show that the set address opcode is ignored in this case.
|
|
EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, 0u);
|
|
}
|
|
|
|
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, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(std::move(Recoverable),
|
|
FailedWithMessage("last sequence in debug line table at "
|
|
"offset 0x00000000 is not terminated"));
|
|
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
EXPECT_EQ((*ExpectedLineTable)->Rows.size(), 6u);
|
|
// The unterminated sequence is not added to the sequence list.
|
|
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
|
|
}
|
|
|
|
struct AdjustAddressFixtureBase : public CommonFixture {
|
|
virtual ~AdjustAddressFixtureBase() {}
|
|
|
|
// Create and update the prologue as specified by the subclass, then return
|
|
// the length of the table.
|
|
virtual uint64_t editPrologue(LineTable <) = 0;
|
|
|
|
virtual uint64_t getAdjustedAddr(uint64_t Base, uint64_t ConstIncrs,
|
|
uint64_t SpecialIncrs,
|
|
uint64_t AdvanceIncrs) {
|
|
return Base + ConstIncrs + SpecialIncrs + AdvanceIncrs;
|
|
}
|
|
|
|
virtual uint64_t getAdjustedLine(uint64_t Base, uint64_t Incr) {
|
|
return Base + Incr;
|
|
}
|
|
|
|
uint64_t setupNoProblemTable() {
|
|
LineTable &NoProblem = Gen->addLineTable();
|
|
NoProblem.addExtendedOpcode(9, DW_LNE_set_address,
|
|
{{0xabcd, LineTable::Quad}});
|
|
NoProblem.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
return editPrologue(NoProblem);
|
|
}
|
|
|
|
uint64_t setupConstAddPcFirstTable() {
|
|
LineTable &ConstAddPCFirst = Gen->addLineTable();
|
|
ConstAddPCFirst.addExtendedOpcode(9, DW_LNE_set_address,
|
|
{{ConstAddPCAddr, LineTable::Quad}});
|
|
ConstAddPCFirst.addStandardOpcode(DW_LNS_const_add_pc, {});
|
|
ConstAddPCFirst.addStandardOpcode(DW_LNS_const_add_pc, {});
|
|
ConstAddPCFirst.addStandardOpcode(DW_LNS_advance_pc,
|
|
{{0x10, LineTable::ULEB}});
|
|
ConstAddPCFirst.addByte(0x21); // Special opcode, +1 op, +1 line.
|
|
ConstAddPCFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
return editPrologue(ConstAddPCFirst);
|
|
}
|
|
|
|
uint64_t setupSpecialFirstTable() {
|
|
LineTable &SpecialFirst = Gen->addLineTable();
|
|
SpecialFirst.addExtendedOpcode(9, DW_LNE_set_address,
|
|
{{SpecialAddr, LineTable::Quad}});
|
|
SpecialFirst.addByte(0x22); // Special opcode, +1 op, +2 line.
|
|
SpecialFirst.addStandardOpcode(DW_LNS_const_add_pc, {});
|
|
SpecialFirst.addStandardOpcode(DW_LNS_advance_pc,
|
|
{{0x20, LineTable::ULEB}});
|
|
SpecialFirst.addByte(0x23); // Special opcode, +1 op, +3 line.
|
|
SpecialFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
return editPrologue(SpecialFirst);
|
|
}
|
|
|
|
uint64_t setupAdvancePcFirstTable() {
|
|
LineTable &AdvancePCFirst = Gen->addLineTable();
|
|
AdvancePCFirst.addExtendedOpcode(9, DW_LNE_set_address,
|
|
{{AdvancePCAddr, LineTable::Quad}});
|
|
AdvancePCFirst.addStandardOpcode(DW_LNS_advance_pc,
|
|
{{0x30, LineTable::ULEB}});
|
|
AdvancePCFirst.addStandardOpcode(DW_LNS_const_add_pc, {});
|
|
AdvancePCFirst.addStandardOpcode(DW_LNS_advance_pc,
|
|
{{0x40, LineTable::ULEB}});
|
|
AdvancePCFirst.addByte(0x24); // Special opcode, +1 op, +4 line.
|
|
AdvancePCFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
return editPrologue(AdvancePCFirst);
|
|
}
|
|
|
|
void setupTables(bool AddAdvancePCFirstTable) {
|
|
LineTable &Padding = Gen->addLineTable();
|
|
Padding.setCustomPrologue({{0, LineTable::Byte}});
|
|
NoProblemOffset = 1;
|
|
|
|
// Show that no warning is generated for the case where no
|
|
// DW_LNS_const_add_pc or special opcode is used.
|
|
ConstAddPCOffset = setupNoProblemTable() + NoProblemOffset;
|
|
|
|
// Show that the warning is emitted for the first DW_LNS_const_add_pc opcode
|
|
// and then not again.
|
|
SpecialOffset = setupConstAddPcFirstTable() + ConstAddPCOffset;
|
|
|
|
// Show that the warning is emitted for the first special opcode and then
|
|
// not again.
|
|
AdvancePCOffset = setupSpecialFirstTable() + SpecialOffset;
|
|
|
|
// Show that the warning is emitted for the first DW_LNS_advance_pc opcode
|
|
// (if requested) and then not again.
|
|
if (AddAdvancePCFirstTable)
|
|
setupAdvancePcFirstTable();
|
|
}
|
|
|
|
Expected<const DWARFDebugLine::LineTable *>
|
|
checkTable(uint64_t Offset, StringRef OpcodeType, const Twine &MsgSuffix) {
|
|
auto ExpectedTable = Line.getOrParseLineTable(LineData, Offset, *Context,
|
|
nullptr, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
|
|
if (!IsErrorExpected) {
|
|
EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());
|
|
} else {
|
|
if (!ExpectedTable)
|
|
return ExpectedTable;
|
|
uint64_t ExpectedOffset = Offset +
|
|
(*ExpectedTable)->Prologue.getLength() +
|
|
11; // 11 == size of DW_LNE_set_address.
|
|
std::string OffsetHex = Twine::utohexstr(Offset).str();
|
|
std::string OffsetZeroes = std::string(8 - OffsetHex.size(), '0');
|
|
std::string ExpectedHex = Twine::utohexstr(ExpectedOffset).str();
|
|
std::string ExpectedZeroes = std::string(8 - ExpectedHex.size(), '0');
|
|
EXPECT_THAT_ERROR(
|
|
std::move(Recoverable),
|
|
FailedWithMessage(("line table program at offset 0x" + OffsetZeroes +
|
|
OffsetHex + " contains a " + OpcodeType +
|
|
" opcode at offset 0x" + ExpectedZeroes +
|
|
ExpectedHex + ", " + MsgSuffix)
|
|
.str()));
|
|
}
|
|
return ExpectedTable;
|
|
}
|
|
|
|
void runTest(bool CheckAdvancePC, Twine MsgSuffix) {
|
|
if (!setupGenerator(Version))
|
|
return;
|
|
|
|
setupTables(/*AddAdvancePCFirstTable=*/CheckAdvancePC);
|
|
|
|
generate();
|
|
|
|
auto ExpectedNoProblem = Line.getOrParseLineTable(
|
|
LineData, NoProblemOffset, *Context, nullptr, RecordRecoverable);
|
|
EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());
|
|
EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
|
|
ASSERT_THAT_EXPECTED(ExpectedNoProblem, Succeeded());
|
|
|
|
auto ExpectedConstAddPC =
|
|
checkTable(ConstAddPCOffset, "DW_LNS_const_add_pc", MsgSuffix);
|
|
ASSERT_THAT_EXPECTED(ExpectedConstAddPC, Succeeded());
|
|
ASSERT_EQ((*ExpectedConstAddPC)->Rows.size(), 2u);
|
|
EXPECT_EQ((*ExpectedConstAddPC)->Rows[0].Address.Address,
|
|
getAdjustedAddr(ConstAddPCAddr, ConstIncr * 2, 0x1, 0x10));
|
|
EXPECT_EQ((*ExpectedConstAddPC)->Rows[0].Line, getAdjustedLine(1, 1));
|
|
EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
|
|
|
|
auto ExpectedSpecial = checkTable(SpecialOffset, "special", MsgSuffix);
|
|
ASSERT_THAT_EXPECTED(ExpectedSpecial, Succeeded());
|
|
ASSERT_EQ((*ExpectedSpecial)->Rows.size(), 3u);
|
|
EXPECT_EQ((*ExpectedSpecial)->Rows[0].Address.Address,
|
|
getAdjustedAddr(SpecialAddr, 0, 1, 0));
|
|
EXPECT_EQ((*ExpectedSpecial)->Rows[0].Line, getAdjustedLine(1, 2));
|
|
EXPECT_EQ((*ExpectedSpecial)->Rows[1].Address.Address,
|
|
getAdjustedAddr(SpecialAddr, ConstIncr, 0x2, 0x20));
|
|
EXPECT_EQ((*ExpectedSpecial)->Rows[1].Line, getAdjustedLine(1, 5));
|
|
EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
|
|
|
|
if (!CheckAdvancePC)
|
|
return;
|
|
|
|
auto ExpectedAdvancePC =
|
|
checkTable(AdvancePCOffset, "DW_LNS_advance_pc", MsgSuffix);
|
|
ASSERT_THAT_EXPECTED(ExpectedAdvancePC, Succeeded());
|
|
ASSERT_EQ((*ExpectedAdvancePC)->Rows.size(), 2u);
|
|
EXPECT_EQ((*ExpectedAdvancePC)->Rows[0].Address.Address,
|
|
getAdjustedAddr(AdvancePCAddr, ConstIncr, 0x1, 0x70));
|
|
EXPECT_EQ((*ExpectedAdvancePC)->Rows[0].Line, getAdjustedLine(1, 4));
|
|
}
|
|
|
|
uint64_t ConstIncr = 0x11;
|
|
uint64_t ConstAddPCAddr = 0x1234;
|
|
uint64_t SpecialAddr = 0x5678;
|
|
uint64_t AdvancePCAddr = 0xabcd;
|
|
uint64_t NoProblemOffset;
|
|
uint64_t ConstAddPCOffset;
|
|
uint64_t SpecialOffset;
|
|
uint64_t AdvancePCOffset;
|
|
|
|
uint16_t Version = 4;
|
|
bool IsErrorExpected;
|
|
};
|
|
|
|
struct MaxOpsPerInstFixture
|
|
: TestWithParam<std::tuple<uint16_t, uint8_t, bool>>,
|
|
AdjustAddressFixtureBase {
|
|
void SetUp() override {
|
|
std::tie(Version, MaxOpsPerInst, IsErrorExpected) = GetParam();
|
|
}
|
|
|
|
uint64_t editPrologue(LineTable <) override {
|
|
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
|
|
Prologue.MaxOpsPerInst = MaxOpsPerInst;
|
|
LT.setPrologue(Prologue);
|
|
return Prologue.TotalLength + Prologue.sizeofTotalLength();
|
|
}
|
|
|
|
uint8_t MaxOpsPerInst;
|
|
};
|
|
|
|
TEST_P(MaxOpsPerInstFixture, MaxOpsPerInstProblemsReportedCorrectly) {
|
|
runTest(/*CheckAdvancePC=*/true,
|
|
"but the prologue maximum_operations_per_instruction value is " +
|
|
Twine(unsigned(MaxOpsPerInst)) +
|
|
", which is unsupported. Assuming a value of 1 instead");
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
MaxOpsPerInstParams, MaxOpsPerInstFixture,
|
|
Values(std::make_tuple(3, 0, false), // Test for version < 4 (no error).
|
|
std::make_tuple(4, 0, true), // Test zero value for V4 (error).
|
|
std::make_tuple(4, 1, false), // Test good value for V4 (no error).
|
|
std::make_tuple(
|
|
4, 2, true)), ); // Test one higher than permitted V4 (error).
|
|
|
|
struct LineRangeFixture : TestWithParam<std::tuple<uint8_t, bool>>,
|
|
AdjustAddressFixtureBase {
|
|
void SetUp() override { std::tie(LineRange, IsErrorExpected) = GetParam(); }
|
|
|
|
uint64_t editPrologue(LineTable <) override {
|
|
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
|
|
Prologue.LineRange = LineRange;
|
|
LT.setPrologue(Prologue);
|
|
return Prologue.TotalLength + Prologue.sizeofTotalLength();
|
|
}
|
|
|
|
uint64_t getAdjustedAddr(uint64_t Base, uint64_t ConstIncr,
|
|
uint64_t SpecialIncr,
|
|
uint64_t AdvanceIncr) override {
|
|
if (LineRange == 0)
|
|
return Base + AdvanceIncr;
|
|
return AdjustAddressFixtureBase::getAdjustedAddr(Base, ConstIncr,
|
|
SpecialIncr, AdvanceIncr);
|
|
}
|
|
|
|
uint64_t getAdjustedLine(uint64_t Base, uint64_t Incr) override {
|
|
return LineRange != 0
|
|
? AdjustAddressFixtureBase::getAdjustedLine(Base, Incr)
|
|
: Base;
|
|
}
|
|
|
|
uint8_t LineRange;
|
|
};
|
|
|
|
TEST_P(LineRangeFixture, LineRangeProblemsReportedCorrectly) {
|
|
runTest(/*CheckAdvancePC=*/false,
|
|
"but the prologue line_range value is 0. The address and line will "
|
|
"not be adjusted");
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
LineRangeParams, LineRangeFixture,
|
|
Values(std::make_tuple(0, true), // Test zero value (error).
|
|
std::make_tuple(14, false)), ); // Test non-zero value (no error).
|
|
|
|
struct BadMinInstLenFixture : TestWithParam<std::tuple<uint8_t, bool>>,
|
|
AdjustAddressFixtureBase {
|
|
void SetUp() override {
|
|
std::tie(MinInstLength, IsErrorExpected) = GetParam();
|
|
}
|
|
|
|
uint64_t editPrologue(LineTable <) override {
|
|
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
|
|
Prologue.MinInstLength = MinInstLength;
|
|
LT.setPrologue(Prologue);
|
|
return Prologue.TotalLength + Prologue.sizeofTotalLength();
|
|
}
|
|
|
|
uint64_t getAdjustedAddr(uint64_t Base, uint64_t ConstIncr,
|
|
uint64_t SpecialIncr,
|
|
uint64_t AdvanceIncr) override {
|
|
return MinInstLength != 0 ? AdjustAddressFixtureBase::getAdjustedAddr(
|
|
Base, ConstIncr, SpecialIncr, AdvanceIncr)
|
|
: Base;
|
|
}
|
|
|
|
uint8_t MinInstLength;
|
|
};
|
|
|
|
TEST_P(BadMinInstLenFixture, MinInstLengthProblemsReportedCorrectly) {
|
|
runTest(/*CheckAdvancePC=*/true,
|
|
"but the prologue minimum_instruction_length value is 0, which "
|
|
"prevents any address advancing");
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
BadMinInstLenParams, BadMinInstLenFixture,
|
|
Values(std::make_tuple(0, true), // Test zero value (error).
|
|
std::make_tuple(1, false)), ); // Test non-zero value (no error).
|
|
|
|
TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) {
|
|
if (!setupGenerator())
|
|
return;
|
|
|
|
DWARFDebugLine::SectionParser Parser = setupParser();
|
|
|
|
EXPECT_EQ(Parser.getOffset(), 0u);
|
|
ASSERT_FALSE(Parser.done());
|
|
|
|
DWARFDebugLine::LineTable Parsed =
|
|
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
|
|
checkDefaultPrologue(4, DWARF32, Parsed.Prologue, 16);
|
|
EXPECT_EQ(Parsed.Sequences.size(), 1u);
|
|
EXPECT_EQ(Parser.getOffset(), 62u);
|
|
ASSERT_FALSE(Parser.done());
|
|
|
|
DWARFDebugLine::LineTable Parsed2 =
|
|
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
|
|
checkDefaultPrologue(4, DWARF64, Parsed2.Prologue, 16);
|
|
EXPECT_EQ(Parsed2.Sequences.size(), 1u);
|
|
EXPECT_EQ(Parser.getOffset(), 136u);
|
|
EXPECT_TRUE(Parser.done());
|
|
|
|
EXPECT_FALSE(Recoverable);
|
|
EXPECT_FALSE(Unrecoverable);
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) {
|
|
if (!setupGenerator())
|
|
return;
|
|
|
|
DWARFDebugLine::SectionParser Parser = setupParser();
|
|
|
|
EXPECT_EQ(Parser.getOffset(), 0u);
|
|
ASSERT_FALSE(Parser.done());
|
|
|
|
Parser.skip(RecordRecoverable, RecordUnrecoverable);
|
|
EXPECT_EQ(Parser.getOffset(), 62u);
|
|
ASSERT_FALSE(Parser.done());
|
|
|
|
Parser.skip(RecordRecoverable, RecordUnrecoverable);
|
|
EXPECT_EQ(Parser.getOffset(), 136u);
|
|
EXPECT_TRUE(Parser.done());
|
|
|
|
EXPECT_FALSE(Recoverable);
|
|
EXPECT_FALSE(Unrecoverable);
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) {
|
|
if (!setupGenerator())
|
|
return;
|
|
|
|
generate();
|
|
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
|
|
|
|
EXPECT_TRUE(Parser.done());
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenParsing) {
|
|
if (!setupGenerator())
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
LT.setCustomPrologue({{0xfffffff0, LineTable::Long}});
|
|
Gen->addLineTable();
|
|
generate();
|
|
|
|
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
|
|
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
|
|
|
|
EXPECT_EQ(Parser.getOffset(), 0u);
|
|
EXPECT_TRUE(Parser.done());
|
|
EXPECT_FALSE(Recoverable);
|
|
|
|
EXPECT_THAT_ERROR(
|
|
std::move(Unrecoverable),
|
|
FailedWithMessage(
|
|
"parsing line table prologue at offset 0x00000000: unsupported "
|
|
"reserved unit length of value 0xfffffff0"));
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenSkipping) {
|
|
if (!setupGenerator())
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
LT.setCustomPrologue({{0xfffffff0, LineTable::Long}});
|
|
Gen->addLineTable();
|
|
generate();
|
|
|
|
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
|
|
Parser.skip(RecordRecoverable, RecordUnrecoverable);
|
|
|
|
EXPECT_EQ(Parser.getOffset(), 0u);
|
|
EXPECT_TRUE(Parser.done());
|
|
EXPECT_FALSE(Recoverable);
|
|
|
|
EXPECT_THAT_ERROR(
|
|
std::move(Unrecoverable),
|
|
FailedWithMessage(
|
|
"parsing line table prologue at offset 0x00000000: unsupported "
|
|
"reserved unit length of value 0xfffffff0"));
|
|
}
|
|
|
|
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, Units);
|
|
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
|
|
ASSERT_FALSE(Parser.done());
|
|
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
|
|
|
|
EXPECT_TRUE(Parser.done());
|
|
EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());
|
|
|
|
EXPECT_THAT_ERROR(
|
|
std::move(Unrecoverable),
|
|
FailedWithMessage("parsing line table prologue at offset 0x00000000: "
|
|
"unsupported version 0",
|
|
"parsing line table prologue at offset 0x00000006: "
|
|
"unsupported version 1"));
|
|
}
|
|
|
|
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, Units);
|
|
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
|
|
EXPECT_FALSE(Unrecoverable);
|
|
ASSERT_FALSE(Parser.done());
|
|
EXPECT_THAT_ERROR(std::move(Recoverable),
|
|
FailedWithMessage("unexpected line op length at offset "
|
|
"0x00000030 expected 0x42 found 0x01"));
|
|
|
|
// Reset the error state so that it does not confuse the next set of checks.
|
|
Unrecoverable = Error::success();
|
|
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
|
|
|
|
EXPECT_TRUE(Parser.done());
|
|
EXPECT_THAT_ERROR(std::move(Recoverable),
|
|
FailedWithMessage("last sequence in debug line table at "
|
|
"offset 0x00000031 is not terminated"));
|
|
EXPECT_FALSE(Unrecoverable);
|
|
}
|
|
|
|
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, Units);
|
|
Parser.skip(RecordRecoverable, RecordUnrecoverable);
|
|
ASSERT_FALSE(Parser.done());
|
|
Parser.skip(RecordRecoverable, RecordUnrecoverable);
|
|
|
|
EXPECT_TRUE(Parser.done());
|
|
EXPECT_FALSE(Recoverable);
|
|
|
|
EXPECT_THAT_ERROR(
|
|
std::move(Unrecoverable),
|
|
FailedWithMessage("parsing line table prologue at offset 0x00000000: "
|
|
"unsupported version 0",
|
|
"parsing line table prologue at offset 0x00000006: "
|
|
"unsupported version 1"));
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) {
|
|
if (!setupGenerator())
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable(DWARF32);
|
|
LT.addExtendedOpcode(42, DW_LNE_end_sequence, {});
|
|
generate();
|
|
|
|
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
|
|
Parser.skip(RecordRecoverable, RecordUnrecoverable);
|
|
|
|
EXPECT_TRUE(Parser.done());
|
|
EXPECT_FALSE(Recoverable);
|
|
EXPECT_FALSE(Unrecoverable);
|
|
}
|
|
|
|
TEST_F(DebugLineBasicFixture, VerboseOutput) {
|
|
if (!setupGenerator(5))
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
LT.addByte(0); // Extended opcode with zero length.
|
|
LT.addByte(0);
|
|
// Zero-value extended opcode.
|
|
LT.addExtendedOpcode(2, 0, {{1, LineTable::Byte}});
|
|
// Unknown extended opcode.
|
|
LT.addExtendedOpcode(2, 0x42, {{1, LineTable::Byte}});
|
|
LT.addExtendedOpcode(9, DW_LNE_set_address,
|
|
{{0x123456789abcdef, LineTable::Quad}});
|
|
LT.addExtendedOpcode(6, DW_LNE_define_file,
|
|
{{'a', LineTable::Byte},
|
|
{'\0', LineTable::Byte},
|
|
{2, LineTable::ULEB},
|
|
{3, LineTable::ULEB},
|
|
{4, LineTable::ULEB}});
|
|
LT.addExtendedOpcode(2, DW_LNE_set_discriminator, {{0x7f, LineTable::ULEB}});
|
|
LT.addStandardOpcode(DW_LNS_copy, {});
|
|
LT.addStandardOpcode(DW_LNS_advance_pc, {{11, LineTable::ULEB}});
|
|
LT.addStandardOpcode(DW_LNS_advance_line, {{22, LineTable::SLEB}});
|
|
LT.addStandardOpcode(DW_LNS_set_file, {{33, LineTable::ULEB}});
|
|
LT.addStandardOpcode(DW_LNS_set_column, {{44, LineTable::ULEB}});
|
|
LT.addStandardOpcode(DW_LNS_negate_stmt, {});
|
|
LT.addStandardOpcode(DW_LNS_set_basic_block, {});
|
|
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
|
|
LT.addStandardOpcode(DW_LNS_fixed_advance_pc, {{55, LineTable::Half}});
|
|
LT.addStandardOpcode(DW_LNS_set_prologue_end, {});
|
|
LT.addStandardOpcode(DW_LNS_set_epilogue_begin, {});
|
|
LT.addStandardOpcode(DW_LNS_set_isa, {{66, LineTable::ULEB}});
|
|
// Add unknown standard opcode with operands.
|
|
LT.addStandardOpcode(
|
|
0xd, {{1, LineTable::ULEB}, {0x123456789abcdef, LineTable::ULEB}});
|
|
// Add unknown standard opcode without operands.
|
|
LT.addStandardOpcode(0xe, {});
|
|
LT.addByte(0xff); // Special opcode.
|
|
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
|
|
|
|
// Adjust the prologue to account for the extra standard opcode.
|
|
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
|
|
Prologue.TotalLength += 2;
|
|
Prologue.PrologueLength += 2;
|
|
Prologue.OpcodeBase += 2;
|
|
Prologue.StandardOpcodeLengths.push_back(2);
|
|
Prologue.StandardOpcodeLengths.push_back(0);
|
|
LT.setPrologue(Prologue);
|
|
|
|
generate();
|
|
|
|
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
|
|
std::string Output;
|
|
raw_string_ostream OS(Output);
|
|
Parser.parseNext(RecordRecoverable, RecordUnrecoverable, &OS,
|
|
/*Verbose=*/true);
|
|
OS.flush();
|
|
StringRef OutputRef(Output);
|
|
|
|
size_t Pos = 0;
|
|
auto NextLine = [&Pos, &OutputRef]() {
|
|
size_t EOL = OutputRef.find_first_of('\n', Pos);
|
|
StringRef Line = OutputRef.substr(Pos, EOL - Pos);
|
|
Pos = EOL + 1;
|
|
return Line;
|
|
};
|
|
EXPECT_EQ(NextLine(), "Line table prologue:");
|
|
EXPECT_EQ(NextLine(), " total_length: 0x00000078");
|
|
EXPECT_EQ(NextLine(), " format: DWARF32");
|
|
EXPECT_EQ(NextLine(), " version: 5");
|
|
EXPECT_EQ(NextLine(), " address_size: 8");
|
|
EXPECT_EQ(NextLine(), " seg_select_size: 0");
|
|
EXPECT_EQ(NextLine(), " prologue_length: 0x0000002c");
|
|
EXPECT_EQ(NextLine(), " min_inst_length: 1");
|
|
EXPECT_EQ(NextLine(), "max_ops_per_inst: 1");
|
|
EXPECT_EQ(NextLine(), " default_is_stmt: 1");
|
|
EXPECT_EQ(NextLine(), " line_base: -5");
|
|
EXPECT_EQ(NextLine(), " line_range: 14");
|
|
EXPECT_EQ(NextLine(), " opcode_base: 15");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_copy] = 0");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_advance_pc] = 1");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_advance_line] = 1");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_file] = 1");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_column] = 1");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_negate_stmt] = 0");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_basic_block] = 0");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_const_add_pc] = 0");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 1");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_prologue_end] = 0");
|
|
EXPECT_EQ(NextLine(),
|
|
"standard_opcode_lengths[DW_LNS_set_epilogue_begin] = 0");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_isa] = 1");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_unknown_d] = 2");
|
|
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_unknown_e] = 0");
|
|
EXPECT_EQ(NextLine(), "include_directories[ 0] = \"a dir\"");
|
|
EXPECT_EQ(NextLine(), "file_names[ 0]:");
|
|
EXPECT_EQ(NextLine(), " name: \"a file\"");
|
|
EXPECT_EQ(NextLine(), " dir_index: 0");
|
|
EXPECT_EQ(NextLine(), "");
|
|
EXPECT_EQ(NextLine(), " Address Line Column File ISA Discriminator Flags");
|
|
EXPECT_EQ(NextLine(), " ------------------ ------ ------ ------ --- ------------- -------------");
|
|
EXPECT_EQ(NextLine(),
|
|
"0x00000038: 00 Badly formed extended line op (length 0)");
|
|
EXPECT_EQ(NextLine(),
|
|
"0x0000003a: 00 Unrecognized extended op 0x00 length 2");
|
|
EXPECT_EQ(NextLine(),
|
|
"0x0000003e: 00 Unrecognized extended op 0x42 length 2");
|
|
EXPECT_EQ(NextLine(),
|
|
"0x00000042: 00 DW_LNE_set_address (0x0123456789abcdef)");
|
|
EXPECT_EQ(NextLine(), "0x0000004d: 00 DW_LNE_define_file (a, dir=2, "
|
|
"mod_time=(0x0000000000000003), length=4)");
|
|
EXPECT_EQ(NextLine(), "0x00000055: 00 DW_LNE_set_discriminator (127)");
|
|
EXPECT_EQ(NextLine(), "0x00000059: 01 DW_LNS_copy");
|
|
EXPECT_EQ(NextLine(), " 0x0123456789abcdef 1 0 1 "
|
|
"0 127 is_stmt");
|
|
EXPECT_EQ(NextLine(), "0x0000005a: 02 DW_LNS_advance_pc (11)");
|
|
EXPECT_EQ(NextLine(), "0x0000005c: 03 DW_LNS_advance_line (23)");
|
|
EXPECT_EQ(NextLine(), "0x0000005e: 04 DW_LNS_set_file (33)");
|
|
EXPECT_EQ(NextLine(), "0x00000060: 05 DW_LNS_set_column (44)");
|
|
EXPECT_EQ(NextLine(), "0x00000062: 06 DW_LNS_negate_stmt");
|
|
EXPECT_EQ(NextLine(), "0x00000063: 07 DW_LNS_set_basic_block");
|
|
EXPECT_EQ(NextLine(),
|
|
"0x00000064: 08 DW_LNS_const_add_pc (0x0000000000000011)");
|
|
EXPECT_EQ(NextLine(), "0x00000065: 09 DW_LNS_fixed_advance_pc (0x0037)");
|
|
EXPECT_EQ(NextLine(), "0x00000068: 0a DW_LNS_set_prologue_end");
|
|
EXPECT_EQ(NextLine(), "0x00000069: 0b DW_LNS_set_epilogue_begin");
|
|
EXPECT_EQ(NextLine(), "0x0000006a: 0c DW_LNS_set_isa (66)");
|
|
EXPECT_EQ(NextLine(), "0x0000006c: 0d Unrecognized standard opcode "
|
|
"(operands: 0x0000000000000001, 0x0123456789abcdef)");
|
|
EXPECT_EQ(NextLine(), "0x00000077: 0e Unrecognized standard opcode");
|
|
EXPECT_EQ(NextLine(), "0x00000078: ff address += 17, line += -3");
|
|
EXPECT_EQ(NextLine(),
|
|
" 0x0123456789abce53 20 44 33 66 "
|
|
" 0 basic_block prologue_end epilogue_begin");
|
|
EXPECT_EQ(NextLine(), "0x00000079: 00 DW_LNE_end_sequence");
|
|
EXPECT_EQ(NextLine(), " 0x0123456789abce53 20 44 33 "
|
|
"66 0 end_sequence");
|
|
EXPECT_EQ(NextLine(), "");
|
|
EXPECT_EQ(Output.size(), Pos);
|
|
}
|
|
|
|
struct TruncatedPrologueFixture
|
|
: public TestWithParam<
|
|
std::tuple<uint64_t, uint64_t, uint16_t, DwarfFormat, StringRef>>,
|
|
public CommonFixture {
|
|
void SetUp() override {
|
|
std::tie(Length, ExpectedOffset, Version, Format, ExpectedErr) = GetParam();
|
|
}
|
|
|
|
uint64_t Length;
|
|
uint64_t ExpectedOffset;
|
|
uint16_t Version;
|
|
DwarfFormat Format;
|
|
StringRef ExpectedErr;
|
|
};
|
|
|
|
TEST_P(TruncatedPrologueFixture, ErrorForTruncatedPrologue) {
|
|
if (!setupGenerator(Version))
|
|
return;
|
|
|
|
LineTable &Padding = Gen->addLineTable();
|
|
// Add some padding to show that a non-zero offset is handled correctly.
|
|
Padding.setCustomPrologue({{0, LineTable::Byte}});
|
|
|
|
// Add a table with only two standard opcodes - we don't need to test the full
|
|
// set.
|
|
LineTable &Table = Gen->addLineTable(Format);
|
|
DWARFDebugLine::Prologue InputPrologue = Table.createBasicPrologue();
|
|
InputPrologue.OpcodeBase = 3;
|
|
InputPrologue.StandardOpcodeLengths.resize(2);
|
|
Table.setPrologue(InputPrologue);
|
|
|
|
generate();
|
|
// Truncate the data extractor to the specified length.
|
|
LineData = DWARFDataExtractor(LineData, Length);
|
|
|
|
DWARFDebugLine::Prologue Prologue;
|
|
uint64_t Offset = 1;
|
|
Error Err = Prologue.parse(LineData, &Offset, RecordRecoverable, *Context);
|
|
|
|
EXPECT_THAT_ERROR(std::move(Err), FailedWithMessage(ExpectedErr.str()));
|
|
EXPECT_EQ(Offset, ExpectedOffset);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
TruncatedPrologueParams, TruncatedPrologueFixture,
|
|
Values(
|
|
// Truncated length:
|
|
std::make_tuple(
|
|
4, 1, 4, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0x4 while reading [0x1, 0x5)"),
|
|
std::make_tuple(
|
|
4, 1, 4, DWARF64,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0x4 while reading [0x1, 0x5)"),
|
|
std::make_tuple(
|
|
0xc, 1, 4, DWARF64,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0xc while reading [0x5, 0xd)"),
|
|
// Truncated version:
|
|
std::make_tuple(
|
|
6, 5, 4, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0x6 while reading [0x5, 0x7)"),
|
|
// Truncated address size:
|
|
std::make_tuple(
|
|
7, 7, 5, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0x7 while reading [0x7, 0x8)"),
|
|
// Truncated segment selector size:
|
|
std::make_tuple(
|
|
8, 8, 5, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0x8 while reading [0x8, 0x9)"),
|
|
// Truncated prologue length:
|
|
std::make_tuple(
|
|
0xa, 7, 4, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0xa while reading [0x7, 0xb)"),
|
|
std::make_tuple(
|
|
0x16, 0xf, 4, DWARF64,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0x16 while reading [0xf, 0x17)"),
|
|
// Truncated min instruction length:
|
|
std::make_tuple(
|
|
0xb, 0xb, 4, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0xb while reading [0xb, 0xc)"),
|
|
// Truncated max ops per inst:
|
|
std::make_tuple(
|
|
0xc, 0xc, 4, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0xc while reading [0xc, 0xd)"),
|
|
// Truncated default is stmt:
|
|
std::make_tuple(
|
|
0xd, 0xd, 4, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0xd while reading [0xd, 0xe)"),
|
|
// Truncated line base:
|
|
std::make_tuple(
|
|
0xe, 0xe, 4, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0xe while reading [0xe, 0xf)"),
|
|
// Truncated line range:
|
|
std::make_tuple(
|
|
0xf, 0xf, 4, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0xf while reading [0xf, 0x10)"),
|
|
// Truncated opcode base:
|
|
std::make_tuple(
|
|
0x10, 0x10, 4, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0x10 while reading [0x10, 0x11)"),
|
|
// Truncated first standard opcode:
|
|
std::make_tuple(
|
|
0x11, 0x11, 4, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0x11 while reading [0x11, 0x12)"),
|
|
// Truncated second standard opcode:
|
|
std::make_tuple(
|
|
0x12, 0x12, 4, DWARF32,
|
|
"parsing line table prologue at offset 0x00000001: unexpected end "
|
|
"of data at offset 0x12 while reading [0x12, 0x13)")), );
|
|
|
|
using ValueAndLengths = std::vector<LineTable::ValueAndLength>;
|
|
|
|
struct TruncatedOpcodeFixtureBase : public CommonFixture {
|
|
LineTable &setupTable() {
|
|
LineTable < = Gen->addLineTable();
|
|
|
|
// Creating the prologue before adding any opcodes ensures that the unit
|
|
// length does not include the table body.
|
|
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
|
|
|
|
// Add an unrecognised standard opcode, and adjust prologue properties
|
|
// accordingly.
|
|
Prologue.TotalLength += BodyLength + 1;
|
|
++Prologue.PrologueLength;
|
|
++Prologue.OpcodeBase;
|
|
Prologue.StandardOpcodeLengths.push_back(2);
|
|
LT.setPrologue(Prologue);
|
|
|
|
return LT;
|
|
}
|
|
|
|
void runTest(uint8_t OpcodeValue) {
|
|
generate();
|
|
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
|
|
std::string Output;
|
|
raw_string_ostream OS(Output);
|
|
Parser.parseNext(RecordRecoverable, RecordUnrecoverable, &OS,
|
|
/*Verbose=*/true);
|
|
OS.flush();
|
|
|
|
std::string LinePrefix =
|
|
("0x0000002f: 0" + Twine::utohexstr(OpcodeValue) + " ").str();
|
|
StringRef OutputRef(Output);
|
|
StringRef OutputToCheck = OutputRef.split(LinePrefix).second;
|
|
// Each extended opcode ends with a new line and then the table ends with an
|
|
// additional blank line.
|
|
EXPECT_EQ((ExpectedOutput + "\n\n").str(), OutputToCheck);
|
|
}
|
|
|
|
uint64_t BodyLength;
|
|
uint8_t Opcode;
|
|
ValueAndLengths Operands;
|
|
StringRef ExpectedOutput;
|
|
StringRef ExpectedErr;
|
|
};
|
|
|
|
struct TruncatedStandardOpcodeFixture
|
|
: public TestWithParam<
|
|
std::tuple<uint64_t, uint8_t, ValueAndLengths, StringRef, StringRef>>,
|
|
public TruncatedOpcodeFixtureBase {
|
|
void SetUp() override {
|
|
std::tie(BodyLength, Opcode, Operands, ExpectedOutput, ExpectedErr) =
|
|
GetParam();
|
|
}
|
|
};
|
|
|
|
struct TruncatedExtendedOpcodeFixture
|
|
: public TestWithParam<std::tuple<uint64_t, uint64_t, uint8_t,
|
|
ValueAndLengths, StringRef, StringRef>>,
|
|
public TruncatedOpcodeFixtureBase {
|
|
void SetUp() override {
|
|
std::tie(BodyLength, OpcodeLength, Opcode, Operands, ExpectedOutput,
|
|
ExpectedErr) = GetParam();
|
|
}
|
|
|
|
uint64_t OpcodeLength;
|
|
};
|
|
|
|
TEST_P(TruncatedExtendedOpcodeFixture, ErrorForTruncatedExtendedOpcode) {
|
|
if (!setupGenerator())
|
|
return;
|
|
LineTable < = setupTable();
|
|
LT.addExtendedOpcode(OpcodeLength, Opcode, Operands);
|
|
runTest(0);
|
|
EXPECT_THAT_ERROR(std::move(Recoverable),
|
|
FailedWithMessage(ExpectedErr.str()));
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
TruncatedExtendedOpcodeParams, TruncatedExtendedOpcodeFixture,
|
|
Values(
|
|
// Truncated length:
|
|
std::make_tuple(1, 1, /*ArbitraryOpcode=*/0x7f, ValueAndLengths(), "",
|
|
"unable to decode LEB128 at offset 0x00000030: "
|
|
"malformed uleb128, extends past end"),
|
|
// Truncated opcode:
|
|
std::make_tuple(
|
|
2, 9, /*ArbitraryOpcode=*/0x7f, ValueAndLengths(), "",
|
|
"unexpected end of data at offset 0x31 while reading [0x31, 0x32)"),
|
|
// Truncated operands:
|
|
std::make_tuple(
|
|
3, 9, DW_LNE_set_address,
|
|
ValueAndLengths{{0x1234567890abcdef, LineTable::Quad}},
|
|
"DW_LNE_set_address",
|
|
"unexpected end of data at offset 0x32 while reading [0x32, 0x3a)"),
|
|
std::make_tuple(
|
|
10, 9, DW_LNE_set_address,
|
|
ValueAndLengths{{0x1234567878563412, LineTable::Quad}},
|
|
"DW_LNE_set_address (<parsing error> 12 34 56 78 78 56 34)",
|
|
"unexpected end of data at offset 0x39 while reading [0x32, 0x3a)"),
|
|
std::make_tuple(3, 6, DW_LNE_define_file,
|
|
ValueAndLengths{{'a', LineTable::Byte},
|
|
{'\0', LineTable::Byte},
|
|
{1, LineTable::ULEB},
|
|
{1, LineTable::ULEB},
|
|
{1, LineTable::ULEB}},
|
|
"DW_LNE_define_file",
|
|
"no null terminated string at offset 0x32"),
|
|
std::make_tuple(5, 6, DW_LNE_define_file,
|
|
ValueAndLengths{{'a', LineTable::Byte},
|
|
{'\0', LineTable::Byte},
|
|
{1, LineTable::ULEB},
|
|
{1, LineTable::ULEB},
|
|
{1, LineTable::ULEB}},
|
|
"DW_LNE_define_file (<parsing error> 61 00)",
|
|
"unable to decode LEB128 at offset 0x00000034: "
|
|
"malformed uleb128, extends past end"),
|
|
std::make_tuple(6, 6, DW_LNE_define_file,
|
|
ValueAndLengths{{'a', LineTable::Byte},
|
|
{'\0', LineTable::Byte},
|
|
{1, LineTable::ULEB},
|
|
{1, LineTable::ULEB},
|
|
{1, LineTable::ULEB}},
|
|
"DW_LNE_define_file (<parsing error> 61 00 01)",
|
|
"unable to decode LEB128 at offset 0x00000035: "
|
|
"malformed uleb128, extends past end"),
|
|
std::make_tuple(7, 6, DW_LNE_define_file,
|
|
ValueAndLengths{{'a', LineTable::Byte},
|
|
{'\0', LineTable::Byte},
|
|
{1, LineTable::ULEB},
|
|
{1, LineTable::ULEB},
|
|
{1, LineTable::ULEB}},
|
|
"DW_LNE_define_file (<parsing error> 61 00 01 01)",
|
|
"unable to decode LEB128 at offset 0x00000036: "
|
|
"malformed uleb128, extends past end"),
|
|
std::make_tuple(3, 2, DW_LNE_set_discriminator,
|
|
ValueAndLengths{{1, LineTable::ULEB}},
|
|
"DW_LNE_set_discriminator",
|
|
"unable to decode LEB128 at offset 0x00000032: "
|
|
"malformed uleb128, extends past end"),
|
|
std::make_tuple(
|
|
6, 5, /*Unknown=*/0x7f,
|
|
ValueAndLengths{{0x12343412, LineTable::Long}},
|
|
"Unrecognized extended op 0x7f length 5 (<parsing error> 12 34 34)",
|
|
"unexpected end of data at offset 0x35 while reading [0x32, "
|
|
"0x36)")), );
|
|
|
|
TEST_P(TruncatedStandardOpcodeFixture, ErrorForTruncatedStandardOpcode) {
|
|
if (!setupGenerator())
|
|
return;
|
|
LineTable < = setupTable();
|
|
LT.addStandardOpcode(Opcode, Operands);
|
|
runTest(Opcode);
|
|
EXPECT_THAT_ERROR(std::move(Unrecoverable),
|
|
FailedWithMessage(ExpectedErr.str()));
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
TruncatedStandardOpcodeParams, TruncatedStandardOpcodeFixture,
|
|
Values(
|
|
std::make_tuple(2, DW_LNS_advance_pc,
|
|
ValueAndLengths{{0x100, LineTable::ULEB}},
|
|
"DW_LNS_advance_pc",
|
|
"unable to decode LEB128 at offset 0x00000030: "
|
|
"malformed uleb128, extends past end"),
|
|
std::make_tuple(2, DW_LNS_advance_line,
|
|
ValueAndLengths{{0x200, LineTable::SLEB}},
|
|
"DW_LNS_advance_line",
|
|
"unable to decode LEB128 at offset 0x00000030: "
|
|
"malformed sleb128, extends past end"),
|
|
std::make_tuple(2, DW_LNS_set_file,
|
|
ValueAndLengths{{0x300, LineTable::ULEB}},
|
|
"DW_LNS_set_file",
|
|
"unable to decode LEB128 at offset 0x00000030: "
|
|
"malformed uleb128, extends past end"),
|
|
std::make_tuple(2, DW_LNS_set_column,
|
|
ValueAndLengths{{0x400, LineTable::ULEB}},
|
|
"DW_LNS_set_column",
|
|
"unable to decode LEB128 at offset 0x00000030: "
|
|
"malformed uleb128, extends past end"),
|
|
std::make_tuple(
|
|
2, DW_LNS_fixed_advance_pc,
|
|
ValueAndLengths{{0x500, LineTable::Half}},
|
|
"DW_LNS_fixed_advance_pc",
|
|
"unexpected end of data at offset 0x31 while reading [0x30, 0x32)"),
|
|
std::make_tuple(2, DW_LNS_set_isa,
|
|
ValueAndLengths{{0x600, LineTable::ULEB}},
|
|
"DW_LNS_set_isa",
|
|
"unable to decode LEB128 at offset 0x00000030: "
|
|
"malformed uleb128, extends past end"),
|
|
std::make_tuple(2, 0xd,
|
|
ValueAndLengths{{0x700, LineTable::ULEB},
|
|
{0x800, LineTable::ULEB}},
|
|
"Unrecognized standard opcode",
|
|
"unable to decode LEB128 at offset 0x00000030: "
|
|
"malformed uleb128, extends past end"),
|
|
std::make_tuple(
|
|
4, 0xd,
|
|
ValueAndLengths{{0x900, LineTable::ULEB}, {0xa00, LineTable::ULEB}},
|
|
"Unrecognized standard opcode (operands: 0x0000000000000900)",
|
|
"unable to decode LEB128 at offset 0x00000032: "
|
|
"malformed uleb128, extends past end")), );
|
|
|
|
TEST_F(DebugLineBasicFixture, PrintPathsProperly) {
|
|
if (!setupGenerator(5))
|
|
return;
|
|
|
|
LineTable < = Gen->addLineTable();
|
|
DWARFDebugLine::Prologue P = LT.createBasicPrologue();
|
|
P.IncludeDirectories.push_back(
|
|
DWARFFormValue::createFromPValue(DW_FORM_string, "b dir"));
|
|
P.FileNames.push_back(DWARFDebugLine::FileNameEntry());
|
|
P.FileNames.back().Name =
|
|
DWARFFormValue::createFromPValue(DW_FORM_string, "b file");
|
|
P.FileNames.back().DirIdx = 1;
|
|
P.TotalLength += 14;
|
|
P.PrologueLength += 14;
|
|
LT.setPrologue(P);
|
|
generate();
|
|
|
|
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
|
|
nullptr, RecordRecoverable);
|
|
EXPECT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
|
|
std::string Result;
|
|
// DWARF 5 stores the compilation directory in two places: the Compilation
|
|
// Unit and the directory table entry 0, and implementations are free to use
|
|
// one or the other. This copy serves as the one stored in the CU.
|
|
StringRef CompDir = "a dir";
|
|
EXPECT_FALSE(
|
|
(*ExpectedLineTable)
|
|
->Prologue.getFileNameByIndex(
|
|
1, CompDir, DILineInfoSpecifier::FileLineInfoKind::None, Result));
|
|
EXPECT_TRUE((*ExpectedLineTable)
|
|
->Prologue.getFileNameByIndex(
|
|
1, CompDir,
|
|
DILineInfoSpecifier::FileLineInfoKind::RawValue, Result));
|
|
EXPECT_TRUE((*ExpectedLineTable)
|
|
->Prologue.getFileNameByIndex(
|
|
1, CompDir,
|
|
DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly,
|
|
Result));
|
|
EXPECT_STREQ(Result.c_str(), "b file");
|
|
EXPECT_TRUE((*ExpectedLineTable)
|
|
->Prologue.getFileNameByIndex(
|
|
1, CompDir,
|
|
DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath,
|
|
Result));
|
|
EXPECT_THAT(Result.c_str(), MatchesRegex("b dir.b file"));
|
|
EXPECT_TRUE((*ExpectedLineTable)
|
|
->Prologue.getFileNameByIndex(
|
|
1, CompDir,
|
|
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
|
|
Result));
|
|
EXPECT_THAT(Result.c_str(), MatchesRegex("a dir.b dir.b file"));
|
|
}
|
|
|
|
} // end anonymous namespace
|