summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/include/llvm/DebugInfo/DIContext.h4
-rw-r--r--llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h2
-rw-r--r--llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h1
-rw-r--r--llvm/lib/DebugInfo/DWARF/DWARFContext.cpp113
-rw-r--r--llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp6
-rw-r--r--llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp53
-rw-r--r--llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp241
7 files changed, 417 insertions, 3 deletions
diff --git a/llvm/include/llvm/DebugInfo/DIContext.h b/llvm/include/llvm/DebugInfo/DIContext.h
index e3386a8dcd2..d51408122fc 100644
--- a/llvm/include/llvm/DebugInfo/DIContext.h
+++ b/llvm/include/llvm/DebugInfo/DIContext.h
@@ -161,6 +161,10 @@ public:
virtual void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All,
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,
DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0;
virtual DILineInfoTable getLineInfoForAddressRange(uint64_t Address,
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h
index d89e2c684cd..3c04c6716ea 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h
@@ -106,6 +106,8 @@ public:
void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All,
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<DWARFTypeUnit>::iterator_range tu_iterator_range;
typedef iterator_range<decltype(TUs)::iterator> tu_section_iterator_range;
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h
index c8d7a0c1ac7..36b27228f5c 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h
@@ -59,6 +59,7 @@ public:
DWARFFormValue(dwarf::Form F = dwarf::Form(0)) : Form(F) {}
dwarf::Form getForm() const { return Form; }
+ uint64_t getRawUValue() const { return Value.uval; }
void setForm(dwarf::Form F) { Form = F; }
void setUValue(uint64_t V) { Value.uval = V; }
void setSValue(int64_t V) { Value.sval = V; }
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
index 7e8d04672c0..b4ecbf805d1 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
@@ -284,6 +284,119 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType, bool DumpEH,
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() {
if (CUIndex)
return *CUIndex;
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp
index 28592e4dfb6..7f827de8924 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp
@@ -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.
// Don't check for DWARF version here, as some producers may still do this
- // by mistake.
- return (Form == DW_FORM_data4 || Form == DW_FORM_data8) &&
+ // by mistake. Also accept DW_FORM_strp since this is .debug_str section
+ // offset.
+ return (Form == DW_FORM_data4 || Form == DW_FORM_data8 ||
+ Form == DW_FORM_strp) &&
FC == FC_SectionOffset;
}
diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
index 84fa0e4d2d9..8ecf1848099 100644
--- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
+++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -78,6 +78,11 @@ static cl::opt<bool>
SummarizeTypes("summarize-types",
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) {
if (!EC)
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),
/// replace it with individual entries for each of the object files inside the
/// bundle otherwise return the input path.
@@ -168,7 +213,13 @@ int main(int argc, char **argv) {
Objects.insert(Objects.end(), Objs.begin(), Objs.end());
}
- std::for_each(Objects.begin(), Objects.end(), DumpInput);
+ 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);
+ }
return EXIT_SUCCESS;
}
diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp
index 2078e3a96a8..3f0e3dab72b 100644
--- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp
+++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp
@@ -1667,4 +1667,245 @@ TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) {
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
OpenPOWER on IntegriCloud