Reapply "DWARFVerifier: Check "completeness" of .debug_names section"

This is a resubmit of r331868 (D46583), which was reverted due to
failures on the PS4 bot.

These have been resolved with r332246/D46748.

llvm-svn: 332349
This commit is contained in:
Pavel Labath 2018-05-15 13:24:10 +00:00
parent a66bfaa4c0
commit 80827f10a1
5 changed files with 392 additions and 4 deletions

View File

@ -429,6 +429,9 @@ public:
Expected<Entry> getEntry(uint32_t *Offset) const;
/// Look up all entries in this Name Index matching \c Key.
iterator_range<ValueIterator> equal_range(StringRef Key) const;
llvm::Error extract();
uint32_t getUnitOffset() const { return Base; }
uint32_t getNextUnitOffset() const { return Base + 4 + Hdr.UnitLength; }
@ -444,6 +447,10 @@ public:
/// "NameIndices" vector in the Accelerator section.
const NameIndex *CurrentIndex = nullptr;
/// Whether this is a local iterator (searches in CurrentIndex only) or not
/// (searches all name indices).
bool IsLocal;
Optional<Entry> CurrentEntry;
unsigned DataOffset = 0; ///< Offset into the section.
std::string Key; ///< The Key we are searching for.
@ -464,6 +471,10 @@ public:
/// Indexes in the section in sequence.
ValueIterator(const DWARFDebugNames &AccelTable, StringRef Key);
/// Create a "begin" iterator for looping over all entries in a specific
/// Name Index. Other indices in the section will not be visited.
ValueIterator(const NameIndex &NI, StringRef Key);
/// End marker.
ValueIterator() = default;
@ -488,6 +499,7 @@ public:
private:
SmallVector<NameIndex, 0> NameIndices;
DenseMap<uint32_t, const NameIndex *> CUToNameIndex;
public:
DWARFDebugNames(const DWARFDataExtractor &AccelSection,
@ -503,6 +515,10 @@ public:
using const_iterator = SmallVector<NameIndex, 0>::const_iterator;
const_iterator begin() const { return NameIndices.begin(); }
const_iterator end() const { return NameIndices.end(); }
/// Return the Name Index covering the compile unit at CUOffset, or nullptr if
/// there is no Name Index covering that unit.
const NameIndex *getCUNameIndex(uint32_t CUOffset);
};
} // end namespace llvm

View File

@ -25,6 +25,7 @@ struct DWARFAttribute;
class DWARFContext;
class DWARFDie;
class DWARFUnit;
class DWARFCompileUnit;
class DWARFDataExtractor;
class DWARFDebugAbbrev;
class DataExtractor;
@ -242,6 +243,8 @@ private:
DWARFDebugNames::AttributeEncoding AttrEnc);
unsigned verifyNameIndexEntries(const DWARFDebugNames::NameIndex &NI,
uint32_t Name, const DataExtractor &StrData);
unsigned verifyNameIndexCompleteness(const DWARFDie &Die,
const DWARFDebugNames::NameIndex &NI);
/// Verify that the DWARF v5 accelerator table is valid.
///
@ -253,8 +256,8 @@ private:
/// - The buckets have a valid index, or they are empty.
/// - All names are reachable via the hash table (they have the correct hash,
/// and the hash is in the correct bucket).
/// - Information in the index entries is consistent with the debug_info
/// section DIEs.
/// - Information in the index entries is complete (all required entries are
/// present) and consistent with the debug_info section DIEs.
///
/// \param AccelSection section containing the acceleration table
/// \param StrData string section

View File

@ -770,6 +770,11 @@ llvm::Error DWARFDebugNames::extract() {
return Error::success();
}
iterator_range<DWARFDebugNames::ValueIterator>
DWARFDebugNames::NameIndex::equal_range(StringRef Key) const {
return make_range(ValueIterator(*this, Key), ValueIterator());
}
LLVM_DUMP_METHOD void DWARFDebugNames::dump(raw_ostream &OS) const {
ScopedPrinter W(OS);
for (const NameIndex &NI : NameIndices)
@ -844,20 +849,44 @@ void DWARFDebugNames::ValueIterator::next() {
if (getEntryAtCurrentOffset())
return;
// Try the next Name Index.
// If we're a local iterator, we're done.
if (IsLocal) {
setEnd();
return;
}
// Otherwise, try the next index.
++CurrentIndex;
searchFromStartOfCurrentIndex();
}
DWARFDebugNames::ValueIterator::ValueIterator(const DWARFDebugNames &AccelTable,
StringRef Key)
: CurrentIndex(AccelTable.NameIndices.begin()), Key(Key) {
: CurrentIndex(AccelTable.NameIndices.begin()), IsLocal(false), Key(Key) {
searchFromStartOfCurrentIndex();
}
DWARFDebugNames::ValueIterator::ValueIterator(
const DWARFDebugNames::NameIndex &NI, StringRef Key)
: CurrentIndex(&NI), IsLocal(true), Key(Key) {
if (!findInCurrentIndex())
setEnd();
}
iterator_range<DWARFDebugNames::ValueIterator>
DWARFDebugNames::equal_range(StringRef Key) const {
if (NameIndices.empty())
return make_range(ValueIterator(), ValueIterator());
return make_range(ValueIterator(*this, Key), ValueIterator());
}
const DWARFDebugNames::NameIndex *
DWARFDebugNames::getCUNameIndex(uint32_t CUOffset) {
if (CUToNameIndex.size() == 0 && NameIndices.size() > 0) {
for (const auto &NI : *this) {
for (uint32_t CU = 0; CU < NI.getCUCount(); ++CU)
CUToNameIndex.try_emplace(NI.getCUOffset(CU), &NI);
}
}
return CUToNameIndex.lookup(CUOffset);
}

View File

@ -1142,6 +1142,141 @@ DWARFVerifier::verifyNameIndexEntries(const DWARFDebugNames::NameIndex &NI,
return NumErrors;
}
static bool isVariableIndexable(const DWARFDie &Die, DWARFContext &DCtx) {
Optional<DWARFFormValue> Location = Die.findRecursively(DW_AT_location);
if (!Location)
return false;
auto ContainsInterestingOperators = [&](StringRef D) {
DWARFUnit *U = Die.getDwarfUnit();
DataExtractor Data(D, DCtx.isLittleEndian(), U->getAddressByteSize());
DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize());
return any_of(Expression, [](DWARFExpression::Operation &Op) {
return !Op.isError() && (Op.getCode() == DW_OP_addr ||
Op.getCode() == DW_OP_form_tls_address ||
Op.getCode() == DW_OP_GNU_push_tls_address);
});
};
if (Optional<ArrayRef<uint8_t>> Expr = Location->getAsBlock()) {
// Inlined location.
if (ContainsInterestingOperators(toStringRef(*Expr)))
return true;
} else if (Optional<uint64_t> Offset = Location->getAsSectionOffset()) {
// Location list.
if (const DWARFDebugLoc *DebugLoc = DCtx.getDebugLoc()) {
if (const DWARFDebugLoc::LocationList *LocList =
DebugLoc->getLocationListAtOffset(*Offset)) {
if (any_of(LocList->Entries, [&](const DWARFDebugLoc::Entry &E) {
return ContainsInterestingOperators({E.Loc.data(), E.Loc.size()});
}))
return true;
}
}
}
return false;
}
unsigned DWARFVerifier::verifyNameIndexCompleteness(
const DWARFDie &Die, const DWARFDebugNames::NameIndex &NI) {
// First check, if the Die should be indexed. The code follows the DWARF v5
// wording as closely as possible.
// "All non-defining declarations (that is, debugging information entries
// with a DW_AT_declaration attribute) are excluded."
if (Die.find(DW_AT_declaration))
return 0;
// "DW_TAG_namespace debugging information entries without a DW_AT_name
// attribute are included with the name “(anonymous namespace)”.
// All other debugging information entries without a DW_AT_name attribute
// are excluded."
// "If a subprogram or inlined subroutine is included, and has a
// DW_AT_linkage_name attribute, there will be an additional index entry for
// the linkage name."
auto EntryNames = getNames(Die);
if (EntryNames.empty())
return 0;
// We deviate from the specification here, which says:
// "The name index must contain an entry for each debugging information entry
// that defines a named subprogram, label, variable, type, or namespace,
// subject to ..."
// Instead whitelisting all TAGs representing a "type" or a "subprogram", to
// make sure we catch any missing items, we instead blacklist all TAGs that we
// know shouldn't be indexed.
switch (Die.getTag()) {
// Compile unit has a name but it shouldn't be indexed.
case DW_TAG_compile_unit:
return 0;
// Function and template parameters are not globally visible, so we shouldn't
// index them.
case DW_TAG_formal_parameter:
case DW_TAG_template_value_parameter:
case DW_TAG_template_type_parameter:
case DW_TAG_GNU_template_parameter_pack:
case DW_TAG_GNU_template_template_param:
return 0;
// Object members aren't globally visible.
case DW_TAG_member:
return 0;
// According to a strict reading of the specification, enumerators should not
// be indexed (and LLVM currently does not do that). However, this causes
// problems for the debuggers, so we may need to reconsider this.
case DW_TAG_enumerator:
return 0;
// Imported declarations should not be indexed according to the specification
// and LLVM currently does not do that.
case DW_TAG_imported_declaration:
return 0;
// "DW_TAG_subprogram, DW_TAG_inlined_subroutine, and DW_TAG_label debugging
// information entries without an address attribute (DW_AT_low_pc,
// DW_AT_high_pc, DW_AT_ranges, or DW_AT_entry_pc) are excluded."
case DW_TAG_subprogram:
case DW_TAG_inlined_subroutine:
case DW_TAG_label:
if (Die.findRecursively(
{DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges, DW_AT_entry_pc}))
break;
return 0;
// "DW_TAG_variable debugging information entries with a DW_AT_location
// attribute that includes a DW_OP_addr or DW_OP_form_tls_address operator are
// included; otherwise, they are excluded."
//
// LLVM extension: We also add DW_OP_GNU_push_tls_address to this list.
case DW_TAG_variable:
if (isVariableIndexable(Die, DCtx))
break;
return 0;
default:
break;
}
// Now we know that our Die should be present in the Index. Let's check if
// that's the case.
unsigned NumErrors = 0;
for (StringRef Name : EntryNames) {
if (none_of(NI.equal_range(Name), [&Die](const DWARFDebugNames::Entry &E) {
return E.getDIESectionOffset() == uint64_t(Die.getOffset());
})) {
error() << formatv("Name Index @ {0:x}: Entry for DIE @ {1:x} ({2}) with "
"name {3} missing.\n",
NI.getUnitOffset(), Die.getOffset(), Die.getTag(),
Name);
++NumErrors;
}
}
return NumErrors;
}
unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection,
const DataExtractor &StrData) {
unsigned NumErrors = 0;
@ -1171,6 +1306,16 @@ unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection,
for (uint64_t Name = 1; Name <= NI.getNameCount(); ++Name)
NumErrors += verifyNameIndexEntries(NI, Name, StrData);
if (NumErrors > 0)
return NumErrors;
for (const std::unique_ptr<DWARFCompileUnit> &CU : DCtx.compile_units()) {
if (const DWARFDebugNames::NameIndex *NI =
AccelTable.getCUNameIndex(CU->getOffset())) {
for (const DWARFDebugInfoEntry &Die : CU->dies())
NumErrors += verifyNameIndexCompleteness(DWARFDie(CU.get(), &Die), *NI);
}
}
return NumErrors;
}

View File

@ -0,0 +1,195 @@
# RUN: llvm-mc -triple x86_64-pc-linux %s -filetype=obj -o - | not llvm-dwarfdump -verify - | FileCheck %s
# CHECK: error: Name Index @ 0x0: Entry for DIE @ 0x10 (DW_TAG_namespace) with name namesp missing.
# CHECK: error: Name Index @ 0x0: Entry for DIE @ 0x15 (DW_TAG_variable) with name var_block_addr missing.
# CHECK: error: Name Index @ 0x0: Entry for DIE @ 0x25 (DW_TAG_namespace) with name (anonymous namespace) missing.
# CHECK: error: Name Index @ 0x0: Entry for DIE @ 0x26 (DW_TAG_variable) with name var_loc_addr missing.
# CHECK: error: Name Index @ 0x0: Entry for DIE @ 0x30 (DW_TAG_variable) with name var_loc_tls missing.
# CHECK: error: Name Index @ 0x0: Entry for DIE @ 0x37 (DW_TAG_variable) with name var_loc_gnu_tls missing.
# CHECK: error: Name Index @ 0x0: Entry for DIE @ 0x3e (DW_TAG_subprogram) with name fun_name missing.
# CHECK: error: Name Index @ 0x0: Entry for DIE @ 0x3e (DW_TAG_subprogram) with name _Z8fun_name missing.
# CHECK: error: Name Index @ 0x0: Entry for DIE @ 0x4f (DW_TAG_inlined_subroutine) with name fun_inline missing.
# CHECK: error: Name Index @ 0x0: Entry for DIE @ 0x64 (DW_TAG_label) with name label missing.
.section .debug_str,"MS",@progbits,1
.Linfo_producer:
.asciz "hand-written DWARF"
.Lname_var_block_addr:
.asciz "var_block_addr"
.Lname_var_loc_addr:
.asciz "var_loc_addr"
.Lname_var_loc_tls:
.asciz "var_loc_tls"
.Lname_var_loc_gnu_tls:
.asciz "var_loc_gnu_tls"
.Lname_fun_name:
.asciz "fun_name"
.Lname_fun_link_name:
.asciz "_Z8fun_name"
.Lname_fun_inline:
.asciz "fun_inline"
.Lnamespace:
.asciz "namesp"
.Lname_label:
.asciz "label"
.section .debug_loc,"",@progbits
.Ldebug_loc0:
.quad 0
.quad 1
.short .Lloc0_end-.Lloc0_start # Loc expr size
.Lloc0_start:
.byte 3 # DW_OP_addr
.quad 0x47
.Lloc0_end:
.quad 0
.quad 0
.section .debug_abbrev,"",@progbits
.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 0 # EOM(1)
.byte 0 # EOM(2)
.byte 2 # Abbreviation Code
.byte 52 # DW_TAG_variable
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 2 # DW_AT_location
.byte 24 # DW_FORM_exprloc
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 3 # Abbreviation Code
.byte 46 # DW_TAG_subprogram
.byte 1 # DW_CHILDREN_yes
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 110 # DW_AT_linkage_name
.byte 14 # DW_FORM_strp
.byte 82 # DW_AT_entry_pc
.byte 1 # DW_FORM_addr
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 4 # Abbreviation Code
.byte 57 # DW_TAG_namespace
.byte 1 # DW_CHILDREN_yes
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 5 # Abbreviation Code
.byte 52 # DW_TAG_variable
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 2 # DW_AT_location
.byte 23 # DW_FORM_sec_offset
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 6 # Abbreviation Code
.byte 57 # DW_TAG_namespace
.byte 1 # DW_CHILDREN_yes
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 7 # Abbreviation Code
.byte 29 # DW_TAG_inlined_subroutine
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 17 # DW_AT_low_pc
.byte 1 # DW_FORM_addr
.byte 18 # DW_AT_high_pc
.byte 1 # DW_FORM_addr
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 8 # Abbreviation Code
.byte 10 # DW_TAG_label
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 82 # DW_AT_entry_pc
.byte 1 # DW_FORM_addr
.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 .debug_abbrev # Offset Into Abbrev. Section
.byte 8 # Address Size (in bytes)
.byte 1 # Abbrev [1] DW_TAG_compile_unit
.long .Linfo_producer # DW_AT_producer
.byte 4 # Abbrev [4] DW_TAG_namespace
.long .Lnamespace # DW_AT_name
.byte 2 # Abbrev [2] DW_TAG_variable
.long .Lname_var_block_addr # DW_AT_name
.byte 9 # DW_AT_location
.byte 3 # DW_OP_addr
.quad 0x47
.byte 0 # End Of Children Mark
.byte 6 # Abbrev [6] DW_TAG_namespace
.byte 5 # Abbrev [5] DW_TAG_variable
.long .Lname_var_loc_addr # DW_AT_name
.long .Ldebug_loc0 # DW_AT_location
.byte 0 # End Of Children Mark
.byte 2 # Abbrev [2] DW_TAG_variable
.long .Lname_var_loc_tls # DW_AT_name
.byte 1 # DW_AT_location
.byte 0x9b # DW_OP_form_tls_address
.byte 2 # Abbrev [2] DW_TAG_variable
.long .Lname_var_loc_gnu_tls # DW_AT_name
.byte 1 # DW_AT_location
.byte 0xe0 # DW_OP_GNU_push_tls_address
.byte 3 # Abbrev [3] DW_TAG_subprogram
.long .Lname_fun_name # DW_AT_name
.long .Lname_fun_link_name # DW_AT_linkage_name
.quad 0x47 # DW_AT_entry_pc
.byte 7 # Abbrev [7] DW_TAG_inlined_subroutine
.long .Lname_fun_inline # DW_AT_name
.quad 0x48 # DW_AT_low_pc
.quad 0x49 # DW_AT_high_pc
.byte 8 # Abbrev [8] DW_TAG_label
.long .Lname_label # DW_AT_name
.quad 0x4a # DW_AT_entry_pc
.byte 0 # End Of Children Mark
.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 0 # End of abbrev list
.Lnames_abbrev_end0:
.Lnames_entries0:
.Lnames_end0: