Skip to content

Add support for parsing type unit entries in .debug_names. #72952

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 2 commits into from
Nov 28, 2023
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
20 changes: 15 additions & 5 deletions lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,29 @@ llvm::DenseSet<dw_offset_t>
DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) {
llvm::DenseSet<dw_offset_t> result;
for (const DebugNames::NameIndex &ni : debug_names) {
for (uint32_t cu = 0; cu < ni.getCUCount(); ++cu)
const uint32_t num_cus = ni.getCUCount();
for (uint32_t cu = 0; cu < num_cus; ++cu)
result.insert(ni.getCUOffset(cu));
const uint32_t num_tus = ni.getLocalTUCount();
for (uint32_t tu = 0; tu < num_tus; ++tu)
result.insert(ni.getLocalTUOffset(tu));
}
return result;
}

std::optional<DIERef>
DebugNamesDWARFIndex::ToDIERef(const DebugNames::Entry &entry) {
std::optional<uint64_t> cu_offset = entry.getCUOffset();
if (!cu_offset)
return std::nullopt;
// Look for a DWARF unit offset (CU offset or local TU offset) as they are
// both offsets into the .debug_info section.
std::optional<uint64_t> unit_offset = entry.getCUOffset();
if (!unit_offset) {
unit_offset = entry.getLocalTUOffset();
if (!unit_offset)
return std::nullopt;
}

DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset);
DWARFUnit *cu =
m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *unit_offset);
Comment on lines +61 to +62
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will return a NULL cu if the *unit_offset is UINT32_MAX, so it will work with tombstoned units. Though we can make a patch to `DebugNames::Entry::getLocalTUOffset()' to return std::nullopt for tombstoned units as a better fix.

if (!cu)
return std::nullopt;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Test that we can use .debug_names to lookup a type that is only referenced
// from within a type unit. In the code below the type named "stype" is only
// referenced within the type unit itself and when we enable .debug_names, we
// expect the have an entry for this and to be able to find this type when
// we do a lookup.

// REQUIRES: lld

// RUN: %clang %s -target x86_64-pc-linux -gdwarf-5 -fdebug-types-section \
// RUN: -gpubnames -fno-limit-debug-info -c -o %t.o
// RUN: ld.lld %t.o -o %t
// RUN: %lldb %t -o "type lookup stype" -b | FileCheck %s --check-prefix=BASE
// RUN: %lldb %t -o "type lookup bar::stype" -b | FileCheck %s --check-prefix=PART
// RUN: %lldb %t -o "type lookup foo::bar::stype" -b | FileCheck %s --check-prefix=FULL

// BASE: (lldb) type lookup stype
// BASE-NEXT: int

// PART: (lldb) type lookup bar::stype
// PART-NEXT: int

// FULL: (lldb) type lookup foo::bar::stype
// FULL-NEXT: int

namespace foo {
class bar {
public:
typedef unsigned utype;
// This type is only referenced from within the type unit and we need to
// make sure we can find it with the new type unit support in .debug_names.
typedef int stype;

private:
utype m_unsigned;

public:
bar(utype u) : m_unsigned(u) {}

utype get() const { return m_unsigned; }
void set(utype u) { m_unsigned = u; }
stype gets() const { return (stype)m_unsigned; }
};
} // namespace foo

int main() {
foo::bar b(12);
return 0;
}
19 changes: 18 additions & 1 deletion llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ class DWARFAcceleratorTable {
/// recorded in this Accelerator Entry.
virtual std::optional<uint64_t> getCUOffset() const = 0;

/// Returns the Offset of the Type Unit associated with this
/// Accelerator Entry or std::nullopt if the Type Unit offset is not
/// recorded in this Accelerator Entry.
virtual std::optional<uint64_t> getLocalTUOffset() const {
// Default return for accelerator tables that don't support type units.
return std::nullopt;
}

/// Returns the Tag of the Debug Info Entry associated with this
/// Accelerator Entry or std::nullopt if the Tag is not recorded in this
/// Accelerator Entry.
Expand Down Expand Up @@ -424,6 +432,7 @@ class DWARFDebugNames : public DWARFAcceleratorTable {

public:
std::optional<uint64_t> getCUOffset() const override;
std::optional<uint64_t> getLocalTUOffset() const override;
std::optional<dwarf::Tag> getTag() const override { return tag(); }

/// Returns the Index into the Compilation Unit list of the owning Name
Expand All @@ -433,9 +442,17 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
/// which will handle that check itself). Note that entries in NameIndexes
/// which index just a single Compilation Unit are implicitly associated
/// with that unit, so this function will return 0 even without an explicit
/// DW_IDX_compile_unit attribute.
/// DW_IDX_compile_unit attribute, unless there is a DW_IDX_type_unit
/// attribute.
std::optional<uint64_t> getCUIndex() const;

/// Returns the Index into the Local Type Unit list 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
/// getLocalTUOffset(), which will handle that check itself).
std::optional<uint64_t> getLocalTUIndex() const;

/// .debug_names-specific getter, which always succeeds (DWARF v5 index
/// entries always have a tag).
dwarf::Tag tag() const { return Abbr->Tag; }
Expand Down
18 changes: 17 additions & 1 deletion llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,10 @@ std::optional<uint64_t> DWARFDebugNames::Entry::getCUIndex() 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, but only if we don't have a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why do we really need this check?
With this check, when there is no DW_IDX_compile_unit but DW_IDX_type_unit presents, getCUIndex will return nullopt. Per my understanding, this is split dwarf (per-CU index) + TU scenario which we should return 0 instead of nulptr, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only return 0 if this DebugName::Entry doesn't have a DW_IDX_type_unit and if there is no valid CU index from a DW_IDX_compile_unit . The code is correct.

// DW_IDX_type_unit.
if (lookup(dwarf::DW_IDX_type_unit).has_value())
return std::nullopt;
if (NameIdx->getCUCount() == 1)
return 0;
return std::nullopt;
Expand All @@ -634,6 +637,19 @@ std::optional<uint64_t> DWARFDebugNames::Entry::getCUOffset() const {
return NameIdx->getCUOffset(*Index);
}

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

std::optional<uint64_t> DWARFDebugNames::Entry::getLocalTUIndex() const {
if (std::optional<DWARFFormValue> Off = lookup(dwarf::DW_IDX_type_unit))
return Off->getAsUnsignedConstant();
return std::nullopt;
}

void DWARFDebugNames::Entry::dump(ScopedPrinter &W) const {
W.startLine() << formatv("Abbrev: {0:x}\n", Abbr->Code);
W.startLine() << formatv("Tag: {0}\n", Abbr->Tag);
Expand Down