llvm.org GIT mirror llvm / ce55265
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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@301844 91177308-0d34-0410-b5e6-96231b3b80d8 Greg Clayton 3 years ago
7 changed file(s) with 417 addition(s) and 3 deletion(s). Raw diff Collapse all Expand all
160160 virtual void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All,
161161 bool DumpEH = false, bool SummarizeTypes = false) = 0;
162162
163 virtual bool verify(raw_ostream &OS, DIDumpType DumpType = DIDT_All) {
164 // No verifier? Just say things went well.
165 return true;
166 }
163167 virtual DILineInfo getLineInfoForAddress(uint64_t Address,
164168 DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0;
165169 virtual DILineInfoTable getLineInfoForAddressRange(uint64_t Address,
105105 void dump(raw_ostream &OS, DIDumpType DumpType = DIDT_All,
106106 bool DumpEH = false, bool SummarizeTypes = false) override;
107107
108 bool verify(raw_ostream &OS, DIDumpType DumpType = DIDT_All) override;
109
108110 typedef DWARFUnitSection::iterator_range cu_iterator_range;
109111 typedef DWARFUnitSection::iterator_range tu_iterator_range;
110112 typedef iterator_range tu_section_iterator_range;
5858 DWARFFormValue(dwarf::Form F = dwarf::Form(0)) : Form(F) {}
5959
6060 dwarf::Form getForm() const { return Form; }
61 uint64_t getRawUValue() const { return Value.uval; }
6162 void setForm(dwarf::Form F) { Form = F; }
6263 void setUValue(uint64_t V) { Value.uval = V; }
6364 void setSValue(int64_t V) { Value.sval = V; }
283283 getStringSection(), isLittleEndian());
284284 }
285285
286 bool DWARFContext::verify(raw_ostream &OS, DIDumpType DumpType) {
287 bool Success = true;
288 if (DumpType == DIDT_All || DumpType == DIDT_Info) {
289 OS << "Verifying .debug_info...\n";
290 for (const auto &CU : compile_units()) {
291 unsigned NumDies = CU->getNumDIEs();
292 for (unsigned I = 0; I < NumDies; ++I) {
293 auto Die = CU->getDIEAtIndex(I);
294 const auto Tag = Die.getTag();
295 if (Tag == DW_TAG_null)
296 continue;
297 for (auto AttrValue : Die.attributes()) {
298 const auto Attr = AttrValue.Attr;
299 const auto Form = AttrValue.Value.getForm();
300 switch (Attr) {
301 case DW_AT_ranges:
302 // Make sure the offset in the DW_AT_ranges attribute is valid.
303 if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
304 if (*SectionOffset >= getRangeSection().Data.size()) {
305 Success = false;
306 OS << "error: DW_AT_ranges offset is beyond .debug_ranges "
307 "bounds:\n";
308 Die.dump(OS, 0);
309 OS << "\n";
310 }
311 } else {
312 Success = false;
313 OS << "error: DIE has invalid DW_AT_ranges encoding:\n";
314 Die.dump(OS, 0);
315 OS << "\n";
316 }
317 break;
318 case DW_AT_stmt_list:
319 // Make sure the offset in the DW_AT_stmt_list attribute is valid.
320 if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
321 if (*SectionOffset >= getLineSection().Data.size()) {
322 Success = false;
323 OS << "error: DW_AT_stmt_list offset is beyond .debug_line "
324 "bounds: "
325 << format("0x%08" PRIx32, *SectionOffset) << "\n";
326 CU->getUnitDIE().dump(OS, 0);
327 OS << "\n";
328 }
329 } else {
330 Success = false;
331 OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n";
332 Die.dump(OS, 0);
333 OS << "\n";
334 }
335 break;
336
337 default:
338 break;
339 }
340 switch (Form) {
341 case DW_FORM_ref1:
342 case DW_FORM_ref2:
343 case DW_FORM_ref4:
344 case DW_FORM_ref8:
345 case DW_FORM_ref_udata: {
346 // Verify all CU relative references are valid CU offsets.
347 Optional RefVal = AttrValue.Value.getAsReference();
348 assert(RefVal);
349 if (RefVal) {
350 auto DieCU = Die.getDwarfUnit();
351 auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset();
352 auto CUOffset = AttrValue.Value.getRawUValue();
353 if (CUOffset >= CUSize) {
354 Success = false;
355 OS << "error: " << FormEncodingString(Form) << " CU offset "
356 << format("0x%08" PRIx32, CUOffset)
357 << " is invalid (must be less than CU size of "
358 << format("0x%08" PRIx32, CUSize) << "):\n";
359 Die.dump(OS, 0);
360 OS << "\n";
361 }
362 }
363 break;
364 }
365 case DW_FORM_ref_addr: {
366 // Verify all absolute DIE references have valid offsets in the
367 // .debug_info section.
368 Optional RefVal = AttrValue.Value.getAsReference();
369 assert(RefVal);
370 if (RefVal && *RefVal >= getInfoSection().Data.size()) {
371 Success = false;
372 OS << "error: DW_FORM_ref_addr offset beyond .debug_info "
373 "bounds:\n";
374 Die.dump(OS, 0);
375 OS << "\n";
376 }
377 break;
378 }
379 case DW_FORM_strp: {
380 auto SecOffset = AttrValue.Value.getAsSectionOffset();
381 assert(SecOffset); // DW_FORM_strp is a section offset.
382 if (SecOffset && *SecOffset >= getStringSection().size()) {
383 Success = false;
384 OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n";
385 Die.dump(OS, 0);
386 OS << "\n";
387 }
388 break;
389 }
390 default:
391 break;
392 }
393 }
394 }
395 }
396 }
397 return Success;
398 }
286399 const DWARFUnitIndex &DWARFContext::getCUIndex() {
287400 if (CUIndex)
288401 return *CUIndex;
308308 }
309309 // In DWARF3 DW_FORM_data4 and DW_FORM_data8 served also as a section offset.
310310 // Don't check for DWARF version here, as some producers may still do this
311 // by mistake.
312 return (Form == DW_FORM_data4 || Form == DW_FORM_data8) &&
311 // by mistake. Also accept DW_FORM_strp since this is .debug_str section
312 // offset.
313 return (Form == DW_FORM_data4 || Form == DW_FORM_data8 ||
314 Form == DW_FORM_strp) &&
313315 FC == FC_SectionOffset;
314316 }
315317
7777 SummarizeTypes("summarize-types",
7878 cl::desc("Abbreviate the description of type unit entries"));
7979
80 static cl::opt Verify("verify", cl::desc("Verify the DWARF debug info"));
81
82 static cl::opt Quiet("quiet",
83 cl::desc("Use with -verify to not emit to STDOUT."));
84
8085 static void error(StringRef Filename, std::error_code EC) {
8186 if (!EC)
8287 return;
113118 DumpObjectFile(**MachOOrErr,
114119 Filename + " (" + ObjForArch.getArchFlagName() + ")");
115120 }
121 }
122
123 static bool VerifyObjectFile(ObjectFile &Obj, Twine Filename) {
124 std::unique_ptr DICtx(new DWARFContextInMemory(Obj));
125
126 // Verify the DWARF and exit with non-zero exit status if verification
127 // fails.
128 raw_ostream &stream = Quiet ? nulls() : outs();
129 stream << "Verifying " << Filename.str() << ":\tfile format "
130 << Obj.getFileFormatName() << "\n";
131 bool Result = DICtx->verify(stream, DumpType);
132 if (Result)
133 stream << "No errors.\n";
134 else
135 stream << "Errors detected.\n";
136 return Result;
137 }
138
139 static bool VerifyInput(StringRef Filename) {
140 ErrorOr> BuffOrErr =
141 MemoryBuffer::getFileOrSTDIN(Filename);
142 error(Filename, BuffOrErr.getError());
143 std::unique_ptr Buff = std::move(BuffOrErr.get());
144
145 Expected> BinOrErr =
146 object::createBinary(Buff->getMemBufferRef());
147 if (!BinOrErr)
148 error(Filename, errorToErrorCode(BinOrErr.takeError()));
149
150 bool Result = true;
151 if (auto *Obj = dyn_cast(BinOrErr->get()))
152 Result = VerifyObjectFile(*Obj, Filename);
153 else if (auto *Fat = dyn_cast(BinOrErr->get()))
154 for (auto &ObjForArch : Fat->objects()) {
155 auto MachOOrErr = ObjForArch.getAsObjectFile();
156 error(Filename, errorToErrorCode(MachOOrErr.takeError()));
157 if (!VerifyObjectFile(**MachOOrErr, Filename + " (" + ObjForArch.getArchFlagName() + ")"))
158 Result = false;
159 }
160 return Result;
116161 }
117162
118163 /// If the input path is a .dSYM bundle (as created by the dsymutil tool),
167212 Objects.insert(Objects.end(), Objs.begin(), Objs.end());
168213 }
169214
170 std::for_each(Objects.begin(), Objects.end(), DumpInput);
215 if (Verify) {
216 // If we encountered errors during verify, exit with a non-zero exit status.
217 if (!std::all_of(Objects.begin(), Objects.end(), VerifyInput))
218 exit(1);
219 } else {
220 std::for_each(Objects.begin(), Objects.end(), DumpInput);
221 }
171222
172223 return EXIT_SUCCESS;
173224 }
16661666 EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2);
16671667 }
16681668
1669 TEST(DWARFDebugInfo, TestDwarfVerifyInvalidCURef) {
1670 // Create a single compile unit with a single function that has a DW_AT_type
1671 // that is CU relative. The CU offset is not valid becuase it is larger than
1672 // the compile unit itself.
1673
1674 const char *yamldata = R"(
1675 debug_str:
1676 - ''
1677 - /tmp/main.c
1678 - main
1679 debug_abbrev:
1680 - Code: 0x00000001
1681 Tag: DW_TAG_compile_unit
1682 Children: DW_CHILDREN_yes
1683 Attributes:
1684 - Attribute: DW_AT_name
1685 Form: DW_FORM_strp
1686 - Code: 0x00000002
1687 Tag: DW_TAG_subprogram
1688 Children: DW_CHILDREN_no
1689 Attributes:
1690 - Attribute: DW_AT_name
1691 Form: DW_FORM_strp
1692 - Attribute: DW_AT_type
1693 Form: DW_FORM_ref4
1694 debug_info:
1695 - Length:
1696 TotalLength: 22
1697 Version: 4
1698 AbbrOffset: 0
1699 AddrSize: 8
1700 Entries:
1701 - AbbrCode: 0x00000001
1702 Values:
1703 - Value: 0x0000000000000001
1704 - AbbrCode: 0x00000002
1705 Values:
1706 - Value: 0x000000000000000D
1707 - Value: 0x0000000000001234
1708 - AbbrCode: 0x00000000
1709 Values:
1710 )";
1711 auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
1712 ASSERT_TRUE((bool)ErrOrSections);
1713
1714 auto &DebugSections = *ErrOrSections;
1715
1716 DWARFContextInMemory DwarfContext(DebugSections, 8);
1717
1718 std::string str;
1719 raw_string_ostream strm(str);
1720 EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
1721 const char *err = "error: DW_FORM_ref4 CU offset 0x00001234 is invalid "
1722 "(must be less than CU size of 0x0000001a):";
1723 EXPECT_TRUE(strm.str().find(err) != std::string::npos);
1724 }
1725
1726 TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddr) {
1727 // Create a single compile unit with a single function that has an invalid
1728 // DW_AT_type with an invalid .debug_info offset in its DW_FORM_ref_addr.
1729 const char *yamldata = R"(
1730 debug_str:
1731 - ''
1732 - /tmp/main.c
1733 - main
1734 debug_abbrev:
1735 - Code: 0x00000001
1736 Tag: DW_TAG_compile_unit
1737 Children: DW_CHILDREN_yes
1738 Attributes:
1739 - Attribute: DW_AT_name
1740 Form: DW_FORM_strp
1741 - Code: 0x00000002
1742 Tag: DW_TAG_subprogram
1743 Children: DW_CHILDREN_no
1744 Attributes:
1745 - Attribute: DW_AT_name
1746 Form: DW_FORM_strp
1747 - Attribute: DW_AT_type
1748 Form: DW_FORM_ref_addr
1749 debug_info:
1750 - Length:
1751 TotalLength: 22
1752 Version: 4
1753 AbbrOffset: 0
1754 AddrSize: 8
1755 Entries:
1756 - AbbrCode: 0x00000001
1757 Values:
1758 - Value: 0x0000000000000001
1759 - AbbrCode: 0x00000002
1760 Values:
1761 - Value: 0x000000000000000D
1762 - Value: 0x0000000000001234
1763 - AbbrCode: 0x00000000
1764 Values:
1765 )";
1766 auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
1767 ASSERT_TRUE((bool)ErrOrSections);
1768
1769 auto &DebugSections = *ErrOrSections;
1770
1771 DWARFContextInMemory DwarfContext(DebugSections, 8);
1772
1773 std::string str;
1774 raw_string_ostream strm(str);
1775 EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
1776 strm.flush();
1777 const char *err = "error: DW_FORM_ref_addr offset beyond .debug_info bounds:";
1778 EXPECT_TRUE(strm.str().find(err) != std::string::npos);
1779 }
1780
1781 TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRanges) {
1782 // Create a single compile unit with a DW_AT_ranges whose section offset
1783 // isn't valid.
1784 const char *yamldata = R"(
1785 debug_str:
1786 - ''
1787 - /tmp/main.c
1788 debug_abbrev:
1789 - Code: 0x00000001
1790 Tag: DW_TAG_compile_unit
1791 Children: DW_CHILDREN_no
1792 Attributes:
1793 - Attribute: DW_AT_name
1794 Form: DW_FORM_strp
1795 - Attribute: DW_AT_ranges
1796 Form: DW_FORM_sec_offset
1797 debug_info:
1798 - Length:
1799 TotalLength: 16
1800 Version: 4
1801 AbbrOffset: 0
1802 AddrSize: 8
1803 Entries:
1804 - AbbrCode: 0x00000001
1805 Values:
1806 - Value: 0x0000000000000001
1807 - Value: 0x0000000000001000
1808
1809 )";
1810 auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
1811 ASSERT_TRUE((bool)ErrOrSections);
1812
1813 auto &DebugSections = *ErrOrSections;
1814
1815 DWARFContextInMemory DwarfContext(DebugSections, 8);
1816
1817 std::string str;
1818 raw_string_ostream strm(str);
1819 EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
1820 strm.flush();
1821 const char *err = "error: DW_AT_ranges offset is beyond .debug_ranges "
1822 "bounds:";
1823 EXPECT_TRUE(strm.str().find(err) != std::string::npos);
1824 }
1825
1826 TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStmtList) {
1827 // Create a single compile unit with a DW_AT_stmt_list whose section offset
1828 // isn't valid.
1829 const char *yamldata = R"(
1830 debug_str:
1831 - ''
1832 - /tmp/main.c
1833 debug_abbrev:
1834 - Code: 0x00000001
1835 Tag: DW_TAG_compile_unit
1836 Children: DW_CHILDREN_no
1837 Attributes:
1838 - Attribute: DW_AT_name
1839 Form: DW_FORM_strp
1840 - Attribute: DW_AT_stmt_list
1841 Form: DW_FORM_sec_offset
1842 debug_info:
1843 - Length:
1844 TotalLength: 16
1845 Version: 4
1846 AbbrOffset: 0
1847 AddrSize: 8
1848 Entries:
1849 - AbbrCode: 0x00000001
1850 Values:
1851 - Value: 0x0000000000000001
1852 - Value: 0x0000000000001000
1853
1854 )";
1855 auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
1856 ASSERT_TRUE((bool)ErrOrSections);
1857
1858 auto &DebugSections = *ErrOrSections;
1859
1860 DWARFContextInMemory DwarfContext(DebugSections, 8);
1861
1862 std::string str;
1863 raw_string_ostream strm(str);
1864 EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
1865 strm.flush();
1866 const char *err = "error: DW_AT_stmt_list offset is beyond .debug_line "
1867 "bounds: 0x00001000";
1868 EXPECT_TRUE(strm.str().find(err) != std::string::npos);
1869 }
1870
1871 TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStrp) {
1872 // Create a single compile unit with a single function that has an invalid
1873 // DW_FORM_strp for the DW_AT_name.
1874 const char *yamldata = R"(
1875 debug_str:
1876 - ''
1877 debug_abbrev:
1878 - Code: 0x00000001
1879 Tag: DW_TAG_compile_unit
1880 Children: DW_CHILDREN_no
1881 Attributes:
1882 - Attribute: DW_AT_name
1883 Form: DW_FORM_strp
1884 debug_info:
1885 - Length:
1886 TotalLength: 12
1887 Version: 4
1888 AbbrOffset: 0
1889 AddrSize: 8
1890 Entries:
1891 - AbbrCode: 0x00000001
1892 Values:
1893 - Value: 0x0000000000001234
1894 )";
1895 auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata));
1896 ASSERT_TRUE((bool)ErrOrSections);
1897
1898 auto &DebugSections = *ErrOrSections;
1899
1900 DWARFContextInMemory DwarfContext(DebugSections, 8);
1901
1902 std::string str;
1903 raw_string_ostream strm(str);
1904 EXPECT_FALSE(DwarfContext.verify(strm, DIDT_All));
1905 strm.flush();
1906 const char *err = "error: DW_FORM_strp offset beyond .debug_str bounds:";
1907 EXPECT_TRUE(strm.str().find(err) != std::string::npos);
1908 }
1909
16691910 } // end anonymous namespace