mirror of
https://github.com/RPCS3/llvm.git
synced 2025-01-10 06:03:52 +00:00
Add line table verification to lldb-dwarfdump --verify
This patch verifies the .debug_line: - verify all addresses in a line table sequence have ascending addresses - verify that all line table file indexes are valid Unit tests added for both cases. Differential Revision: https://reviews.llvm.org/D32765 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@301984 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
3d8a34a84b
commit
cfa02bf159
@ -104,7 +104,7 @@ public:
|
||||
void postAppend();
|
||||
void reset(bool DefaultIsStmt);
|
||||
void dump(raw_ostream &OS) const;
|
||||
|
||||
static void dumpTableHeader(raw_ostream &OS);
|
||||
static bool orderByAddress(const Row &LHS, const Row &RHS) {
|
||||
return LHS.Address < RHS.Address;
|
||||
}
|
||||
|
@ -445,6 +445,75 @@ public:
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
bool HandleDebugLine() {
|
||||
bool Success = true;
|
||||
OS << "Verifying .debug_line...\n";
|
||||
for (const auto &CU : DCtx.compile_units()) {
|
||||
uint32_t LineTableOffset = 0;
|
||||
auto StmtFormValue = CU->getUnitDIE().find(DW_AT_stmt_list);
|
||||
if (!StmtFormValue) {
|
||||
// No line table for this compile unit.
|
||||
continue;
|
||||
}
|
||||
// Get the attribute value as a section offset. No need to produce an
|
||||
// error here if the encoding isn't correct because we validate this in
|
||||
// the .debug_info verifier.
|
||||
if (auto StmtSectionOffset = toSectionOffset(StmtFormValue)) {
|
||||
LineTableOffset = *StmtSectionOffset;
|
||||
if (LineTableOffset >= DCtx.getLineSection().Data.size()) {
|
||||
// Make sure we don't get a valid line table back if the offset
|
||||
// is wrong.
|
||||
assert(DCtx.getLineTableForUnit(CU.get()) == nullptr);
|
||||
// Skip this line table as it isn't valid. No need to create an error
|
||||
// here because we validate this in the .debug_info verifier.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto LineTable = DCtx.getLineTableForUnit(CU.get());
|
||||
if (!LineTable) {
|
||||
Success = false;
|
||||
OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset)
|
||||
<< "] was not able to be parsed for CU:\n";
|
||||
CU->getUnitDIE().dump(OS, 0);
|
||||
OS << '\n';
|
||||
continue;
|
||||
}
|
||||
uint32_t MaxFileIndex = LineTable->Prologue.FileNames.size();
|
||||
uint64_t PrevAddress = 0;
|
||||
uint32_t RowIndex = 0;
|
||||
for (const auto &Row : LineTable->Rows) {
|
||||
if (Row.Address < PrevAddress) {
|
||||
Success = false;
|
||||
OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset)
|
||||
<< "] row[" << RowIndex
|
||||
<< "] decreases in address from previous row:\n";
|
||||
|
||||
DWARFDebugLine::Row::dumpTableHeader(OS);
|
||||
if (RowIndex > 0)
|
||||
LineTable->Rows[RowIndex - 1].dump(OS);
|
||||
Row.dump(OS);
|
||||
OS << '\n';
|
||||
}
|
||||
|
||||
if (Row.File > MaxFileIndex) {
|
||||
Success = false;
|
||||
OS << "error: .debug_line[" << format("0x%08" PRIx32, LineTableOffset)
|
||||
<< "][" << RowIndex << "] has invalid file index " << Row.File
|
||||
<< " (valid values are [1," << MaxFileIndex << "]):\n";
|
||||
DWARFDebugLine::Row::dumpTableHeader(OS);
|
||||
Row.dump(OS);
|
||||
OS << '\n';
|
||||
}
|
||||
if (Row.EndSequence)
|
||||
PrevAddress = 0;
|
||||
else
|
||||
PrevAddress = Row.Address;
|
||||
++RowIndex;
|
||||
}
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
@ -456,6 +525,10 @@ bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
|
||||
if (!verifier.HandleDebugInfo())
|
||||
Success = false;
|
||||
}
|
||||
if (DumpType == DIDT_All || DumpType == DIDT_Line) {
|
||||
if (!verifier.HandleDebugLine())
|
||||
Success = false;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
const DWARFUnitIndex &DWARFContext::getCUIndex() {
|
||||
|
@ -287,6 +287,12 @@ void DWARFDebugLine::Row::reset(bool DefaultIsStmt) {
|
||||
EpilogueBegin = false;
|
||||
}
|
||||
|
||||
void DWARFDebugLine::Row::dumpTableHeader(raw_ostream &OS) {
|
||||
OS << "Address Line Column File ISA Discriminator Flags\n"
|
||||
<< "------------------ ------ ------ ------ --- ------------- "
|
||||
"-------------\n";
|
||||
}
|
||||
|
||||
void DWARFDebugLine::Row::dump(raw_ostream &OS) const {
|
||||
OS << format("0x%16.16" PRIx64 " %6u %6u", Address, Line, Column)
|
||||
<< format(" %6u %3u %13u ", File, Isa, Discriminator)
|
||||
@ -313,9 +319,7 @@ void DWARFDebugLine::LineTable::dump(raw_ostream &OS) const {
|
||||
OS << '\n';
|
||||
|
||||
if (!Rows.empty()) {
|
||||
OS << "Address Line Column File ISA Discriminator Flags\n"
|
||||
<< "------------------ ------ ------ ------ --- ------------- "
|
||||
"-------------\n";
|
||||
Row::dumpTableHeader(OS);
|
||||
for (const Row &R : Rows) {
|
||||
R.dump(OS);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "DwarfGenerator.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/Config/llvm-config.h"
|
||||
@ -18,8 +19,8 @@
|
||||
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "llvm/ObjectYAML/DWARFYAML.h"
|
||||
#include "llvm/ObjectYAML/DWARFEmitter.h"
|
||||
#include "llvm/ObjectYAML/DWARFYAML.h"
|
||||
#include "llvm/Support/Dwarf.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
@ -1191,10 +1192,7 @@ TEST(DWARFDebugInfo, TestEmptyChildren) {
|
||||
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
DWARFContextInMemory DwarfContext(*ErrOrSections, 8);
|
||||
|
||||
// Verify the number of compile units is correct.
|
||||
uint32_t NumCUs = DwarfContext.getNumCompileUnits();
|
||||
@ -1667,6 +1665,13 @@ TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) {
|
||||
EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2);
|
||||
}
|
||||
|
||||
void VerifyError(DWARFContext &DwarfContext, StringRef Error) {
|
||||
SmallString<1024> Str;
|
||||
raw_svector_ostream Strm(Str);
|
||||
EXPECT_FALSE(DwarfContext.verify(Strm, DIDT_All));
|
||||
EXPECT_TRUE(Str.str().contains(Error));
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidCURef) {
|
||||
// Create a single compile unit with a single function that has a DW_AT_type
|
||||
// that is CU relative. The CU offset is not valid becuase it is larger than
|
||||
@ -1711,17 +1716,10 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidCURef) {
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
|
||||
std::string str;
|
||||
raw_string_ostream strm(str);
|
||||
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
|
||||
const char *err = "error: DW_FORM_ref4 CU offset 0x00001234 is invalid "
|
||||
"(must be less than CU size of 0x0000001a):";
|
||||
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
|
||||
DWARFContextInMemory DwarfContext(*ErrOrSections, 8);
|
||||
VerifyError(DwarfContext, "error: DW_FORM_ref4 CU offset 0x00001234 is "
|
||||
"invalid (must be less than CU size of "
|
||||
"0x0000001a):");
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddr) {
|
||||
@ -1766,17 +1764,9 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddr) {
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
|
||||
std::string str;
|
||||
raw_string_ostream strm(str);
|
||||
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
|
||||
strm.flush();
|
||||
const char *err = "error: DW_FORM_ref_addr offset beyond .debug_info bounds:";
|
||||
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
|
||||
DWARFContextInMemory DwarfContext(*ErrOrSections, 8);
|
||||
VerifyError(DwarfContext,
|
||||
"error: DW_FORM_ref_addr offset beyond .debug_info bounds:");
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRanges) {
|
||||
@ -1810,18 +1800,9 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRanges) {
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
|
||||
std::string str;
|
||||
raw_string_ostream strm(str);
|
||||
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
|
||||
strm.flush();
|
||||
const char *err = "error: DW_AT_ranges offset is beyond .debug_ranges "
|
||||
"bounds:";
|
||||
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
|
||||
DWARFContextInMemory DwarfContext(*ErrOrSections, 8);
|
||||
VerifyError(DwarfContext,
|
||||
"error: DW_AT_ranges offset is beyond .debug_ranges bounds:");
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStmtList) {
|
||||
@ -1855,18 +1836,10 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStmtList) {
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
|
||||
std::string str;
|
||||
raw_string_ostream strm(str);
|
||||
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
|
||||
strm.flush();
|
||||
const char *err = "error: DW_AT_stmt_list offset is beyond .debug_line "
|
||||
"bounds: 0x00001000";
|
||||
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
|
||||
DWARFContextInMemory DwarfContext(*ErrOrSections, 8);
|
||||
VerifyError(
|
||||
DwarfContext,
|
||||
"error: DW_AT_stmt_list offset is beyond .debug_line bounds: 0x00001000");
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStrp) {
|
||||
@ -1895,17 +1868,9 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStrp) {
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
|
||||
std::string str;
|
||||
raw_string_ostream strm(str);
|
||||
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
|
||||
strm.flush();
|
||||
const char *err = "error: DW_FORM_strp offset beyond .debug_str bounds:";
|
||||
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
|
||||
DWARFContextInMemory DwarfContext(*ErrOrSections, 8);
|
||||
VerifyError(DwarfContext,
|
||||
"error: DW_FORM_strp offset beyond .debug_str bounds:");
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddrBetween) {
|
||||
@ -1950,18 +1915,150 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddrBetween) {
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
|
||||
auto &DebugSections = *ErrOrSections;
|
||||
|
||||
DWARFContextInMemory DwarfContext(DebugSections, 8);
|
||||
|
||||
std::string str;
|
||||
raw_string_ostream strm(str);
|
||||
EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
|
||||
strm.flush();
|
||||
const char *err = "error: invalid DIE reference 0x00000011. Offset is in "
|
||||
"between DIEs:";
|
||||
EXPECT_TRUE(strm.str().find(err) != std::string::npos);
|
||||
DWARFContextInMemory DwarfContext(*ErrOrSections, 8);
|
||||
VerifyError(
|
||||
DwarfContext,
|
||||
"error: invalid DIE reference 0x00000011. Offset is in between DIEs:");
|
||||
}
|
||||
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidLineSequence) {
|
||||
// Create a single compile unit whose line table has a sequence in it where
|
||||
// the address decreases.
|
||||
StringRef yamldata = R"(
|
||||
debug_str:
|
||||
- ''
|
||||
- /tmp/main.c
|
||||
debug_abbrev:
|
||||
- Code: 0x00000001
|
||||
Tag: DW_TAG_compile_unit
|
||||
Children: DW_CHILDREN_no
|
||||
Attributes:
|
||||
- Attribute: DW_AT_name
|
||||
Form: DW_FORM_strp
|
||||
- Attribute: DW_AT_stmt_list
|
||||
Form: DW_FORM_sec_offset
|
||||
debug_info:
|
||||
- Length:
|
||||
TotalLength: 16
|
||||
Version: 4
|
||||
AbbrOffset: 0
|
||||
AddrSize: 8
|
||||
Entries:
|
||||
- AbbrCode: 0x00000001
|
||||
Values:
|
||||
- Value: 0x0000000000000001
|
||||
- Value: 0x0000000000000000
|
||||
debug_line:
|
||||
- Length:
|
||||
TotalLength: 68
|
||||
Version: 2
|
||||
PrologueLength: 34
|
||||
MinInstLength: 1
|
||||
DefaultIsStmt: 1
|
||||
LineBase: 251
|
||||
LineRange: 14
|
||||
OpcodeBase: 13
|
||||
StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
|
||||
IncludeDirs:
|
||||
- /tmp
|
||||
Files:
|
||||
- Name: main.c
|
||||
DirIdx: 1
|
||||
ModTime: 0
|
||||
Length: 0
|
||||
Opcodes:
|
||||
- Opcode: DW_LNS_extended_op
|
||||
ExtLen: 9
|
||||
SubOpcode: DW_LNE_set_address
|
||||
Data: 4112
|
||||
- Opcode: DW_LNS_advance_line
|
||||
SData: 9
|
||||
Data: 4112
|
||||
- Opcode: DW_LNS_copy
|
||||
Data: 4112
|
||||
- Opcode: DW_LNS_advance_pc
|
||||
Data: 18446744073709551600
|
||||
- Opcode: DW_LNS_extended_op
|
||||
ExtLen: 1
|
||||
SubOpcode: DW_LNE_end_sequence
|
||||
Data: 18446744073709551600
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata);
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
DWARFContextInMemory DwarfContext(*ErrOrSections, 8);
|
||||
VerifyError(DwarfContext, "error: .debug_line[0x00000000] row[1] decreases "
|
||||
"in address from previous row:");
|
||||
}
|
||||
|
||||
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidLineFileIndex) {
|
||||
// Create a single compile unit whose line table has a line table row with
|
||||
// an invalid file index.
|
||||
StringRef yamldata = R"(
|
||||
debug_str:
|
||||
- ''
|
||||
- /tmp/main.c
|
||||
debug_abbrev:
|
||||
- Code: 0x00000001
|
||||
Tag: DW_TAG_compile_unit
|
||||
Children: DW_CHILDREN_no
|
||||
Attributes:
|
||||
- Attribute: DW_AT_name
|
||||
Form: DW_FORM_strp
|
||||
- Attribute: DW_AT_stmt_list
|
||||
Form: DW_FORM_sec_offset
|
||||
debug_info:
|
||||
- Length:
|
||||
TotalLength: 16
|
||||
Version: 4
|
||||
AbbrOffset: 0
|
||||
AddrSize: 8
|
||||
Entries:
|
||||
- AbbrCode: 0x00000001
|
||||
Values:
|
||||
- Value: 0x0000000000000001
|
||||
- Value: 0x0000000000000000
|
||||
debug_line:
|
||||
- Length:
|
||||
TotalLength: 61
|
||||
Version: 2
|
||||
PrologueLength: 34
|
||||
MinInstLength: 1
|
||||
DefaultIsStmt: 1
|
||||
LineBase: 251
|
||||
LineRange: 14
|
||||
OpcodeBase: 13
|
||||
StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
|
||||
IncludeDirs:
|
||||
- /tmp
|
||||
Files:
|
||||
- Name: main.c
|
||||
DirIdx: 1
|
||||
ModTime: 0
|
||||
Length: 0
|
||||
Opcodes:
|
||||
- Opcode: DW_LNS_extended_op
|
||||
ExtLen: 9
|
||||
SubOpcode: DW_LNE_set_address
|
||||
Data: 4096
|
||||
- Opcode: DW_LNS_advance_line
|
||||
SData: 9
|
||||
Data: 4096
|
||||
- Opcode: DW_LNS_copy
|
||||
Data: 4096
|
||||
- Opcode: DW_LNS_advance_pc
|
||||
Data: 16
|
||||
- Opcode: DW_LNS_set_file
|
||||
Data: 5
|
||||
- Opcode: DW_LNS_extended_op
|
||||
ExtLen: 1
|
||||
SubOpcode: DW_LNE_end_sequence
|
||||
Data: 5
|
||||
)";
|
||||
auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata);
|
||||
ASSERT_TRUE((bool)ErrOrSections);
|
||||
DWARFContextInMemory DwarfContext(*ErrOrSections, 8);
|
||||
VerifyError(DwarfContext, "error: .debug_line[0x00000000][1] has invalid "
|
||||
"file index 5 (valid values are [1,1]):");
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
Loading…
Reference in New Issue
Block a user