Skip to content

Add verification support for .debug_names with foreign type units. #109011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
std::optional<uint64_t> getForeignTUTypeSignature() const override;
std::optional<dwarf::Tag> getTag() const override { return tag(); }

// Special function that will return the related CU offset needed type
// Special function that will return the related CU offset needed type
// units. This gets used to find the .dwo file that originated the entries
// for a given type unit.
std::optional<uint64_t> getRelatedCUOffset() const;
Expand All @@ -468,12 +468,13 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
/// index for an entry that is a type unit.
std::optional<uint64_t> getRelatedCUIndex() const;

/// Returns the Index into the Local Type Unit list of the owning Name
/// Returns the index of the Type Unit of the owning
/// Name
/// Index or std::nullopt if this Accelerator Entry does not have an
/// associated Type Unit. It is up to the user to verify that the
/// returned Index is valid in the owning NameIndex (or use
/// returned Index is a valid index in the owning NameIndex (or use
/// getLocalTUOffset(), which will handle that check itself).
std::optional<uint64_t> getLocalTUIndex() const;
std::optional<uint64_t> getTUIndex() const;

/// .debug_names-specific getter, which always succeeds (DWARF v5 index
/// entries always have a tag).
Expand Down Expand Up @@ -803,7 +804,7 @@ class DWARFDebugNames : public DWARFAcceleratorTable {

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

public:
DWARFDebugNames(const DWARFDataExtractor &AccelSection,
Expand All @@ -820,9 +821,9 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
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(uint64_t CUOffset);
/// Return the Name Index covering the compile unit or local type unit at
/// UnitOffset, or nullptr if there is no Name Index covering that unit.
const NameIndex *getCUOrTUNameIndex(uint64_t UnitOffset);
};

/// Calculates the starting offsets for various sections within the
Expand Down
5 changes: 4 additions & 1 deletion llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ class DWARFContext : public DIContext {
return State->getDWOUnits();
}

/// Return true of this DWARF context is a DWP file.
bool isDWP() const;

/// Get units from .debug_types.dwo in the DWO context.
unit_iterator_range dwo_types_section_units() {
DWARFUnitVector &DWOUnits = State->getDWOUnits();
Expand Down Expand Up @@ -262,7 +265,7 @@ class DWARFContext : public DIContext {
}

DWARFCompileUnit *getDWOCompileUnitForHash(uint64_t Hash);
DWARFTypeUnit *getTypeUnitForHash(uint16_t Version, uint64_t Hash, bool IsDWO);
DWARFTypeUnit *getTypeUnitForHash(uint64_t Hash, bool IsDWO);

/// Return the DWARF unit that includes an offset (relative to .debug_info).
DWARFUnit *getUnitForOffset(uint64_t Offset);
Expand Down
18 changes: 10 additions & 8 deletions llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ std::optional<uint64_t> DWARFDebugNames::Entry::getRelatedCUIndex() const {
if (std::optional<DWARFFormValue> Off = lookup(dwarf::DW_IDX_compile_unit))
return Off->getAsUnsignedConstant();
// In a per-CU index, the entries without a DW_IDX_compile_unit attribute
// implicitly refer to the single CU.
// implicitly refer to the single CU.
if (NameIdx->getCUCount() == 1)
return 0;
return std::nullopt;
Expand Down Expand Up @@ -665,15 +665,15 @@ std::optional<uint64_t> DWARFDebugNames::Entry::getRelatedCUOffset() const {
}

std::optional<uint64_t> DWARFDebugNames::Entry::getLocalTUOffset() const {
std::optional<uint64_t> Index = getLocalTUIndex();
std::optional<uint64_t> Index = getTUIndex();
if (!Index || *Index >= NameIdx->getLocalTUCount())
return std::nullopt;
return NameIdx->getLocalTUOffset(*Index);
}

std::optional<uint64_t>
DWARFDebugNames::Entry::getForeignTUTypeSignature() const {
std::optional<uint64_t> Index = getLocalTUIndex();
std::optional<uint64_t> Index = getTUIndex();
const uint32_t NumLocalTUs = NameIdx->getLocalTUCount();
if (!Index || *Index < NumLocalTUs)
return std::nullopt; // Invalid TU index or TU index is for a local TU
Expand All @@ -684,7 +684,7 @@ DWARFDebugNames::Entry::getForeignTUTypeSignature() const {
return NameIdx->getForeignTUSignature(ForeignTUIndex);
}

std::optional<uint64_t> DWARFDebugNames::Entry::getLocalTUIndex() const {
std::optional<uint64_t> DWARFDebugNames::Entry::getTUIndex() const {
if (std::optional<DWARFFormValue> Off = lookup(dwarf::DW_IDX_type_unit))
return Off->getAsUnsignedConstant();
return std::nullopt;
Expand Down Expand Up @@ -1061,14 +1061,16 @@ DWARFDebugNames::equal_range(StringRef Key) const {
}

const DWARFDebugNames::NameIndex *
DWARFDebugNames::getCUNameIndex(uint64_t CUOffset) {
if (CUToNameIndex.size() == 0 && NameIndices.size() > 0) {
DWARFDebugNames::getCUOrTUNameIndex(uint64_t UnitOffset) {
if (UnitOffsetToNameIndex.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);
UnitOffsetToNameIndex.try_emplace(NI.getCUOffset(CU), &NI);
for (uint32_t TU = 0; TU < NI.getLocalTUCount(); ++TU)
UnitOffsetToNameIndex.try_emplace(NI.getLocalTUOffset(TU), &NI);
}
}
return CUToNameIndex.lookup(CUOffset);
return UnitOffsetToNameIndex.lookup(UnitOffset);
}

static bool isObjCSelector(StringRef Name) {
Expand Down
5 changes: 3 additions & 2 deletions llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1345,8 +1345,7 @@ void DWARFContext::dump(
getDebugNames().dump(OS);
}

DWARFTypeUnit *DWARFContext::getTypeUnitForHash(uint16_t Version, uint64_t Hash,
bool IsDWO) {
DWARFTypeUnit *DWARFContext::getTypeUnitForHash(uint64_t Hash, bool IsDWO) {
DWARFUnitVector &DWOUnits = State->getDWOUnits();
if (const auto &TUI = getTUIndex()) {
if (const auto *R = TUI.getFromHash(Hash))
Expand Down Expand Up @@ -2478,3 +2477,5 @@ uint8_t DWARFContext::getCUAddrSize() {
auto CUs = compile_units();
return CUs.empty() ? 0 : (*CUs.begin())->getAddressByteSize();
}

bool DWARFContext::isDWP() const { return !DObj->getCUIndexSection().empty(); }
8 changes: 4 additions & 4 deletions llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,8 @@ DWARFDie::getAttributeValueAsReferencedDie(const DWARFFormValue &V) const {
if (DWARFUnit *SpecUnit = U->getUnitVector().getUnitForOffset(*Offset))
Result = SpecUnit->getDIEForOffset(*Offset);
} else if (std::optional<uint64_t> Sig = V.getAsSignatureReference()) {
if (DWARFTypeUnit *TU = U->getContext().getTypeUnitForHash(
U->getVersion(), *Sig, U->isDWOUnit()))
if (DWARFTypeUnit *TU =
U->getContext().getTypeUnitForHash(*Sig, U->isDWOUnit()))
Result = TU->getDIEForOffset(TU->getTypeOffset() + TU->getOffset());
}
return Result;
Expand All @@ -329,8 +329,8 @@ DWARFDie::getAttributeValueAsReferencedDie(const DWARFFormValue &V) const {
DWARFDie DWARFDie::resolveTypeUnitReference() const {
if (auto Attr = find(DW_AT_signature)) {
if (std::optional<uint64_t> Sig = Attr->getAsReferenceUVal()) {
if (DWARFTypeUnit *TU = U->getContext().getTypeUnitForHash(
U->getVersion(), *Sig, U->isDWOUnit()))
if (DWARFTypeUnit *TU =
U->getContext().getTypeUnitForHash(*Sig, U->isDWOUnit()))
return TU->getDIEForOffset(TU->getTypeOffset() + TU->getOffset());
}
}
Expand Down
138 changes: 103 additions & 35 deletions llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "llvm/DebugInfo/DWARF/DWARFLocationExpression.h"
#include "llvm/DebugInfo/DWARF/DWARFObject.h"
#include "llvm/DebugInfo/DWARF/DWARFSection.h"
#include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h"
#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
#include "llvm/Object/Error.h"
#include "llvm/Support/DJB.h"
Expand Down Expand Up @@ -1479,13 +1480,6 @@ unsigned DWARFVerifier::verifyNameIndexAttribute(

unsigned
DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) {
if (NI.getForeignTUCount() > 0) {
warn() << formatv("Name Index @ {0:x}: Verifying indexes of foreign type "
"units is not currently supported.\n",
NI.getUnitOffset());
return 0;
}

unsigned NumErrors = 0;
for (const auto &Abbrev : NI.getAbbrevs()) {
StringRef TagName = dwarf::TagString(Abbrev.Tag);
Expand Down Expand Up @@ -1573,10 +1567,6 @@ static SmallVector<std::string, 3> getNames(const DWARFDie &DIE,
unsigned DWARFVerifier::verifyNameIndexEntries(
const DWARFDebugNames::NameIndex &NI,
const DWARFDebugNames::NameTableEntry &NTE) {
// Verifying foreign type unit indexes not supported.
if (NI.getForeignTUCount() > 0)
return 0;

const char *CStr = NTE.getString();
if (!CStr) {
ErrorCategory.Report("Unable to get string associated with name", [&]() {
Expand All @@ -1596,8 +1586,8 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
for (; EntryOr; ++NumEntries, EntryID = NextEntryID,
EntryOr = NI.getEntry(&NextEntryID)) {

std::optional<uint64_t> CUIndex = EntryOr->getCUIndex();
std::optional<uint64_t> TUIndex = EntryOr->getLocalTUIndex();
std::optional<uint64_t> CUIndex = EntryOr->getRelatedCUIndex();
std::optional<uint64_t> TUIndex = EntryOr->getTUIndex();
if (CUIndex && *CUIndex >= NI.getCUCount()) {
ErrorCategory.Report("Name Index entry contains invalid CU index", [&]() {
error() << formatv("Name Index @ {0:x}: Entry @ {1:x} contains an "
Expand All @@ -1607,7 +1597,9 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
++NumErrors;
continue;
}
if (TUIndex && *TUIndex >= NI.getLocalTUCount()) {
const uint32_t NumLocalTUs = NI.getLocalTUCount();
const uint32_t NumForeignTUs = NI.getForeignTUCount();
if (TUIndex && *TUIndex >= (NumLocalTUs + NumForeignTUs)) {
ErrorCategory.Report("Name Index entry contains invalid TU index", [&]() {
error() << formatv("Name Index @ {0:x}: Entry @ {1:x} contains an "
"invalid TU index ({2}).\n",
Expand All @@ -1617,11 +1609,44 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
continue;
}
std::optional<uint64_t> UnitOffset;
if (TUIndex)
UnitOffset = NI.getLocalTUOffset(*TUIndex);
else if (CUIndex)
if (TUIndex) {
// We have a local or foreign type unit.
if (*TUIndex >= NumLocalTUs) {
// This is a foreign type unit, we will find the right type unit by
// type unit signature later in this function.

// Foreign type units must have a valid CU index, either from a
// DW_IDX_comp_unit attribute value or from the .debug_names table only
// having a single compile unit. We need the originating compile unit
// because foreign type units can come from any .dwo file, yet only one
// copy of the type unit will end up in the .dwp file.
if (CUIndex) {
// We need the local skeleton unit offset for the code below.
UnitOffset = NI.getCUOffset(*CUIndex);
} else {
ErrorCategory.Report(
"Name Index entry contains foreign TU index with invalid CU "
"index",
[&]() {
error() << formatv(
"Name Index @ {0:x}: Entry @ {1:x} contains an "
"foreign TU index ({2}) with no CU index.\n",
NI.getUnitOffset(), EntryID, *TUIndex);
});
++NumErrors;
continue;
}
} else {
// Local type unit, get the DWARF unit offset for the type unit.
UnitOffset = NI.getLocalTUOffset(*TUIndex);
}
} else if (CUIndex) {
// Local CU entry, get the DWARF unit offset for the CU.
UnitOffset = NI.getCUOffset(*CUIndex);
if (!UnitOffset)
}

// Watch for tombstoned type unit entries.
if (!UnitOffset || UnitOffset == UINT32_MAX)
continue;
// For split DWARF entries we need to make sure we find the non skeleton
// DWARF unit that is needed and use that's DWARF unit offset as the
Expand All @@ -1633,7 +1658,7 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
ErrorCategory.Report(
"Name Index entry contains invalid CU or TU offset", [&]() {
error() << formatv("Name Index @ {0:x}: Entry @ {1:x} contains an "
"invalid CU or TU offset {1:x}.\n",
"invalid CU or TU offset {2:x}.\n",
NI.getUnitOffset(), EntryID, *UnitOffset);
});
++NumErrors;
Expand All @@ -1650,20 +1675,52 @@ unsigned DWARFVerifier::verifyNameIndexEntries(
// call to properly deal with it. It isn't clear that getNonSkeletonUnitDIE
// will return the unit DIE of DU if we aren't able to get the .dwo file,
// but that is what the function currently does.
DWARFDie UnitDie = DU->getUnitDIE();
DWARFDie NonSkeletonUnitDie = DU->getNonSkeletonUnitDIE();
if (DU->getDWOId() && DU->getUnitDIE() == NonSkeletonUnitDie) {
if (DU->getDWOId() && UnitDie == NonSkeletonUnitDie) {
ErrorCategory.Report("Unable to get load .dwo file", [&]() {
error() << formatv("Name Index @ {0:x}: Entry @ {1:x} unable to load "
".dwo file \"{2}\" for DWARF unit @ {3:x}.\n",
NI.getUnitOffset(), EntryID,
dwarf::toString(DU->getUnitDIE().find(
{DW_AT_dwo_name, DW_AT_GNU_dwo_name})),
*UnitOffset);
error() << formatv(
"Name Index @ {0:x}: Entry @ {1:x} unable to load "
".dwo file \"{2}\" for DWARF unit @ {3:x}.\n",
NI.getUnitOffset(), EntryID,
dwarf::toString(UnitDie.find({DW_AT_dwo_name, DW_AT_GNU_dwo_name})),
*UnitOffset);
});
++NumErrors;
continue;
}
DWARFUnit *NonSkeletonUnit = NonSkeletonUnitDie.getDwarfUnit();
DWARFUnit *NonSkeletonUnit = nullptr;
if (TUIndex && *TUIndex >= NumLocalTUs) {
// We have a foreign TU index, which either means we have a .dwo file
// that has one or more type units, or we have a .dwp file with one or
// more type units. We need to get the type unit from the DWARFContext
// of the .dwo. We got the NonSkeletonUnitDie above that has the .dwo
// or .dwp DWARF context, so we have to get the type unit from that file.
// We have also verified that NonSkeletonUnitDie points to a DWO file
// above, so we know we have the right file.
const uint32_t ForeignTUIdx = *TUIndex - NumLocalTUs;
const uint64_t TypeSig = NI.getForeignTUSignature(ForeignTUIdx);
llvm::DWARFContext &SkeletonDCtx =
NonSkeletonUnitDie.getDwarfUnit()->getContext();
// Now find the type unit from the type signature and then update the
// NonSkeletonUnitDie to point to the actual type unit in the .dwo/.dwp.
NonSkeletonUnit =
SkeletonDCtx.getTypeUnitForHash(TypeSig, /*IsDWO=*/true);
NonSkeletonUnitDie = NonSkeletonUnit->getUnitDIE(true);
// If we have foreign type unit in a DWP file, then we need to ignore
// any entries from type units that don't match the one that made it into
// the .dwp file.
if (SkeletonDCtx.isDWP()) {
StringRef DUDwoName = dwarf::toStringRef(
UnitDie.find({DW_AT_dwo_name, DW_AT_GNU_dwo_name}));
StringRef TUDwoName = dwarf::toStringRef(
NonSkeletonUnitDie.find({DW_AT_dwo_name, DW_AT_GNU_dwo_name}));
if (DUDwoName != TUDwoName)
continue; // Skip this TU, it isn't the one in the .dwp file.
}
} else {
NonSkeletonUnit = NonSkeletonUnitDie.getDwarfUnit();
}
uint64_t DIEOffset =
NonSkeletonUnit->getOffset() + *EntryOr->getDIEUnitOffset();
const uint64_t NextUnitOffset = NonSkeletonUnit->getNextUnitOffset();
Expand Down Expand Up @@ -1920,15 +1977,26 @@ unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection,
for (const DWARFDebugNames::NameTableEntry &NTE : NI)
NumErrors += verifyNameIndexEntries(NI, NTE);

if (NumErrors > 0)
return NumErrors;

for (const std::unique_ptr<DWARFUnit> &U : DCtx.compile_units()) {
for (const std::unique_ptr<DWARFUnit> &U : DCtx.info_section_units()) {
if (const DWARFDebugNames::NameIndex *NI =
AccelTable.getCUNameIndex(U->getOffset())) {
auto *CU = cast<DWARFCompileUnit>(U.get());
for (const DWARFDebugInfoEntry &Die : CU->dies())
NumErrors += verifyNameIndexCompleteness(DWARFDie(CU, &Die), *NI);
AccelTable.getCUOrTUNameIndex(U->getOffset())) {
DWARFCompileUnit *CU = dyn_cast<DWARFCompileUnit>(U.get());
if (CU) {
if (CU->getDWOId()) {
DWARFDie CUDie = CU->getUnitDIE(true);
DWARFDie NonSkeletonUnitDie =
CUDie.getDwarfUnit()->getNonSkeletonUnitDIE(false);
if (CUDie != NonSkeletonUnitDie) {
for (const DWARFDebugInfoEntry &Die :
NonSkeletonUnitDie.getDwarfUnit()->dies())
NumErrors += verifyNameIndexCompleteness(
DWARFDie(NonSkeletonUnitDie.getDwarfUnit(), &Die), *NI);
}
} else {
for (const DWARFDebugInfoEntry &Die : CU->dies())
NumErrors += verifyNameIndexCompleteness(DWARFDie(CU, &Die), *NI);
}
}
}
}
return NumErrors;
Expand Down
Loading
Loading