|
|
|
@ -167,20 +167,48 @@ bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData,
|
|
|
|
|
if (!ValidLength || !ValidVersion || !ValidAddrSize || !ValidAbbrevOffset ||
|
|
|
|
|
!ValidType) {
|
|
|
|
|
Success = false;
|
|
|
|
|
error() << format("Units[%d] - start offset: 0x%08" PRIx64 " \n", UnitIndex,
|
|
|
|
|
OffsetStart);
|
|
|
|
|
bool HeaderShown = false;
|
|
|
|
|
auto ShowHeaderOnce = [&]() {
|
|
|
|
|
if (!HeaderShown) {
|
|
|
|
|
error() << format("Units[%d] - start offset: 0x%08" PRIx64 " \n",
|
|
|
|
|
UnitIndex, OffsetStart);
|
|
|
|
|
HeaderShown = true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
if (!ValidLength)
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Unit Header Length: Unit too large for .debug_info provided", [&]() {
|
|
|
|
|
ShowHeaderOnce();
|
|
|
|
|
note() << "The length for this unit is too "
|
|
|
|
|
"large for the .debug_info provided.\n";
|
|
|
|
|
});
|
|
|
|
|
if (!ValidVersion)
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Unit Header Length: 16 bit unit header version is not valid", [&]() {
|
|
|
|
|
ShowHeaderOnce();
|
|
|
|
|
note() << "The 16 bit unit header version is not valid.\n";
|
|
|
|
|
});
|
|
|
|
|
if (!ValidType)
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Unit Header Length: Unit type encoding is not valid", [&]() {
|
|
|
|
|
ShowHeaderOnce();
|
|
|
|
|
note() << "The unit type encoding is not valid.\n";
|
|
|
|
|
});
|
|
|
|
|
if (!ValidAbbrevOffset)
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Unit Header Length: Offset into the .debug_abbrev section is not "
|
|
|
|
|
"valid",
|
|
|
|
|
[&]() {
|
|
|
|
|
ShowHeaderOnce();
|
|
|
|
|
note() << "The offset into the .debug_abbrev section is "
|
|
|
|
|
"not valid.\n";
|
|
|
|
|
});
|
|
|
|
|
if (!ValidAddrSize)
|
|
|
|
|
ErrorCategory.Report("Unit Header Length: Address size is unsupported",
|
|
|
|
|
[&]() {
|
|
|
|
|
ShowHeaderOnce();
|
|
|
|
|
note() << "The address size is unsupported.\n";
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
*Offset = OffsetStart + Length + (isUnitDWARF64 ? 12 : 4);
|
|
|
|
|
return Success;
|
|
|
|
@ -198,12 +226,16 @@ bool DWARFVerifier::verifyName(const DWARFDie &Die) {
|
|
|
|
|
if (OriginalFullName.empty() || OriginalFullName == ReconstructedName)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
error() << "Simplified template DW_AT_name could not be reconstituted:\n"
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Simplified template DW_AT_name could not be reconstituted", [&]() {
|
|
|
|
|
error()
|
|
|
|
|
<< "Simplified template DW_AT_name could not be reconstituted:\n"
|
|
|
|
|
<< formatv(" original: {0}\n"
|
|
|
|
|
" reconstituted: {1}\n",
|
|
|
|
|
OriginalFullName, ReconstructedName);
|
|
|
|
|
dump(Die) << '\n';
|
|
|
|
|
dump(Die.getDwarfUnit()->getUnitDIE()) << '\n';
|
|
|
|
|
});
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -240,22 +272,28 @@ unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit,
|
|
|
|
|
|
|
|
|
|
DWARFDie Die = Unit.getUnitDIE(/* ExtractUnitDIEOnly = */ false);
|
|
|
|
|
if (!Die) {
|
|
|
|
|
ErrorCategory.Report("Compilation unit missing DIE", [&]() {
|
|
|
|
|
error() << "Compilation unit without DIE.\n";
|
|
|
|
|
});
|
|
|
|
|
NumUnitErrors++;
|
|
|
|
|
return NumUnitErrors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!dwarf::isUnitType(Die.getTag())) {
|
|
|
|
|
ErrorCategory.Report("Compilation unit root DIE is not a unit DIE", [&]() {
|
|
|
|
|
error() << "Compilation unit root DIE is not a unit DIE: "
|
|
|
|
|
<< dwarf::TagString(Die.getTag()) << ".\n";
|
|
|
|
|
});
|
|
|
|
|
NumUnitErrors++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t UnitType = Unit.getUnitType();
|
|
|
|
|
if (!DWARFUnit::isMatchingUnitTypeAndTag(UnitType, Die.getTag())) {
|
|
|
|
|
ErrorCategory.Report("Mismatched unit type", [&]() {
|
|
|
|
|
error() << "Compilation unit type (" << dwarf::UnitTypeString(UnitType)
|
|
|
|
|
<< ") and root DIE (" << dwarf::TagString(Die.getTag())
|
|
|
|
|
<< ") do not match.\n";
|
|
|
|
|
});
|
|
|
|
|
NumUnitErrors++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -263,7 +301,9 @@ unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit,
|
|
|
|
|
// 3.1.2 Skeleton Compilation Unit Entries:
|
|
|
|
|
// "A skeleton compilation unit has no children."
|
|
|
|
|
if (Die.getTag() == dwarf::DW_TAG_skeleton_unit && Die.hasChildren()) {
|
|
|
|
|
ErrorCategory.Report("Skeleton CU has children", [&]() {
|
|
|
|
|
error() << "Skeleton compilation unit has children.\n";
|
|
|
|
|
});
|
|
|
|
|
NumUnitErrors++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -280,15 +320,21 @@ unsigned DWARFVerifier::verifyDebugInfoCallSite(const DWARFDie &Die) {
|
|
|
|
|
DWARFDie Curr = Die.getParent();
|
|
|
|
|
for (; Curr.isValid() && !Curr.isSubprogramDIE(); Curr = Die.getParent()) {
|
|
|
|
|
if (Curr.getTag() == DW_TAG_inlined_subroutine) {
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Call site nested entry within inlined subroutine", [&]() {
|
|
|
|
|
error() << "Call site entry nested within inlined subroutine:";
|
|
|
|
|
Curr.dump(OS);
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Curr.isValid()) {
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Call site entry not nested within valid subprogram", [&]() {
|
|
|
|
|
error() << "Call site entry not nested within a valid subprogram:";
|
|
|
|
|
Die.dump(OS);
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -297,9 +343,13 @@ unsigned DWARFVerifier::verifyDebugInfoCallSite(const DWARFDie &Die) {
|
|
|
|
|
DW_AT_call_all_tail_calls, DW_AT_GNU_all_call_sites,
|
|
|
|
|
DW_AT_GNU_all_source_call_sites, DW_AT_GNU_all_tail_call_sites});
|
|
|
|
|
if (!CallAttr) {
|
|
|
|
|
error() << "Subprogram with call site entry has no DW_AT_call attribute:";
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Subprogram with call site entry has no DW_AT_call attribute", [&]() {
|
|
|
|
|
error()
|
|
|
|
|
<< "Subprogram with call site entry has no DW_AT_call attribute:";
|
|
|
|
|
Curr.dump(OS);
|
|
|
|
|
Die.dump(OS, /*indent*/ 1);
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -313,7 +363,9 @@ unsigned DWARFVerifier::verifyAbbrevSection(const DWARFDebugAbbrev *Abbrev) {
|
|
|
|
|
Expected<const DWARFAbbreviationDeclarationSet *> AbbrDeclsOrErr =
|
|
|
|
|
Abbrev->getAbbreviationDeclarationSet(0);
|
|
|
|
|
if (!AbbrDeclsOrErr) {
|
|
|
|
|
error() << toString(AbbrDeclsOrErr.takeError()) << "\n";
|
|
|
|
|
std::string ErrMsg = toString(AbbrDeclsOrErr.takeError());
|
|
|
|
|
ErrorCategory.Report("Abbreviation Declaration error",
|
|
|
|
|
[&]() { error() << ErrMsg << "\n"; });
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -324,9 +376,12 @@ unsigned DWARFVerifier::verifyAbbrevSection(const DWARFDebugAbbrev *Abbrev) {
|
|
|
|
|
for (auto Attribute : AbbrDecl.attributes()) {
|
|
|
|
|
auto Result = AttributeSet.insert(Attribute.Attr);
|
|
|
|
|
if (!Result.second) {
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Abbreviation declartion contains multiple attributes", [&]() {
|
|
|
|
|
error() << "Abbreviation declaration contains multiple "
|
|
|
|
|
<< AttributeString(Attribute.Attr) << " attributes.\n";
|
|
|
|
|
AbbrDecl.dump(OS);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -440,10 +495,15 @@ unsigned DWARFVerifier::verifyIndex(StringRef Name,
|
|
|
|
|
auto &M = *Sections[Col];
|
|
|
|
|
auto I = M.find(SC.getOffset());
|
|
|
|
|
if (I != M.end() && I.start() < (SC.getOffset() + SC.getLength())) {
|
|
|
|
|
StringRef Category = InfoColumnKind == DWARFSectionKind::DW_SECT_INFO
|
|
|
|
|
? "Overlapping CU index entries"
|
|
|
|
|
: "Overlapping TU index entries";
|
|
|
|
|
ErrorCategory.Report(Category, [&]() {
|
|
|
|
|
error() << llvm::formatv(
|
|
|
|
|
"overlapping index entries for entries {0:x16} "
|
|
|
|
|
"and {1:x16} for column {2}\n",
|
|
|
|
|
*I, Sig, toString(Index.getColumnKinds()[Col]));
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
M.insert(SC.getOffset(), SC.getOffset() + SC.getLength() - 1, Sig);
|
|
|
|
@ -532,8 +592,10 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die,
|
|
|
|
|
for (const auto &Range : Ranges) {
|
|
|
|
|
if (!Range.valid()) {
|
|
|
|
|
++NumErrors;
|
|
|
|
|
ErrorCategory.Report("Invalid address range", [&]() {
|
|
|
|
|
error() << "Invalid address range " << Range << "\n";
|
|
|
|
|
DumpDieAfterError = true;
|
|
|
|
|
});
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -545,9 +607,11 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die,
|
|
|
|
|
// address: 0 or -1.
|
|
|
|
|
if (auto PrevRange = RI.insert(Range)) {
|
|
|
|
|
++NumErrors;
|
|
|
|
|
ErrorCategory.Report("DIE has overlapping DW_AT_ranges", [&]() {
|
|
|
|
|
error() << "DIE has overlapping ranges in DW_AT_ranges attribute: "
|
|
|
|
|
<< *PrevRange << " and " << Range << '\n';
|
|
|
|
|
DumpDieAfterError = true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (DumpDieAfterError)
|
|
|
|
@ -558,9 +622,11 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die,
|
|
|
|
|
const auto IntersectingChild = ParentRI.insert(RI);
|
|
|
|
|
if (IntersectingChild != ParentRI.Children.end()) {
|
|
|
|
|
++NumErrors;
|
|
|
|
|
ErrorCategory.Report("DIEs have overlapping address ranges", [&]() {
|
|
|
|
|
error() << "DIEs have overlapping address ranges:";
|
|
|
|
|
dump(Die);
|
|
|
|
|
dump(IntersectingChild->Die) << '\n';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify that ranges are contained within their parent.
|
|
|
|
@ -569,9 +635,13 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die,
|
|
|
|
|
ParentRI.Die.getTag() == DW_TAG_subprogram);
|
|
|
|
|
if (ShouldBeContained && !ParentRI.contains(RI)) {
|
|
|
|
|
++NumErrors;
|
|
|
|
|
error() << "DIE address ranges are not contained in its parent's ranges:";
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"DIE address ranges are not contained by parent ranges", [&]() {
|
|
|
|
|
error()
|
|
|
|
|
<< "DIE address ranges are not contained in its parent's ranges:";
|
|
|
|
|
dump(ParentRI.Die);
|
|
|
|
|
dump(Die, 2) << '\n';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Recursively check children.
|
|
|
|
@ -584,10 +654,12 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die,
|
|
|
|
|
unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
|
|
|
|
|
DWARFAttribute &AttrValue) {
|
|
|
|
|
unsigned NumErrors = 0;
|
|
|
|
|
auto ReportError = [&](const Twine &TitleMsg) {
|
|
|
|
|
auto ReportError = [&](StringRef category, const Twine &TitleMsg) {
|
|
|
|
|
++NumErrors;
|
|
|
|
|
ErrorCategory.Report(category, [&]() {
|
|
|
|
|
error() << TitleMsg << '\n';
|
|
|
|
|
dump(Die) << '\n';
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const DWARFObject &DObj = DCtx.getDWARFObj();
|
|
|
|
@ -604,23 +676,27 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
|
|
|
|
|
if (U->isDWOUnit() && RangeSection.Data.empty())
|
|
|
|
|
break;
|
|
|
|
|
if (*SectionOffset >= RangeSection.Data.size())
|
|
|
|
|
ReportError(
|
|
|
|
|
ReportError("DW_AT_ranges offset out of bounds",
|
|
|
|
|
"DW_AT_ranges offset is beyond " +
|
|
|
|
|
StringRef(DwarfVersion < 5 ? ".debug_ranges" : ".debug_rnglists") +
|
|
|
|
|
StringRef(DwarfVersion < 5 ? ".debug_ranges"
|
|
|
|
|
: ".debug_rnglists") +
|
|
|
|
|
" bounds: " + llvm::formatv("{0:x8}", *SectionOffset));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ReportError("DIE has invalid DW_AT_ranges encoding:");
|
|
|
|
|
ReportError("Invalid DW_AT_ranges encoding",
|
|
|
|
|
"DIE has invalid DW_AT_ranges encoding:");
|
|
|
|
|
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 >= U->getLineSection().Data.size())
|
|
|
|
|
ReportError("DW_AT_stmt_list offset is beyond .debug_line bounds: " +
|
|
|
|
|
ReportError("DW_AT_stmt_list offset out of bounds",
|
|
|
|
|
"DW_AT_stmt_list offset is beyond .debug_line bounds: " +
|
|
|
|
|
llvm::formatv("{0:x8}", *SectionOffset));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ReportError("DIE has invalid DW_AT_stmt_list encoding:");
|
|
|
|
|
ReportError("Invalid DW_AT_stmt_list encoding",
|
|
|
|
|
"DIE has invalid DW_AT_stmt_list encoding:");
|
|
|
|
|
break;
|
|
|
|
|
case DW_AT_location: {
|
|
|
|
|
// FIXME: It might be nice if there's a way to walk location expressions
|
|
|
|
@ -644,14 +720,15 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
|
|
|
|
|
return Op.isError();
|
|
|
|
|
});
|
|
|
|
|
if (Error || !Expression.verify(U))
|
|
|
|
|
ReportError("DIE contains invalid DWARF expression:");
|
|
|
|
|
ReportError("Invalid DWARF expressions",
|
|
|
|
|
"DIE contains invalid DWARF expression:");
|
|
|
|
|
}
|
|
|
|
|
} else if (Error Err = handleErrors(
|
|
|
|
|
Loc.takeError(), [&](std::unique_ptr<ResolverError> E) {
|
|
|
|
|
return U->isDWOUnit() ? Error::success()
|
|
|
|
|
: Error(std::move(E));
|
|
|
|
|
}))
|
|
|
|
|
ReportError(toString(std::move(Err)));
|
|
|
|
|
ReportError("Invalid DW_AT_location", toString(std::move(Err)));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case DW_AT_specification:
|
|
|
|
@ -668,7 +745,8 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
|
|
|
|
|
// This might be reference to a function declaration.
|
|
|
|
|
if (DieTag == DW_TAG_GNU_call_site && RefTag == DW_TAG_subprogram)
|
|
|
|
|
break;
|
|
|
|
|
ReportError("DIE with tag " + TagString(DieTag) + " has " +
|
|
|
|
|
ReportError("Incompatible DW_AT_abstract_origin tag reference",
|
|
|
|
|
"DIE with tag " + TagString(DieTag) + " has " +
|
|
|
|
|
AttributeString(Attr) +
|
|
|
|
|
" that points to DIE with "
|
|
|
|
|
"incompatible tag " +
|
|
|
|
@ -679,7 +757,8 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
|
|
|
|
|
case DW_AT_type: {
|
|
|
|
|
DWARFDie TypeDie = Die.getAttributeValueAsReferencedDie(DW_AT_type);
|
|
|
|
|
if (TypeDie && !isType(TypeDie.getTag())) {
|
|
|
|
|
ReportError("DIE has " + AttributeString(Attr) +
|
|
|
|
|
ReportError("Incompatible DW_AT_type attribute tag",
|
|
|
|
|
"DIE has " + AttributeString(Attr) +
|
|
|
|
|
" with incompatible tag " + TagString(TypeDie.getTag()));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
@ -695,26 +774,32 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
|
|
|
|
|
bool IsZeroIndexed = LT->Prologue.getVersion() >= 5;
|
|
|
|
|
if (std::optional<uint64_t> LastFileIdx =
|
|
|
|
|
LT->getLastValidFileIndex()) {
|
|
|
|
|
ReportError("DIE has " + AttributeString(Attr) +
|
|
|
|
|
ReportError("Invalid file index in DW_AT_decl_file",
|
|
|
|
|
"DIE has " + AttributeString(Attr) +
|
|
|
|
|
" with an invalid file index " +
|
|
|
|
|
llvm::formatv("{0}", *FileIdx) +
|
|
|
|
|
" (valid values are [" + (IsZeroIndexed ? "0-" : "1-") +
|
|
|
|
|
" (valid values are [" +
|
|
|
|
|
(IsZeroIndexed ? "0-" : "1-") +
|
|
|
|
|
llvm::formatv("{0}", *LastFileIdx) + "])");
|
|
|
|
|
} else {
|
|
|
|
|
ReportError("DIE has " + AttributeString(Attr) +
|
|
|
|
|
ReportError("Invalid file index in DW_AT_decl_file",
|
|
|
|
|
"DIE has " + AttributeString(Attr) +
|
|
|
|
|
" with an invalid file index " +
|
|
|
|
|
llvm::formatv("{0}", *FileIdx) +
|
|
|
|
|
" (the file table in the prologue is empty)");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ReportError("DIE has " + AttributeString(Attr) +
|
|
|
|
|
ReportError(
|
|
|
|
|
"File index in DW_AT_decl_file reference CU with no line table",
|
|
|
|
|
"DIE has " + AttributeString(Attr) +
|
|
|
|
|
" that references a file with index " +
|
|
|
|
|
llvm::formatv("{0}", *FileIdx) +
|
|
|
|
|
" and the compile unit has no line table");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ReportError("DIE has " + AttributeString(Attr) +
|
|
|
|
|
ReportError("Invalid encoding in DW_AT_decl_file",
|
|
|
|
|
"DIE has " + AttributeString(Attr) +
|
|
|
|
|
" with invalid encoding");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
@ -722,8 +807,10 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
|
|
|
|
|
case DW_AT_call_line:
|
|
|
|
|
case DW_AT_decl_line: {
|
|
|
|
|
if (!AttrValue.Value.getAsUnsignedConstant()) {
|
|
|
|
|
ReportError("DIE has " + AttributeString(Attr) +
|
|
|
|
|
" with invalid encoding");
|
|
|
|
|
ReportError(
|
|
|
|
|
Attr == DW_AT_call_line ? "Invalid file index in DW_AT_decl_line"
|
|
|
|
|
: "Invalid file index in DW_AT_call_line",
|
|
|
|
|
"DIE has " + AttributeString(Attr) + " with invalid encoding");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -754,12 +841,14 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
|
|
|
|
|
auto CUOffset = AttrValue.Value.getRawUValue();
|
|
|
|
|
if (CUOffset >= CUSize) {
|
|
|
|
|
++NumErrors;
|
|
|
|
|
ErrorCategory.Report("Invalid CU offset", [&]() {
|
|
|
|
|
error() << FormEncodingString(Form) << " CU offset "
|
|
|
|
|
<< format("0x%08" PRIx64, CUOffset)
|
|
|
|
|
<< " is invalid (must be less than CU size of "
|
|
|
|
|
<< format("0x%08" PRIx64, CUSize) << "):\n";
|
|
|
|
|
Die.dump(OS, 0, DumpOpts);
|
|
|
|
|
dump(Die) << '\n';
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// Valid reference, but we will verify it points to an actual
|
|
|
|
|
// DIE later.
|
|
|
|
@ -776,9 +865,11 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
|
|
|
|
|
if (RefVal) {
|
|
|
|
|
if (*RefVal >= DieCU->getInfoSection().Data.size()) {
|
|
|
|
|
++NumErrors;
|
|
|
|
|
ErrorCategory.Report("DW_FORM_ref_addr offset out of bounds", [&]() {
|
|
|
|
|
error() << "DW_FORM_ref_addr offset beyond .debug_info "
|
|
|
|
|
"bounds:\n";
|
|
|
|
|
dump(Die) << '\n';
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// Valid reference, but we will verify it points to an actual
|
|
|
|
|
// DIE later.
|
|
|
|
@ -796,8 +887,11 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
|
|
|
|
|
case DW_FORM_line_strp: {
|
|
|
|
|
if (Error E = AttrValue.Value.getAsCString().takeError()) {
|
|
|
|
|
++NumErrors;
|
|
|
|
|
error() << toString(std::move(E)) << ":\n";
|
|
|
|
|
std::string ErrMsg = toString(std::move(E));
|
|
|
|
|
ErrorCategory.Report("Invalid DW_FORM attribute", [&]() {
|
|
|
|
|
error() << ErrMsg << ":\n";
|
|
|
|
|
dump(Die) << '\n';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -821,11 +915,13 @@ unsigned DWARFVerifier::verifyDebugInfoReferences(
|
|
|
|
|
if (GetDIEForOffset(Pair.first))
|
|
|
|
|
continue;
|
|
|
|
|
++NumErrors;
|
|
|
|
|
ErrorCategory.Report("Invalid DIE reference", [&]() {
|
|
|
|
|
error() << "invalid DIE reference " << format("0x%08" PRIx64, Pair.first)
|
|
|
|
|
<< ". Offset is in between DIEs:\n";
|
|
|
|
|
for (auto Offset : Pair.second)
|
|
|
|
|
dump(GetDIEForOffset(Offset)) << '\n';
|
|
|
|
|
OS << "\n";
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return NumErrors;
|
|
|
|
|
}
|
|
|
|
@ -845,9 +941,11 @@ void DWARFVerifier::verifyDebugLineStmtOffsets() {
|
|
|
|
|
if (LineTableOffset < DCtx.getDWARFObj().getLineSection().Data.size()) {
|
|
|
|
|
if (!LineTable) {
|
|
|
|
|
++NumDebugLineErrors;
|
|
|
|
|
ErrorCategory.Report("Unparsable .debug_line entry", [&]() {
|
|
|
|
|
error() << ".debug_line[" << format("0x%08" PRIx64, LineTableOffset)
|
|
|
|
|
<< "] was not able to be parsed for CU:\n";
|
|
|
|
|
dump(Die) << '\n';
|
|
|
|
|
});
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@ -860,12 +958,14 @@ void DWARFVerifier::verifyDebugLineStmtOffsets() {
|
|
|
|
|
auto Iter = StmtListToDie.find(LineTableOffset);
|
|
|
|
|
if (Iter != StmtListToDie.end()) {
|
|
|
|
|
++NumDebugLineErrors;
|
|
|
|
|
ErrorCategory.Report("Identical DW_AT_stmt_list section offset", [&]() {
|
|
|
|
|
error() << "two compile unit DIEs, "
|
|
|
|
|
<< format("0x%08" PRIx64, Iter->second.getOffset()) << " and "
|
|
|
|
|
<< format("0x%08" PRIx64, Die.getOffset())
|
|
|
|
|
<< ", have the same DW_AT_stmt_list section offset:\n";
|
|
|
|
|
dump(Iter->second);
|
|
|
|
|
dump(Die) << '\n';
|
|
|
|
|
});
|
|
|
|
|
// Already verified this line table before, no need to do it again.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
@ -892,12 +992,16 @@ void DWARFVerifier::verifyDebugLineRows() {
|
|
|
|
|
// Verify directory index.
|
|
|
|
|
if (FileName.DirIdx > MaxDirIndex) {
|
|
|
|
|
++NumDebugLineErrors;
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Invalid index in .debug_line->prologue.file_names->dir_idx",
|
|
|
|
|
[&]() {
|
|
|
|
|
error() << ".debug_line["
|
|
|
|
|
<< format("0x%08" PRIx64,
|
|
|
|
|
*toSectionOffset(Die.find(DW_AT_stmt_list)))
|
|
|
|
|
<< "].prologue.file_names[" << FileIndex
|
|
|
|
|
<< "].dir_idx contains an invalid index: " << FileName.DirIdx
|
|
|
|
|
<< "\n";
|
|
|
|
|
<< "].dir_idx contains an invalid index: "
|
|
|
|
|
<< FileName.DirIdx << "\n";
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check file paths for duplicates.
|
|
|
|
@ -910,7 +1014,7 @@ void DWARFVerifier::verifyDebugLineRows() {
|
|
|
|
|
auto It = FullPathMap.find(FullPath);
|
|
|
|
|
if (It == FullPathMap.end())
|
|
|
|
|
FullPathMap[FullPath] = FileIndex;
|
|
|
|
|
else if (It->second != FileIndex) {
|
|
|
|
|
else if (It->second != FileIndex && DumpOpts.Verbose) {
|
|
|
|
|
warn() << ".debug_line["
|
|
|
|
|
<< format("0x%08" PRIx64,
|
|
|
|
|
*toSectionOffset(Die.find(DW_AT_stmt_list)))
|
|
|
|
@ -928,6 +1032,8 @@ void DWARFVerifier::verifyDebugLineRows() {
|
|
|
|
|
// Verify row address.
|
|
|
|
|
if (Row.Address.Address < PrevAddress) {
|
|
|
|
|
++NumDebugLineErrors;
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"decreasing address between debug_line rows", [&]() {
|
|
|
|
|
error() << ".debug_line["
|
|
|
|
|
<< format("0x%08" PRIx64,
|
|
|
|
|
*toSectionOffset(Die.find(DW_AT_stmt_list)))
|
|
|
|
@ -939,6 +1045,7 @@ void DWARFVerifier::verifyDebugLineRows() {
|
|
|
|
|
LineTable->Rows[RowIndex - 1].dump(OS);
|
|
|
|
|
Row.dump(OS);
|
|
|
|
|
OS << '\n';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the prologue contains no file names and the line table has only one
|
|
|
|
@ -949,6 +1056,7 @@ void DWARFVerifier::verifyDebugLineRows() {
|
|
|
|
|
LineTable->Rows.size() != 1) &&
|
|
|
|
|
!LineTable->hasFileAtIndex(Row.File)) {
|
|
|
|
|
++NumDebugLineErrors;
|
|
|
|
|
ErrorCategory.Report("Invalid file index in debug_line", [&]() {
|
|
|
|
|
error() << ".debug_line["
|
|
|
|
|
<< format("0x%08" PRIx64,
|
|
|
|
|
*toSectionOffset(Die.find(DW_AT_stmt_list)))
|
|
|
|
@ -959,6 +1067,7 @@ void DWARFVerifier::verifyDebugLineRows() {
|
|
|
|
|
DWARFDebugLine::Row::dumpTableHeader(OS, 0);
|
|
|
|
|
Row.dump(OS);
|
|
|
|
|
OS << '\n';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (Row.EndSequence)
|
|
|
|
|
PrevAddress = 0;
|
|
|
|
@ -973,6 +1082,7 @@ DWARFVerifier::DWARFVerifier(raw_ostream &S, DWARFContext &D,
|
|
|
|
|
DIDumpOptions DumpOpts)
|
|
|
|
|
: OS(S), DCtx(D), DumpOpts(std::move(DumpOpts)), IsObjectFile(false),
|
|
|
|
|
IsMachOObject(false) {
|
|
|
|
|
ErrorCategory.ShowDetail(DumpOpts.Verbose || !DumpOpts.ShowAggregateErrors);
|
|
|
|
|
if (const auto *F = DCtx.getDWARFObj().getFile()) {
|
|
|
|
|
IsObjectFile = F->isRelocatableObject();
|
|
|
|
|
IsMachOObject = F->isMachO();
|
|
|
|
@ -999,13 +1109,17 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
|
|
|
|
|
|
|
|
|
|
// Verify that the fixed part of the header is not too short.
|
|
|
|
|
if (!AccelSectionData.isValidOffset(AccelTable.getSizeHdr())) {
|
|
|
|
|
ErrorCategory.Report("Section is too small to fit a section header", [&]() {
|
|
|
|
|
error() << "Section is too small to fit a section header.\n";
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify that the section is not too short.
|
|
|
|
|
if (Error E = AccelTable.extract()) {
|
|
|
|
|
error() << toString(std::move(E)) << '\n';
|
|
|
|
|
std::string Msg = toString(std::move(E));
|
|
|
|
|
ErrorCategory.Report("Section is too small to fit a section header",
|
|
|
|
|
[&]() { error() << Msg << '\n'; });
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1020,18 +1134,24 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
|
|
|
|
|
for (uint32_t BucketIdx = 0; BucketIdx < NumBuckets; ++BucketIdx) {
|
|
|
|
|
uint32_t HashIdx = AccelSectionData.getU32(&BucketsOffset);
|
|
|
|
|
if (HashIdx >= NumHashes && HashIdx != UINT32_MAX) {
|
|
|
|
|
ErrorCategory.Report("Invalid hash index", [&]() {
|
|
|
|
|
error() << format("Bucket[%d] has invalid hash index: %u.\n", BucketIdx,
|
|
|
|
|
HashIdx);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
uint32_t NumAtoms = AccelTable.getAtomsDesc().size();
|
|
|
|
|
if (NumAtoms == 0) {
|
|
|
|
|
ErrorCategory.Report("No atoms", [&]() {
|
|
|
|
|
error() << "No atoms: failed to read HashData.\n";
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (!AccelTable.validateForms()) {
|
|
|
|
|
ErrorCategory.Report("Unsupported form", [&]() {
|
|
|
|
|
error() << "Unsupported form: failed to read HashData.\n";
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1042,9 +1162,11 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
|
|
|
|
|
uint64_t HashDataOffset = AccelSectionData.getU32(&DataOffset);
|
|
|
|
|
if (!AccelSectionData.isValidOffsetForDataOfSize(HashDataOffset,
|
|
|
|
|
sizeof(uint64_t))) {
|
|
|
|
|
ErrorCategory.Report("Invalid HashData offset", [&]() {
|
|
|
|
|
error() << format("Hash[%d] has invalid HashData offset: "
|
|
|
|
|
"0x%08" PRIx64 ".\n",
|
|
|
|
|
HashIdx, HashDataOffset);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1068,21 +1190,25 @@ unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection,
|
|
|
|
|
if (!Name)
|
|
|
|
|
Name = "<NULL>";
|
|
|
|
|
|
|
|
|
|
ErrorCategory.Report("Invalid DIE offset", [&]() {
|
|
|
|
|
error() << format(
|
|
|
|
|
"%s Bucket[%d] Hash[%d] = 0x%08x "
|
|
|
|
|
"Str[%u] = 0x%08" PRIx64 " DIE[%d] = 0x%08" PRIx64 " "
|
|
|
|
|
"is not a valid DIE offset for \"%s\".\n",
|
|
|
|
|
SectionName, BucketIdx, HashIdx, Hash, StringCount, StrpOffset,
|
|
|
|
|
HashDataIdx, Offset, Name);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
++NumErrors;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if ((Tag != dwarf::DW_TAG_null) && (Die.getTag() != Tag)) {
|
|
|
|
|
ErrorCategory.Report("Mismatched Tag in accellerator table", [&]() {
|
|
|
|
|
error() << "Tag " << dwarf::TagString(Tag)
|
|
|
|
|
<< " in accelerator table does not match Tag "
|
|
|
|
|
<< dwarf::TagString(Die.getTag()) << " of DIE[" << HashDataIdx
|
|
|
|
|
<< "].\n";
|
|
|
|
|
<< dwarf::TagString(Die.getTag()) << " of DIE["
|
|
|
|
|
<< HashDataIdx << "].\n";
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1106,8 +1232,10 @@ DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) {
|
|
|
|
|
unsigned NumErrors = 0;
|
|
|
|
|
for (const DWARFDebugNames::NameIndex &NI : AccelTable) {
|
|
|
|
|
if (NI.getCUCount() == 0) {
|
|
|
|
|
ErrorCategory.Report("Name Index doesn't index any CU", [&]() {
|
|
|
|
|
error() << formatv("Name Index @ {0:x} does not index any CU\n",
|
|
|
|
|
NI.getUnitOffset());
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
@ -1116,17 +1244,22 @@ DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) {
|
|
|
|
|
auto Iter = CUMap.find(Offset);
|
|
|
|
|
|
|
|
|
|
if (Iter == CUMap.end()) {
|
|
|
|
|
ErrorCategory.Report("Name Index references non-existing CU", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"Name Index @ {0:x} references a non-existing CU @ {1:x}\n",
|
|
|
|
|
NI.getUnitOffset(), Offset);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Iter->second != NotIndexed) {
|
|
|
|
|
error() << formatv("Name Index @ {0:x} references a CU @ {1:x}, but "
|
|
|
|
|
ErrorCategory.Report("Duplicate Name Index", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"Name Index @ {0:x} references a CU @ {1:x}, but "
|
|
|
|
|
"this CU is already indexed by Name Index @ {2:x}\n",
|
|
|
|
|
NI.getUnitOffset(), Offset, Iter->second);
|
|
|
|
|
});
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Iter->second = NI.getUnitOffset();
|
|
|
|
@ -1167,9 +1300,12 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
|
|
|
|
|
for (uint32_t Bucket = 0, End = NI.getBucketCount(); Bucket < End; ++Bucket) {
|
|
|
|
|
uint32_t Index = NI.getBucketArrayEntry(Bucket);
|
|
|
|
|
if (Index > NI.getNameCount()) {
|
|
|
|
|
ErrorCategory.Report("Name Index Bucket contains invalid value", [&]() {
|
|
|
|
|
error() << formatv("Bucket {0} of Name Index @ {1:x} contains invalid "
|
|
|
|
|
"value {2}. Valid range is [0, {3}].\n",
|
|
|
|
|
Bucket, NI.getUnitOffset(), Index, NI.getNameCount());
|
|
|
|
|
Bucket, NI.getUnitOffset(), Index,
|
|
|
|
|
NI.getNameCount());
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
@ -1202,9 +1338,11 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
|
|
|
|
|
// will not match because we have already verified that the name's hash
|
|
|
|
|
// puts it into the previous bucket.)
|
|
|
|
|
if (B.Index > NextUncovered) {
|
|
|
|
|
ErrorCategory.Report("Name table entries uncovered by hash table", [&]() {
|
|
|
|
|
error() << formatv("Name Index @ {0:x}: Name table entries [{1}, {2}] "
|
|
|
|
|
"are not covered by the hash table.\n",
|
|
|
|
|
NI.getUnitOffset(), NextUncovered, B.Index - 1);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
uint32_t Idx = B.Index;
|
|
|
|
@ -1220,11 +1358,13 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
|
|
|
|
|
// bucket as empty.
|
|
|
|
|
uint32_t FirstHash = NI.getHashArrayEntry(Idx);
|
|
|
|
|
if (FirstHash % NI.getBucketCount() != B.Bucket) {
|
|
|
|
|
ErrorCategory.Report("Name Index point to mismatched hash value", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"Name Index @ {0:x}: Bucket {1} is not empty but points to a "
|
|
|
|
|
"mismatched hash value {2:x} (belonging to bucket {3}).\n",
|
|
|
|
|
NI.getUnitOffset(), B.Bucket, FirstHash,
|
|
|
|
|
FirstHash % NI.getBucketCount());
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1238,11 +1378,14 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
|
|
|
|
|
|
|
|
|
|
const char *Str = NI.getNameTableEntry(Idx).getString();
|
|
|
|
|
if (caseFoldingDjbHash(Str) != Hash) {
|
|
|
|
|
error() << formatv("Name Index @ {0:x}: String ({1}) at index {2} "
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"String hash doesn't match Name Index hash", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"Name Index @ {0:x}: String ({1}) at index {2} "
|
|
|
|
|
"hashes to {3:x}, but "
|
|
|
|
|
"the Name Index hash is {4:x}\n",
|
|
|
|
|
NI.getUnitOffset(), Str, Idx,
|
|
|
|
|
caseFoldingDjbHash(Str), Hash);
|
|
|
|
|
NI.getUnitOffset(), Str, Idx, caseFoldingDjbHash(Str), Hash);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1258,19 +1401,23 @@ unsigned DWARFVerifier::verifyNameIndexAttribute(
|
|
|
|
|
DWARFDebugNames::AttributeEncoding AttrEnc) {
|
|
|
|
|
StringRef FormName = dwarf::FormEncodingString(AttrEnc.Form);
|
|
|
|
|
if (FormName.empty()) {
|
|
|
|
|
ErrorCategory.Report("Unknown NameIndex Abbreviation", [&]() {
|
|
|
|
|
error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an "
|
|
|
|
|
"unknown form: {3}.\n",
|
|
|
|
|
NI.getUnitOffset(), Abbr.Code, AttrEnc.Index,
|
|
|
|
|
AttrEnc.Form);
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AttrEnc.Index == DW_IDX_type_hash) {
|
|
|
|
|
if (AttrEnc.Form != dwarf::DW_FORM_data8) {
|
|
|
|
|
ErrorCategory.Report("Unexpected NameIndex Abbreviation", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_type_hash "
|
|
|
|
|
"uses an unexpected form {2} (should be {3}).\n",
|
|
|
|
|
NI.getUnitOffset(), Abbr.Code, AttrEnc.Form, dwarf::DW_FORM_data8);
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
@ -1280,10 +1427,13 @@ unsigned DWARFVerifier::verifyNameIndexAttribute(
|
|
|
|
|
constexpr static auto AllowedForms = {dwarf::Form::DW_FORM_flag_present,
|
|
|
|
|
dwarf::Form::DW_FORM_ref4};
|
|
|
|
|
if (!is_contained(AllowedForms, AttrEnc.Form)) {
|
|
|
|
|
error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_parent "
|
|
|
|
|
ErrorCategory.Report("Unexpected NameIndex Abbreviation", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_parent "
|
|
|
|
|
"uses an unexpected form {2} (should be "
|
|
|
|
|
"DW_FORM_ref4 or DW_FORM_flag_present).\n",
|
|
|
|
|
NI.getUnitOffset(), Abbr.Code, AttrEnc.Form);
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
@ -1315,10 +1465,12 @@ unsigned DWARFVerifier::verifyNameIndexAttribute(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!DWARFFormValue(AttrEnc.Form).isFormClass(Iter->Class)) {
|
|
|
|
|
ErrorCategory.Report("Unexpected NameIndex Abbreviation", [&]() {
|
|
|
|
|
error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an "
|
|
|
|
|
"unexpected form {3} (expected form class {4}).\n",
|
|
|
|
|
NI.getUnitOffset(), Abbr.Code, AttrEnc.Index,
|
|
|
|
|
AttrEnc.Form, Iter->ClassName);
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
@ -1344,9 +1496,13 @@ DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) {
|
|
|
|
|
SmallSet<unsigned, 5> Attributes;
|
|
|
|
|
for (const auto &AttrEnc : Abbrev.Attributes) {
|
|
|
|
|
if (!Attributes.insert(AttrEnc.Index).second) {
|
|
|
|
|
error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains "
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"NameIndex Abbreviateion contains multiple attributes", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"NameIndex @ {0:x}: Abbreviation {1:x} contains "
|
|
|
|
|
"multiple {2} attributes.\n",
|
|
|
|
|
NI.getUnitOffset(), Abbrev.Code, AttrEnc.Index);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
@ -1354,16 +1510,20 @@ DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NI.getCUCount() > 1 && !Attributes.count(dwarf::DW_IDX_compile_unit)) {
|
|
|
|
|
ErrorCategory.Report("Abbreviation contains no attribute", [&]() {
|
|
|
|
|
error() << formatv("NameIndex @ {0:x}: Indexing multiple compile units "
|
|
|
|
|
"and abbreviation {1:x} has no {2} attribute.\n",
|
|
|
|
|
NI.getUnitOffset(), Abbrev.Code,
|
|
|
|
|
dwarf::DW_IDX_compile_unit);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
if (!Attributes.count(dwarf::DW_IDX_die_offset)) {
|
|
|
|
|
ErrorCategory.Report("Abbreviate in NameIndex missing attribute", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"NameIndex @ {0:x}: Abbreviation {1:x} has no {2} attribute.\n",
|
|
|
|
|
NI.getUnitOffset(), Abbrev.Code, dwarf::DW_IDX_die_offset);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1417,9 +1577,11 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
|
|
|
|
|
|
|
|
|
|
const char *CStr = NTE.getString();
|
|
|
|
|
if (!CStr) {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"Name Index @ {0:x}: Unable to get string associated with name {1}.\n",
|
|
|
|
|
ErrorCategory.Report("Unable to get string associated with name", [&]() {
|
|
|
|
|
error() << formatv("Name Index @ {0:x}: Unable to get string associated "
|
|
|
|
|
"with name {1}.\n",
|
|
|
|
|
NI.getUnitOffset(), NTE.getIndex());
|
|
|
|
|
});
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
StringRef Str(CStr);
|
|
|
|
@ -1433,9 +1595,11 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
|
|
|
|
|
EntryOr = NI.getEntry(&NextEntryID)) {
|
|
|
|
|
uint32_t CUIndex = *EntryOr->getCUIndex();
|
|
|
|
|
if (CUIndex > NI.getCUCount()) {
|
|
|
|
|
ErrorCategory.Report("Name Index entry contains invalid CU index", [&]() {
|
|
|
|
|
error() << formatv("Name Index @ {0:x}: Entry @ {1:x} contains an "
|
|
|
|
|
"invalid CU index ({2}).\n",
|
|
|
|
|
NI.getUnitOffset(), EntryID, CUIndex);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
@ -1443,24 +1607,32 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
|
|
|
|
|
uint64_t DIEOffset = CUOffset + *EntryOr->getDIEUnitOffset();
|
|
|
|
|
DWARFDie DIE = DCtx.getDIEForOffset(DIEOffset);
|
|
|
|
|
if (!DIE) {
|
|
|
|
|
ErrorCategory.Report("NameIndex references nonexistent DIE", [&]() {
|
|
|
|
|
error() << formatv("Name Index @ {0:x}: Entry @ {1:x} references a "
|
|
|
|
|
"non-existing DIE @ {2:x}.\n",
|
|
|
|
|
NI.getUnitOffset(), EntryID, DIEOffset);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (DIE.getDwarfUnit()->getOffset() != CUOffset) {
|
|
|
|
|
error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched CU of "
|
|
|
|
|
ErrorCategory.Report("Name index contains mismatched CU of DIE", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"Name Index @ {0:x}: Entry @ {1:x}: mismatched CU of "
|
|
|
|
|
"DIE @ {2:x}: index - {3:x}; debug_info - {4:x}.\n",
|
|
|
|
|
NI.getUnitOffset(), EntryID, DIEOffset, CUOffset,
|
|
|
|
|
DIE.getDwarfUnit()->getOffset());
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
if (DIE.getTag() != EntryOr->tag()) {
|
|
|
|
|
error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Tag of "
|
|
|
|
|
ErrorCategory.Report("Name Index contains mismatched Tag of DIE", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"Name Index @ {0:x}: Entry @ {1:x}: mismatched Tag of "
|
|
|
|
|
"DIE @ {2:x}: index - {3}; debug_info - {4}.\n",
|
|
|
|
|
NI.getUnitOffset(), EntryID, DIEOffset, EntryOr->tag(),
|
|
|
|
|
DIE.getTag());
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1471,27 +1643,34 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
|
|
|
|
|
DIE.getTag() == DW_TAG_inlined_subroutine;
|
|
|
|
|
auto EntryNames = getNames(DIE, IncludeStrippedTemplateNames);
|
|
|
|
|
if (!is_contained(EntryNames, Str)) {
|
|
|
|
|
ErrorCategory.Report("Name Index contains mismatched name of DIE", [&]() {
|
|
|
|
|
error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Name "
|
|
|
|
|
"of DIE @ {2:x}: index - {3}; debug_info - {4}.\n",
|
|
|
|
|
NI.getUnitOffset(), EntryID, DIEOffset, Str,
|
|
|
|
|
make_range(EntryNames.begin(), EntryNames.end()));
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
handleAllErrors(EntryOr.takeError(),
|
|
|
|
|
handleAllErrors(
|
|
|
|
|
EntryOr.takeError(),
|
|
|
|
|
[&](const DWARFDebugNames::SentinelError &) {
|
|
|
|
|
if (NumEntries > 0)
|
|
|
|
|
return;
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"NameIndex Name is not associated with any entries", [&]() {
|
|
|
|
|
error() << formatv("Name Index @ {0:x}: Name {1} ({2}) is "
|
|
|
|
|
"not associated with any entries.\n",
|
|
|
|
|
NI.getUnitOffset(), NTE.getIndex(), Str);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
},
|
|
|
|
|
[&](const ErrorInfoBase &Info) {
|
|
|
|
|
error()
|
|
|
|
|
<< formatv("Name Index @ {0:x}: Name {1} ({2}): {3}\n",
|
|
|
|
|
ErrorCategory.Report("Uncategorized NameIndex error", [&]() {
|
|
|
|
|
error() << formatv("Name Index @ {0:x}: Name {1} ({2}): {3}\n",
|
|
|
|
|
NI.getUnitOffset(), NTE.getIndex(), Str,
|
|
|
|
|
Info.message());
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
});
|
|
|
|
|
return NumErrors;
|
|
|
|
@ -1619,10 +1798,12 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness(
|
|
|
|
|
if (none_of(NI.equal_range(Name), [&](const DWARFDebugNames::Entry &E) {
|
|
|
|
|
return E.getDIEUnitOffset() == DieUnitOffset;
|
|
|
|
|
})) {
|
|
|
|
|
error() << formatv("Name Index @ {0:x}: Entry for DIE @ {1:x} ({2}) with "
|
|
|
|
|
ErrorCategory.Report("Name Index DIE entry missing name", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"Name Index @ {0:x}: Entry for DIE @ {1:x} ({2}) with "
|
|
|
|
|
"name {3} missing.\n",
|
|
|
|
|
NI.getUnitOffset(), Die.getOffset(), Die.getTag(),
|
|
|
|
|
Name);
|
|
|
|
|
NI.getUnitOffset(), Die.getOffset(), Die.getTag(), Name);
|
|
|
|
|
});
|
|
|
|
|
++NumErrors;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1641,7 +1822,9 @@ unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection,
|
|
|
|
|
// This verifies that we can read individual name indices and their
|
|
|
|
|
// abbreviation tables.
|
|
|
|
|
if (Error E = AccelTable.extract()) {
|
|
|
|
|
error() << toString(std::move(E)) << '\n';
|
|
|
|
|
std::string Msg = toString(std::move(E));
|
|
|
|
|
ErrorCategory.Report("Accelerator Table Error",
|
|
|
|
|
[&]() { error() << Msg << '\n'; });
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1741,13 +1924,17 @@ bool DWARFVerifier::verifyDebugStrOffsets(
|
|
|
|
|
if (!C)
|
|
|
|
|
break;
|
|
|
|
|
if (C.tell() + Length > DA.getData().size()) {
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Section contribution length exceeds available space", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"{0}: contribution {1:X}: length exceeds available space "
|
|
|
|
|
"(contribution "
|
|
|
|
|
"offset ({1:X}) + length field space ({2:X}) + length ({3:X}) == "
|
|
|
|
|
"offset ({1:X}) + length field space ({2:X}) + length "
|
|
|
|
|
"({3:X}) == "
|
|
|
|
|
"{4:X} > section size {5:X})\n",
|
|
|
|
|
SectionName, StartOffset, C.tell() - StartOffset, Length,
|
|
|
|
|
C.tell() + Length, DA.getData().size());
|
|
|
|
|
});
|
|
|
|
|
Success = false;
|
|
|
|
|
// Nothing more to do - no other contributions to try.
|
|
|
|
|
break;
|
|
|
|
@ -1755,8 +1942,10 @@ bool DWARFVerifier::verifyDebugStrOffsets(
|
|
|
|
|
NextUnit = C.tell() + Length;
|
|
|
|
|
uint8_t Version = DA.getU16(C);
|
|
|
|
|
if (C && Version != 5) {
|
|
|
|
|
ErrorCategory.Report("Invalid Section version", [&]() {
|
|
|
|
|
error() << formatv("{0}: contribution {1:X}: invalid version {2}\n",
|
|
|
|
|
SectionName, StartOffset, Version);
|
|
|
|
|
});
|
|
|
|
|
Success = false;
|
|
|
|
|
// Can't parse the rest of this contribution, since we don't know the
|
|
|
|
|
// version, but we can pick up with the next contribution.
|
|
|
|
@ -1768,10 +1957,12 @@ bool DWARFVerifier::verifyDebugStrOffsets(
|
|
|
|
|
DA.setAddressSize(OffsetByteSize);
|
|
|
|
|
uint64_t Remainder = (Length - 4) % OffsetByteSize;
|
|
|
|
|
if (Remainder != 0) {
|
|
|
|
|
ErrorCategory.Report("Invalid section contribution length", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"{0}: contribution {1:X}: invalid length ((length ({2:X}) "
|
|
|
|
|
"- header (0x4)) % offset size {3:X} == {4:X} != 0)\n",
|
|
|
|
|
SectionName, StartOffset, Length, OffsetByteSize, Remainder);
|
|
|
|
|
});
|
|
|
|
|
Success = false;
|
|
|
|
|
}
|
|
|
|
|
for (uint64_t Index = 0; C && C.tell() + OffsetByteSize <= NextUnit; ++Index) {
|
|
|
|
@ -1781,29 +1972,64 @@ bool DWARFVerifier::verifyDebugStrOffsets(
|
|
|
|
|
if (StrOff == 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (StrData.size() <= StrOff) {
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"String offset out of bounds of string section", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"{0}: contribution {1:X}: index {2:X}: invalid string "
|
|
|
|
|
"offset *{3:X} == {4:X}, is beyond the bounds of the string section of length {5:X}\n",
|
|
|
|
|
SectionName, StartOffset, Index, OffOff, StrOff, StrData.size());
|
|
|
|
|
"offset *{3:X} == {4:X}, is beyond the bounds of the string "
|
|
|
|
|
"section of length {5:X}\n",
|
|
|
|
|
SectionName, StartOffset, Index, OffOff, StrOff,
|
|
|
|
|
StrData.size());
|
|
|
|
|
});
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (StrData[StrOff - 1] == '\0')
|
|
|
|
|
continue;
|
|
|
|
|
error() << formatv("{0}: contribution {1:X}: index {2:X}: invalid string "
|
|
|
|
|
ErrorCategory.Report(
|
|
|
|
|
"Section contribution contains invalid string offset", [&]() {
|
|
|
|
|
error() << formatv(
|
|
|
|
|
"{0}: contribution {1:X}: index {2:X}: invalid string "
|
|
|
|
|
"offset *{3:X} == {4:X}, is neither zero nor "
|
|
|
|
|
"immediately following a null character\n",
|
|
|
|
|
SectionName, StartOffset, Index, OffOff, StrOff);
|
|
|
|
|
});
|
|
|
|
|
Success = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Error E = C.takeError()) {
|
|
|
|
|
error() << SectionName << ": " << toString(std::move(E)) << '\n';
|
|
|
|
|
std::string Msg = toString(std::move(E));
|
|
|
|
|
ErrorCategory.Report("String offset error", [&]() {
|
|
|
|
|
error() << SectionName << ": " << Msg << '\n';
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return Success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OutputCategoryAggregator::Report(
|
|
|
|
|
StringRef s, std::function<void(void)> detailCallback) {
|
|
|
|
|
Aggregation[std::string(s)]++;
|
|
|
|
|
if (IncludeDetail)
|
|
|
|
|
detailCallback();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OutputCategoryAggregator::EnumerateResults(
|
|
|
|
|
std::function<void(StringRef, unsigned)> handleCounts) {
|
|
|
|
|
for (auto &&[name, count] : Aggregation) {
|
|
|
|
|
handleCounts(name, count);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DWARFVerifier::summarize() {
|
|
|
|
|
if (ErrorCategory.GetNumCategories() && DumpOpts.ShowAggregateErrors) {
|
|
|
|
|
error() << "Aggregated error counts:\n";
|
|
|
|
|
ErrorCategory.EnumerateResults([&](StringRef s, unsigned count) {
|
|
|
|
|
error() << s << " occurred " << count << " time(s).\n";
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
raw_ostream &DWARFVerifier::error() const { return WithColor::error(OS); }
|
|
|
|
|
|
|
|
|
|
raw_ostream &DWARFVerifier::warn() const { return WithColor::warning(OS); }
|
|
|
|
|