summaryrefslogtreecommitdiff
path: root/lib/DebugInfo/DWARF/DWARFVerifier.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/DebugInfo/DWARF/DWARFVerifier.cpp')
-rw-r--r--lib/DebugInfo/DWARF/DWARFVerifier.cpp195
1 files changed, 162 insertions, 33 deletions
diff --git a/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/lib/DebugInfo/DWARF/DWARFVerifier.cpp
index 0a10e6b78911d..6cf44ffa3796a 100644
--- a/lib/DebugInfo/DWARF/DWARFVerifier.cpp
+++ b/lib/DebugInfo/DWARF/DWARFVerifier.cpp
@@ -24,22 +24,166 @@ using namespace llvm;
using namespace dwarf;
using namespace object;
-void DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
- DWARFAttribute &AttrValue) {
+bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData,
+ uint32_t *Offset, unsigned UnitIndex,
+ uint8_t &UnitType, bool &isUnitDWARF64) {
+ uint32_t AbbrOffset, Length;
+ uint8_t AddrSize = 0;
+ uint16_t Version;
+ bool Success = true;
+
+ bool ValidLength = false;
+ bool ValidVersion = false;
+ bool ValidAddrSize = false;
+ bool ValidType = true;
+ bool ValidAbbrevOffset = true;
+
+ uint32_t OffsetStart = *Offset;
+ Length = DebugInfoData.getU32(Offset);
+ if (Length == UINT32_MAX) {
+ isUnitDWARF64 = true;
+ OS << format(
+ "Unit[%d] is in 64-bit DWARF format; cannot verify from this point.\n",
+ UnitIndex);
+ return false;
+ }
+ Version = DebugInfoData.getU16(Offset);
+
+ if (Version >= 5) {
+ UnitType = DebugInfoData.getU8(Offset);
+ AddrSize = DebugInfoData.getU8(Offset);
+ AbbrOffset = DebugInfoData.getU32(Offset);
+ ValidType = DWARFUnit::isValidUnitType(UnitType);
+ } else {
+ UnitType = 0;
+ AbbrOffset = DebugInfoData.getU32(Offset);
+ AddrSize = DebugInfoData.getU8(Offset);
+ }
+
+ if (!DCtx.getDebugAbbrev()->getAbbreviationDeclarationSet(AbbrOffset))
+ ValidAbbrevOffset = false;
+
+ ValidLength = DebugInfoData.isValidOffset(OffsetStart + Length + 3);
+ ValidVersion = DWARFContext::isSupportedVersion(Version);
+ ValidAddrSize = AddrSize == 4 || AddrSize == 8;
+ if (!ValidLength || !ValidVersion || !ValidAddrSize || !ValidAbbrevOffset ||
+ !ValidType) {
+ Success = false;
+ OS << format("Units[%d] - start offset: 0x%08x \n", UnitIndex, OffsetStart);
+ if (!ValidLength)
+ OS << "\tError: The length for this unit is too "
+ "large for the .debug_info provided.\n";
+ if (!ValidVersion)
+ OS << "\tError: The 16 bit unit header version is not valid.\n";
+ if (!ValidType)
+ OS << "\tError: The unit type encoding is not valid.\n";
+ if (!ValidAbbrevOffset)
+ OS << "\tError: The offset into the .debug_abbrev section is "
+ "not valid.\n";
+ if (!ValidAddrSize)
+ OS << "\tError: The address size is unsupported.\n";
+ }
+ *Offset = OffsetStart + Length + 4;
+ return Success;
+}
+
+bool DWARFVerifier::verifyUnitContents(DWARFUnit Unit) {
+ uint32_t NumUnitErrors = 0;
+ unsigned NumDies = Unit.getNumDIEs();
+ for (unsigned I = 0; I < NumDies; ++I) {
+ auto Die = Unit.getDIEAtIndex(I);
+ if (Die.getTag() == DW_TAG_null)
+ continue;
+ for (auto AttrValue : Die.attributes()) {
+ NumUnitErrors += verifyDebugInfoAttribute(Die, AttrValue);
+ NumUnitErrors += verifyDebugInfoForm(Die, AttrValue);
+ }
+ }
+ return NumUnitErrors == 0;
+}
+
+bool DWARFVerifier::handleDebugInfo() {
+ OS << "Verifying .debug_info Unit Header Chain...\n";
+
+ DWARFDataExtractor DebugInfoData(DCtx.getInfoSection(), DCtx.isLittleEndian(),
+ 0);
+ uint32_t NumDebugInfoErrors = 0;
+ uint32_t OffsetStart = 0, Offset = 0, UnitIdx = 0;
+ uint8_t UnitType = 0;
+ bool isUnitDWARF64 = false;
+ bool isHeaderChainValid = true;
+ bool hasDIE = DebugInfoData.isValidOffset(Offset);
+ while (hasDIE) {
+ OffsetStart = Offset;
+ if (!verifyUnitHeader(DebugInfoData, &Offset, UnitIdx, UnitType,
+ isUnitDWARF64)) {
+ isHeaderChainValid = false;
+ if (isUnitDWARF64)
+ break;
+ } else {
+ std::unique_ptr<DWARFUnit> Unit;
+ switch (UnitType) {
+ case dwarf::DW_UT_type:
+ case dwarf::DW_UT_split_type: {
+ DWARFUnitSection<DWARFTypeUnit> TUSection{};
+ Unit.reset(new DWARFTypeUnit(
+ DCtx, DCtx.getInfoSection(), DCtx.getDebugAbbrev(),
+ &DCtx.getRangeSection(), DCtx.getStringSection(),
+ DCtx.getStringOffsetSection(), &DCtx.getAppleObjCSection(),
+ DCtx.getLineSection(), DCtx.isLittleEndian(), false, TUSection,
+ nullptr));
+ break;
+ }
+ case dwarf::DW_UT_skeleton:
+ case dwarf::DW_UT_split_compile:
+ case dwarf::DW_UT_compile:
+ case dwarf::DW_UT_partial:
+ // UnitType = 0 means that we are
+ // verifying a compile unit in DWARF v4.
+ case 0: {
+ DWARFUnitSection<DWARFCompileUnit> CUSection{};
+ Unit.reset(new DWARFCompileUnit(
+ DCtx, DCtx.getInfoSection(), DCtx.getDebugAbbrev(),
+ &DCtx.getRangeSection(), DCtx.getStringSection(),
+ DCtx.getStringOffsetSection(), &DCtx.getAppleObjCSection(),
+ DCtx.getLineSection(), DCtx.isLittleEndian(), false, CUSection,
+ nullptr));
+ break;
+ }
+ default: { llvm_unreachable("Invalid UnitType."); }
+ }
+ Unit->extract(DebugInfoData, &OffsetStart);
+ if (!verifyUnitContents(*Unit))
+ ++NumDebugInfoErrors;
+ }
+ hasDIE = DebugInfoData.isValidOffset(Offset);
+ ++UnitIdx;
+ }
+ if (UnitIdx == 0 && !hasDIE) {
+ OS << "Warning: .debug_info is empty.\n";
+ isHeaderChainValid = true;
+ }
+ NumDebugInfoErrors += verifyDebugInfoReferences();
+ return (isHeaderChainValid && NumDebugInfoErrors == 0);
+}
+
+unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
+ DWARFAttribute &AttrValue) {
+ unsigned NumErrors = 0;
const auto Attr = AttrValue.Attr;
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 >= DCtx.getRangeSection().Data.size()) {
- ++NumDebugInfoErrors;
+ ++NumErrors;
OS << "error: DW_AT_ranges offset is beyond .debug_ranges "
"bounds:\n";
Die.dump(OS, 0);
OS << "\n";
}
} else {
- ++NumDebugInfoErrors;
+ ++NumErrors;
OS << "error: DIE has invalid DW_AT_ranges encoding:\n";
Die.dump(OS, 0);
OS << "\n";
@@ -49,7 +193,7 @@ void DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
// Make sure the offset in the DW_AT_stmt_list attribute is valid.
if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) {
if (*SectionOffset >= DCtx.getLineSection().Data.size()) {
- ++NumDebugInfoErrors;
+ ++NumErrors;
OS << "error: DW_AT_stmt_list offset is beyond .debug_line "
"bounds: "
<< format("0x%08" PRIx32, *SectionOffset) << "\n";
@@ -57,7 +201,7 @@ void DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
OS << "\n";
}
} else {
- ++NumDebugInfoErrors;
+ ++NumErrors;
OS << "error: DIE has invalid DW_AT_stmt_list encoding:\n";
Die.dump(OS, 0);
OS << "\n";
@@ -67,10 +211,12 @@ void DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
default:
break;
}
+ return NumErrors;
}
-void DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
- DWARFAttribute &AttrValue) {
+unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
+ DWARFAttribute &AttrValue) {
+ unsigned NumErrors = 0;
const auto Form = AttrValue.Value.getForm();
switch (Form) {
case DW_FORM_ref1:
@@ -86,7 +232,7 @@ void DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset();
auto CUOffset = AttrValue.Value.getRawUValue();
if (CUOffset >= CUSize) {
- ++NumDebugInfoErrors;
+ ++NumErrors;
OS << "error: " << FormEncodingString(Form) << " CU offset "
<< format("0x%08" PRIx32, CUOffset)
<< " is invalid (must be less than CU size of "
@@ -108,7 +254,7 @@ void DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
assert(RefVal);
if (RefVal) {
if (*RefVal >= DCtx.getInfoSection().Data.size()) {
- ++NumDebugInfoErrors;
+ ++NumErrors;
OS << "error: DW_FORM_ref_addr offset beyond .debug_info "
"bounds:\n";
Die.dump(OS, 0);
@@ -125,7 +271,7 @@ void DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
auto SecOffset = AttrValue.Value.getAsSectionOffset();
assert(SecOffset); // DW_FORM_strp is a section offset.
if (SecOffset && *SecOffset >= DCtx.getStringSection().size()) {
- ++NumDebugInfoErrors;
+ ++NumErrors;
OS << "error: DW_FORM_strp offset beyond .debug_str bounds:\n";
Die.dump(OS, 0);
OS << "\n";
@@ -135,17 +281,19 @@ void DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
default:
break;
}
+ return NumErrors;
}
-void DWARFVerifier::verifyDebugInfoReferences() {
+unsigned DWARFVerifier::verifyDebugInfoReferences() {
// Take all references and make sure they point to an actual DIE by
// getting the DIE by offset and emitting an error
OS << "Verifying .debug_info references...\n";
+ unsigned NumErrors = 0;
for (auto Pair : ReferenceToDIEOffsets) {
auto Die = DCtx.getDIEForOffset(Pair.first);
if (Die)
continue;
- ++NumDebugInfoErrors;
+ ++NumErrors;
OS << "error: invalid DIE reference " << format("0x%08" PRIx64, Pair.first)
<< ". Offset is in between DIEs:\n";
for (auto Offset : Pair.second) {
@@ -155,26 +303,7 @@ void DWARFVerifier::verifyDebugInfoReferences() {
}
OS << "\n";
}
-}
-
-bool DWARFVerifier::handleDebugInfo() {
- NumDebugInfoErrors = 0;
- OS << "Verifying .debug_info...\n";
- for (const auto &CU : DCtx.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()) {
- verifyDebugInfoAttribute(Die, AttrValue);
- verifyDebugInfoForm(Die, AttrValue);
- }
- }
- }
- verifyDebugInfoReferences();
- return NumDebugInfoErrors == 0;
+ return NumErrors;
}
void DWARFVerifier::verifyDebugLineStmtOffsets() {