Index: include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h +++ include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h @@ -425,6 +425,10 @@ uint32_t getNameCount() const { return Hdr.NameCount; } + const DenseSet &getAbbrevs() const { + return Abbrevs; + } + llvm::Error extract(); uint32_t getUnitOffset() const { return Base; } uint32_t getNextUnitOffset() const { return Base + 4 + Hdr.UnitLength; } Index: include/llvm/DebugInfo/DWARF/DWARFVerifier.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFVerifier.h +++ include/llvm/DebugInfo/DWARF/DWARFVerifier.h @@ -236,12 +236,17 @@ unsigned verifyDebugNamesCULists(const DWARFDebugNames &AccelTable); unsigned verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI, const DataExtractor &StrData); + unsigned verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI); + unsigned verifyNameIndexAttribute(const DWARFDebugNames::NameIndex &NI, + const DWARFDebugNames::Abbrev &Abbr, + DWARFDebugNames::AttributeEncoding AttrEnc); /// Verify that the DWARF v5 accelerator table is valid. /// /// This function currently checks that: - /// - Headers and abbreviation tables of individual Name Indices fit into the - /// section and can be parsed. + /// - Headers individual Name Indices fit into the section and can be parsed. + /// - Abbreviation tables can be parsed and contain valid index attributes + /// with correct form encodings. /// - The CU lists reference existing compile units. /// - The buckets have a valid index, or they are empty. /// - All names are reachable via the hash table (they have the correct hash, Index: lib/DebugInfo/DWARF/DWARFVerifier.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFVerifier.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" @@ -939,6 +940,89 @@ return NumErrors; } +unsigned DWARFVerifier::verifyNameIndexAttribute( + const DWARFDebugNames::NameIndex &NI, const DWARFDebugNames::Abbrev &Abbr, + DWARFDebugNames::AttributeEncoding AttrEnc) { + StringRef FormName = dwarf::FormEncodingString(AttrEnc.Form); + if (FormName.empty()) { + 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; + } + + // A list of known index attributes and their expected form classes. + // DW_IDX_type_hash is handled specially as it has a specific form we should + // expect. + struct FormClassTable { + dwarf::Index Index; + DWARFFormValue::FormClass Class; + StringLiteral ClassName; + }; + static constexpr FormClassTable Table[] = { + {dwarf::DW_IDX_compile_unit, DWARFFormValue::FC_Constant, {"constant"}}, + {dwarf::DW_IDX_type_unit, DWARFFormValue::FC_Constant, {"constant"}}, + {dwarf::DW_IDX_die_offset, DWARFFormValue::FC_Reference, {"reference"}}, + {dwarf::DW_IDX_parent, DWARFFormValue::FC_Constant, {"constant"}}, + }; + + if (AttrEnc.Index == DW_IDX_type_hash) { + if (AttrEnc.Form != dwarf::DW_FORM_data8) { + 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; + } + } + + ArrayRef TableRef(Table); + auto Iter = find_if(TableRef, [AttrEnc](const FormClassTable &T) { + return T.Index == AttrEnc.Index; + }); + if (Iter == TableRef.end()) { + warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains an " + "unknown index attribute: {2}.\n", + NI.getUnitOffset(), Abbr.Code, AttrEnc.Index); + return 0; + } + + if (!DWARFFormValue(AttrEnc.Form).isFormClass(Iter->Class)) { + 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; +} + +unsigned +DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) { + unsigned NumErrors = 0; + for (const auto &Abbrev : NI.getAbbrevs()) { + StringRef TagName = dwarf::TagString(Abbrev.Tag); + if (TagName.empty()) { + warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} references an " + "unknown tag: {2}.\n", + NI.getUnitOffset(), Abbrev.Code, Abbrev.Tag); + } + SmallSet Attributes; + for (const auto &AttrEnc : Abbrev.Attributes) { + if (!Attributes.insert(AttrEnc.Index).second) { + error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains " + "multiple {2} attributes.\n", + NI.getUnitOffset(), Abbrev.Code, AttrEnc.Index); + ++NumErrors; + continue; + } + NumErrors += verifyNameIndexAttribute(NI, Abbrev, AttrEnc); + } + } + return NumErrors; +} + unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection, const DataExtractor &StrData) { unsigned NumErrors = 0; @@ -958,6 +1042,8 @@ NumErrors += verifyDebugNamesCULists(AccelTable); for (const auto &NI : AccelTable) NumErrors += verifyNameIndexBuckets(NI, StrData); + for (const auto &NI : AccelTable) + NumErrors += verifyNameIndexAbbrevs(NI); return NumErrors; } Index: test/tools/llvm-dwarfdump/X86/debug-names-verify-abbrev-forms.s =================================================================== --- /dev/null +++ test/tools/llvm-dwarfdump/X86/debug-names-verify-abbrev-forms.s @@ -0,0 +1,92 @@ +# RUN: llvm-mc -triple x86_64-pc-linux %s -filetype=obj | \ +# RUN: not llvm-dwarfdump -verify - | FileCheck %s + +# CHECK: error: NameIndex @ 0x0: Abbreviation 0x2: DW_IDX_compile_unit uses an unexpected form DW_FORM_ref1 (expected form class constant). +# CHECK: error: NameIndex @ 0x0: Abbreviation 0x2: DW_IDX_type_unit uses an unexpected form DW_FORM_ref1 (expected form class constant). +# CHECK: error: NameIndex @ 0x0: Abbreviation 0x2: DW_IDX_type_hash uses an unexpected form DW_FORM_data4 (should be DW_FORM_data8). +# CHECK: warning: NameIndex @ 0x0: Abbreviation 0x2 contains an unknown index attribute: DW_IDX_unknown_2020. +# CHECK: error: NameIndex @ 0x0: Abbreviation 0x4 contains multiple DW_IDX_die_offset attributes. +# CHECK: NameIndex @ 0x0: Abbreviation 0x1: DW_IDX_die_offset uses an unknown form: DW_FORM_unknown_1fff. +# CHECK: warning: NameIndex @ 0x0: Abbreviation 0x3 references an unknown tag: DW_TAG_unknown_8080. + + .section .debug_str,"MS",@progbits,1 +.Lstring_producer: + .asciz "Hand-written dwarf" + + .section .debug_abbrev,"",@progbits +.Lsection_abbrev: + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 37 # DW_AT_producer + .byte 14 # DW_FORM_strp + .byte 19 # DW_AT_language + .byte 5 # DW_FORM_data2 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Lcu_end0-.Lcu_start0 # Length of Unit +.Lcu_start0: + .short 4 # DWARF version number + .long .Lsection_abbrev # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] DW_TAG_compile_unit + .long .Lstring_producer # DW_AT_producer + .short 12 # DW_AT_language + .byte 0 # End Of Children Mark +.Lcu_end0: + + .section .debug_names,"",@progbits + .long .Lnames_end0-.Lnames_start0 # Header: contribution length +.Lnames_start0: + .short 5 # Header: version + .short 0 # Header: padding + .long 1 # Header: compilation unit count + .long 0 # Header: local type unit count + .long 0 # Header: foreign type unit count + .long 0 # Header: bucket count + .long 0 # Header: name count + .long .Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size + .long 0 # Header: augmentation length + .long .Lcu_begin0 # Compilation unit 0 +.Lnames_abbrev_start0: + .byte 1 # Abbrev code + .byte 46 # DW_TAG_subprogram + .byte 3 # DW_IDX_die_offset + .uleb128 0x1fff # DW_FORM_unknown_1fff + .byte 0 # End of abbrev + .byte 0 # End of abbrev + .byte 2 # Abbrev code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_IDX_compile_unit + .byte 17 # DW_FORM_ref1 + .byte 2 # DW_IDX_type_unit + .byte 17 # DW_FORM_ref1 + .byte 2 # DW_IDX_die_offset + .byte 5 # DW_FORM_data2 + .byte 5 # DW_IDX_type_hash + .byte 6 # DW_FORM_data4 + .uleb128 0x2020 # DW_IDX_unknown_2020 + .byte 6 # DW_FORM_data4 + .byte 0 # End of abbrev + .byte 0 # End of abbrev + .byte 3 # Abbrev code + .uleb128 0x8080 # DW_TAG_unknown_8080 + .byte 3 # DW_IDX_die_offset + .byte 17 # DW_FORM_ref1 + .byte 0 # End of abbrev + .byte 0 # End of abbrev + .byte 4 # Abbrev code + .byte 46 # DW_TAG_subprogram + .byte 3 # DW_IDX_die_offset + .byte 17 # DW_FORM_ref1 + .byte 3 # DW_IDX_die_offset + .byte 17 # DW_FORM_ref1 + .byte 0 # End of abbrev + .byte 0 # End of abbrev + .byte 0 # End of abbrev list +.Lnames_abbrev_end0: +.Lnames_end0: Index: test/tools/llvm-dwarfdump/X86/debug-names-verify-no-buckets.s =================================================================== --- test/tools/llvm-dwarfdump/X86/debug-names-verify-no-buckets.s +++ test/tools/llvm-dwarfdump/X86/debug-names-verify-no-buckets.s @@ -66,7 +66,7 @@ .byte 46 # Abbrev code .byte 46 # DW_TAG_subprogram .byte 3 # DW_IDX_die_offset - .byte 6 # DW_FORM_data4 + .byte 19 # DW_FORM_ref4 .byte 0 # End of abbrev .byte 0 # End of abbrev .byte 0 # End of abbrev list