1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 02:52:53 +02:00

Adds initial llvm-dwarfdump --verify support with unit tests.

lldb-dwarfdump gets a new "--verify" option that will verify a single file's DWARF debug info and will print out any errors that it finds. It will return an non-zero exit status if verification fails, and a zero exit status if verification succeeds. Adding the --quiet option will suppress any output the STDOUT or STDERR.

The first part of the verify does the following:

- verifies that all CU relative references (DW_FORM_ref1, DW_FORM_ref2, DW_FORM_ref4, DW_FORM_ref8, DW_FORM_ref_udata) have valid CU offsets
- verifies that all DW_FORM_ref_addr references have valid .debug_info offsets
- verifies that all DW_AT_ranges attributes have valid .debug_ranges offsets
- verifies that all DW_AT_stmt_list attributes have valid .debug_line offsets
- verifies that all DW_FORM_strp attributes have valid .debug_str offsets

Unit tests were added for each of the above cases.

Differential Revision: https://reviews.llvm.org/D32707

llvm-svn: 301844
This commit is contained in:
Greg Clayton 2017-05-01 22:07:02 +00:00
parent 3a58d4ac26
commit 5e0a89ef4f
7 changed files with 417 additions and 3 deletions

View File

@ -161,6 +161,10 @@ public:
virtual void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All, virtual void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All,
bool DumpEH = false, bool SummarizeTypes = false) = 0; bool DumpEH = false, bool SummarizeTypes = false) = 0;
virtual bool verify(raw_ostream &OS, DIDumpType DumpType = DIDT_All) {
// No verifier? Just say things went well.
return true;
}
virtual DILineInfo getLineInfoForAddress(uint64_t Address, virtual DILineInfo getLineInfoForAddress(uint64_t Address,
DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0; DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0;
virtual DILineInfoTable getLineInfoForAddressRange(uint64_t Address, virtual DILineInfoTable getLineInfoForAddressRange(uint64_t Address,

View File

@ -106,6 +106,8 @@ public:
void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All, void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All,
bool DumpEH = false, bool SummarizeTypes = false) override; bool DumpEH = false, bool SummarizeTypes = false) override;
bool verify(raw_ostream &OS, DIDumpType DumpType = DIDT_All) override;
typedef DWARFUnitSection<DWARFCompileUnit>::iterator_range cu_iterator_range; typedef DWARFUnitSection<DWARFCompileUnit>::iterator_range cu_iterator_range;
typedef DWARFUnitSection<DWARFTypeUnit>::iterator_range tu_iterator_range; typedef DWARFUnitSection<DWARFTypeUnit>::iterator_range tu_iterator_range;
typedef iterator_range<decltype(TUs)::iterator> tu_section_iterator_range; typedef iterator_range<decltype(TUs)::iterator> tu_section_iterator_range;

View File

@ -59,6 +59,7 @@ public:
DWARFFormValue(dwarf::Form F = dwarf::Form(0)) : Form(F) {} DWARFFormValue(dwarf::Form F = dwarf::Form(0)) : Form(F) {}
dwarf::Form getForm() const { return Form; } dwarf::Form getForm() const { return Form; }
uint64_t getRawUValue() const { return Value.uval; }
void setForm(dwarf::Form F) { Form = F; } void setForm(dwarf::Form F) { Form = F; }
void setUValue(uint64_t V) { Value.uval = V; } void setUValue(uint64_t V) { Value.uval = V; }
void setSValue(int64_t V) { Value.sval = V; } void setSValue(int64_t V) { Value.sval = V; }

View File

@ -284,6 +284,119 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType, bool DumpEH,
getStringSection(), isLittleEndian()); getStringSection(), isLittleEndian());
} }
bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
bool Success = true;
if (DumpType == DIDT_All || DumpType == DIDT_Info) {
OS << "Verifying .debug_info...\n";
for (const auto &CU : compile_units()) {
unsigned NumDies = CU->getNumDIEs();
for (unsigned I = 0; I < NumDies; ++I) {
auto Die = CU->getDIEAtIndex(I);
const auto Tag = Die.getTag();
if (Tag == DW_TAG_null)
continue;
for (auto AttrValue : Die.attributes()) {
const auto Attr = AttrValue.Attr;
const auto Form = AttrValue.Value.getForm();
switch (Attr) {
case DW_AT_ranges:
// Make sure the offset in the DW_AT_ranges attribute is valid.
if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
if (*SectionOffset >= getRangeSection().Data.size()) {
Success = false;
OS << "error: DW_AT_ranges offset is beyond .debug_ranges "
"bounds:\n";
Die.dump(OS, 0);
OS << "\n";
}
} else {
Success = false;
OS << "error: DIE has invalid DW_AT_ranges encoding:\n";
Die.dump(OS, 0);
OS << "\n";
}
break;
case DW_AT_stmt_list:
// Make sure the offset in the DW_AT_stmt_list attribute is valid.
if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
if (*SectionOffset >= getLineSection().Data.size()) {
Success = false;
OS << "error: DW_AT_stmt_list offset is beyond .debug_line "
"bounds: "
<< format("0x%08" PRIx32, *SectionOffset) << "\n";
CU->getUnitDIE().dump(OS, 0);
OS << "\n";
}
} else {
Success = false;
OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n";
Die.dump(OS, 0);
OS << "\n";
}
break;
default:
break;
}
switch (Form) {
case DW_FORM_ref1:
case DW_FORM_ref2:
case DW_FORM_ref4:
case DW_FORM_ref8:
case DW_FORM_ref_udata: {
// Verify all CU relative references are valid CU offsets.
Optional<uint64_t> RefVal = AttrValue.Value.getAsReference();
assert(RefVal);
if (RefVal) {
auto DieCU = Die.getDwarfUnit();
auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset();
auto CUOffset = AttrValue.Value.getRawUValue();
if (CUOffset >= CUSize) {
Success = false;
OS << "error: " << FormEncodingString(Form) << " CU offset "
<< format("0x%08" PRIx32, CUOffset)
<< " is invalid (must be less than CU size of "
<< format("0x%08" PRIx32, CUSize) << "):\n";
Die.dump(OS, 0);
OS << "\n";
}
}
break;
}
case DW_FORM_ref_addr: {
// Verify all absolute DIE references have valid offsets in the
// .debug_info section.
Optional<uint64_t> RefVal = AttrValue.Value.getAsReference();
assert(RefVal);
if (RefVal && *RefVal >= getInfoSection().Data.size()) {
Success = false;
OS << "error: DW_FORM_ref_addr offset beyond .debug_info "
"bounds:\n";
Die.dump(OS, 0);
OS << "\n";
}
break;
}
case DW_FORM_strp: {
auto SecOffset = AttrValue.Value.getAsSectionOffset();
assert(SecOffset); // DW_FORM_strp is a section offset.
if (SecOffset && *SecOffset >= getStringSection().size()) {
Success = false;
OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n";
Die.dump(OS, 0);
OS << "\n";
}
break;
}
default:
break;
}
}
}
}
}
return Success;
}
const DWARFUnitIndex &DWARFContext::getCUIndex() { const DWARFUnitIndex &DWARFContext::getCUIndex() {
if (CUIndex) if (CUIndex)
return *CUIndex; return *CUIndex;

View File

@ -309,8 +309,10 @@ bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const {
} }
// In DWARF3 DW_FORM_data4 and DW_FORM_data8 served also as a section offset. // In DWARF3 DW_FORM_data4 and DW_FORM_data8 served also as a section offset.
// Don't check for DWARF version here, as some producers may still do this // Don't check for DWARF version here, as some producers may still do this
// by mistake. // by mistake. Also accept DW_FORM_strp since this is .debug_str section
return (Form == DW_FORM_data4 || Form == DW_FORM_data8) && // offset.
return (Form == DW_FORM_data4 || Form == DW_FORM_data8 ||
Form == DW_FORM_strp) &&
FC == FC_SectionOffset; FC == FC_SectionOffset;
} }

View File

@ -78,6 +78,11 @@ static cl::opt<bool>
SummarizeTypes("summarize-types", SummarizeTypes("summarize-types",
cl::desc("Abbreviate the description of type unit entries")); cl::desc("Abbreviate the description of type unit entries"));
static cl::opt<bool> Verify("verify", cl::desc("Verify the DWARF debug info"));
static cl::opt<bool> Quiet("quiet",
cl::desc("Use with -verify to not emit to STDOUT."));
static void error(StringRef Filename, std::error_code EC) { static void error(StringRef Filename, std::error_code EC) {
if (!EC) if (!EC)
return; return;
@ -116,6 +121,46 @@ static void DumpInput(StringRef Filename) {
} }
} }
static bool VerifyObjectFile(ObjectFile &Obj, Twine Filename) {
std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj));
// Verify the DWARF and exit with non-zero exit status if verification
// fails.
raw_ostream &stream = Quiet ? nulls() : outs();
stream << "Verifying " << Filename.str() << ":\tfile format "
<< Obj.getFileFormatName() << "\n";
bool Result = DICtx->verify(stream, DumpType);
if (Result)
stream << "No errors.\n";
else
stream << "Errors detected.\n";
return Result;
}
static bool VerifyInput(StringRef Filename) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
MemoryBuffer::getFileOrSTDIN(Filename);
error(Filename, BuffOrErr.getError());
std::unique_ptr<MemoryBuffer> Buff = std::move(BuffOrErr.get());
Expected<std::unique_ptr<Binary>> BinOrErr =
object::createBinary(Buff->getMemBufferRef());
if (!BinOrErr)
error(Filename, errorToErrorCode(BinOrErr.takeError()));
bool Result = true;
if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get()))
Result = VerifyObjectFile(*Obj, Filename);
else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))
for (auto &ObjForArch : Fat->objects()) {
auto MachOOrErr = ObjForArch.getAsObjectFile();
error(Filename, errorToErrorCode(MachOOrErr.takeError()));
if (!VerifyObjectFile(**MachOOrErr, Filename + " (" + ObjForArch.getArchFlagName() + ")"))
Result = false;
}
return Result;
}
/// If the input path is a .dSYM bundle (as created by the dsymutil tool), /// If the input path is a .dSYM bundle (as created by the dsymutil tool),
/// replace it with individual entries for each of the object files inside the /// replace it with individual entries for each of the object files inside the
/// bundle otherwise return the input path. /// bundle otherwise return the input path.
@ -168,7 +213,13 @@ int main(int argc, char **argv) {
Objects.insert(Objects.end(), Objs.begin(), Objs.end()); Objects.insert(Objects.end(), Objs.begin(), Objs.end());
} }
if (Verify) {
// If we encountered errors during verify, exit with a non-zero exit status.
if (!std::all_of(Objects.begin(), Objects.end(), VerifyInput))
exit(1);
} else {
std::for_each(Objects.begin(), Objects.end(), DumpInput); std::for_each(Objects.begin(), Objects.end(), DumpInput);
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -1667,4 +1667,245 @@ TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) {
EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2); EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2);
} }
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
// the compile unit itself.
const char *yamldata = R"(
debug_str:
- ''
- /tmp/main.c
- main
debug_abbrev:
- Code: 0x00000001
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Code: 0x00000002
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Attribute: DW_AT_type
Form: DW_FORM_ref4
debug_info:
- Length:
TotalLength: 22
Version: 4
AbbrOffset: 0
AddrSize: 8
Entries:
- AbbrCode: 0x00000001
Values:
- Value: 0x0000000000000001
- AbbrCode: 0x00000002
Values:
- Value: 0x000000000000000D
- Value: 0x0000000000001234
- AbbrCode: 0x00000000
Values:
)";
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);
}
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddr) {
// Create a single compile unit with a single function that has an invalid
// DW_AT_type with an invalid .debug_info offset in its DW_FORM_ref_addr.
const char *yamldata = R"(
debug_str:
- ''
- /tmp/main.c
- main
debug_abbrev:
- Code: 0x00000001
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Code: 0x00000002
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Attribute: DW_AT_type
Form: DW_FORM_ref_addr
debug_info:
- Length:
TotalLength: 22
Version: 4
AbbrOffset: 0
AddrSize: 8
Entries:
- AbbrCode: 0x00000001
Values:
- Value: 0x0000000000000001
- AbbrCode: 0x00000002
Values:
- Value: 0x000000000000000D
- Value: 0x0000000000001234
- AbbrCode: 0x00000000
Values:
)";
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);
}
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRanges) {
// Create a single compile unit with a DW_AT_ranges whose section offset
// isn't valid.
const char *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_ranges
Form: DW_FORM_sec_offset
debug_info:
- Length:
TotalLength: 16
Version: 4
AbbrOffset: 0
AddrSize: 8
Entries:
- AbbrCode: 0x00000001
Values:
- Value: 0x0000000000000001
- Value: 0x0000000000001000
)";
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);
}
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStmtList) {
// Create a single compile unit with a DW_AT_stmt_list whose section offset
// isn't valid.
const char *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: 0x0000000000001000
)";
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);
}
TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStrp) {
// Create a single compile unit with a single function that has an invalid
// DW_FORM_strp for the DW_AT_name.
const char *yamldata = R"(
debug_str:
- ''
debug_abbrev:
- Code: 0x00000001
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
debug_info:
- Length:
TotalLength: 12
Version: 4
AbbrOffset: 0
AddrSize: 8
Entries:
- AbbrCode: 0x00000001
Values:
- Value: 0x0000000000001234
)";
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);
}
} // end anonymous namespace } // end anonymous namespace