Skip to content

Commit

Permalink
DWARFVerifier: Check "completeness" of .debug_names section
Browse files Browse the repository at this point in the history
Summary:
This patch implements a check which makes sure all entries required by
the DWARF v5 specification are present in the Name Index. The algorithm
tries to follow the wording of Section 6.1.1.1 of the spec as closely as
possible.

The main deviation from it is that instead of a whitelist-based approach
in the spec "The name index must contain an entry for each debugging
information entry that defines a named subprogram, label, variable,
type, or namespace" I chose a blacklist-based one, where I consider
everything to be "in" and then remove the entries that don't make sense.
I did this because it has more potential for catching interesting cases
and the above is a bit vague (it uses plain words like "variable" and
"subprogram", but the rest of the section speaks about specific TAGs).

This approach has raised some interesting questions, the main one being
whether enumerator values should be indexed. The consensus seems to be
that they should, although it does not follow from section 6.1.1.1.
For the time being I made the verifier ignore these, as LLVM does not do
this yet, and I wanted to get a clean run when verifying generated debug
info.

Another interesting case was the DW_TAG_imported_declaration. It was not
immediately clear to me whether this should go in or not, but currently
it is not indexed, and (unlike the enumerators) in does not seem to cause
problems for LLDB, so I've also ignored it.

Reviewers: JDevlieghere, aprantl, dblaikie

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D46583

llvm-svn: 331868
  • Loading branch information
labath committed May 9, 2018
1 parent fe5c527 commit 3280e04
Show file tree
Hide file tree
Showing 5 changed files with 393 additions and 4 deletions.
16 changes: 16 additions & 0 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
Expand Up @@ -429,6 +429,9 @@ class DWARFDebugNames : public DWARFAcceleratorTable {

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; }
Expand All @@ -444,6 +447,10 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
/// "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.
Expand All @@ -464,6 +471,10 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
/// 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;

Expand All @@ -488,6 +499,7 @@ class DWARFDebugNames : public DWARFAcceleratorTable {

private:
SmallVector<NameIndex, 0> NameIndices;
DenseMap<uint32_t, const NameIndex *> CUToNameIndex;

public:
DWARFDebugNames(const DWARFDataExtractor &AccelSection,
Expand All @@ -503,6 +515,10 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
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
Expand Down
7 changes: 5 additions & 2 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h
Expand Up @@ -25,6 +25,7 @@ struct DWARFAttribute;
class DWARFContext;
class DWARFDie;
class DWARFUnit;
class DWARFCompileUnit;
class DWARFDataExtractor;
class DWARFDebugAbbrev;
class DataExtractor;
Expand Down Expand Up @@ -242,6 +243,8 @@ class DWARFVerifier {
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.
///
Expand All @@ -253,8 +256,8 @@ class DWARFVerifier {
/// - 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
Expand Down
33 changes: 31 additions & 2 deletions llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
146 changes: 146 additions & 0 deletions llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
Expand Up @@ -1122,6 +1122,7 @@ DWARFVerifier::verifyNameIndexEntries(const DWARFDebugNames::NameIndex &NI,
"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(),
Expand All @@ -1142,6 +1143,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;
Expand Down Expand Up @@ -1171,6 +1307,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;
}

Expand Down

0 comments on commit 3280e04

Please sign in to comment.