Index: llvm/trunk/include/llvm/DebugInfo/DIContext.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/DIContext.h +++ llvm/trunk/include/llvm/DebugInfo/DIContext.h @@ -161,6 +161,10 @@ 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, Index: llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFContext.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFContext.h +++ llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFContext.h @@ -106,6 +106,8 @@ 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::iterator_range cu_iterator_range; typedef DWARFUnitSection::iterator_range tu_iterator_range; typedef iterator_range tu_section_iterator_range; Index: llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFFormValue.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFFormValue.h +++ llvm/trunk/include/llvm/DebugInfo/DWARF/DWARFFormValue.h @@ -59,6 +59,7 @@ 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; } Index: llvm/trunk/lib/DebugInfo/DWARF/DWARFContext.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/DWARF/DWARFContext.cpp +++ llvm/trunk/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -284,6 +284,119 @@ 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 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 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; Index: llvm/trunk/lib/DebugInfo/DWARF/DWARFFormValue.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/DWARF/DWARFFormValue.cpp +++ llvm/trunk/lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -309,8 +309,10 @@ } // 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; } Index: llvm/trunk/tools/llvm-dwarfdump/llvm-dwarfdump.cpp =================================================================== --- llvm/trunk/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ llvm/trunk/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -78,6 +78,11 @@ SummarizeTypes("summarize-types", cl::desc("Abbreviate the description of type unit entries")); +static cl::opt Verify("verify", cl::desc("Verify the DWARF debug info")); + +static cl::opt 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 bool VerifyObjectFile(ObjectFile &Obj, Twine Filename) { + std::unique_ptr 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> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(Filename); + error(Filename, BuffOrErr.getError()); + std::unique_ptr Buff = std::move(BuffOrErr.get()); + + Expected> BinOrErr = + object::createBinary(Buff->getMemBufferRef()); + if (!BinOrErr) + error(Filename, errorToErrorCode(BinOrErr.takeError())); + + bool Result = true; + if (auto *Obj = dyn_cast(BinOrErr->get())) + Result = VerifyObjectFile(*Obj, Filename); + else if (auto *Fat = dyn_cast(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 @@ 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; } Index: llvm/trunk/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp =================================================================== --- llvm/trunk/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ llvm/trunk/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -1667,4 +1667,245 @@ 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