Skip to content

[lldb][DWARFASTParserClang] Support constant index encoding of DW_AT_object_pointer #144998

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 7 commits into from
Jun 23, 2025
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
51 changes: 38 additions & 13 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,44 @@ DWARFASTParserClang::GetObjectParameter(const DWARFDIE &subprogram,
subprogram.Tag() == DW_TAG_inlined_subroutine ||
subprogram.Tag() == DW_TAG_subroutine_type);

if (DWARFDIE object_parameter =
subprogram.GetAttributeValueAsReferenceDIE(DW_AT_object_pointer))
return object_parameter;
// The DW_AT_object_pointer may be either encoded as a reference to a DIE,
// in which case that's the object parameter we want. Or it can be a constant
// index of the parameter.
std::optional<size_t> object_pointer_index;
DWARFFormValue form_value;
if (subprogram.GetDIE()->GetAttributeValue(
subprogram.GetCU(), DW_AT_object_pointer, form_value,
/*end_attr_offset_ptr=*/nullptr, /*check_elaborating_dies=*/true)) {
if (auto ref = form_value.Reference())
return ref;

object_pointer_index = form_value.Unsigned();
}

// Try to find the DW_TAG_formal_parameter via object_pointer_index.
DWARFDIE object_pointer;
size_t param_index = 0;
for (const auto &child : subprogram.children()) {
if (child.Tag() != DW_TAG_formal_parameter)
continue;

if (param_index == object_pointer_index.value_or(0)) {
object_pointer = child;
break;
}

++param_index;
}

// No formal parameter found for object pointer index.
// Nothing to be done.
if (!object_pointer)
return {};

// We found the object pointer encoded via DW_AT_object_pointer.
// No need for the remaining heuristics.
if (object_pointer_index)
return object_pointer;

// If no DW_AT_object_pointer was specified, assume the implicit object
// parameter is the first parameter to the function, is called "this" and is
Expand All @@ -178,16 +213,6 @@ DWARFASTParserClang::GetObjectParameter(const DWARFDIE &subprogram,
if (!decl_ctx_die.IsStructUnionOrClass())
return {};

auto children = subprogram.children();
auto it = llvm::find_if(children, [](const DWARFDIE &child) {
return child.Tag() == DW_TAG_formal_parameter;
});

if (it == children.end())
return {};

DWARFDIE object_pointer = *it;

if (!object_pointer.GetAttributeValueAsUnsigned(DW_AT_artificial, 0))
return {};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
// XFAIL: *
//
// FIXME: Explicit object parameter is not shown in
// type lookup output. This is because we don't attach
// valid source locations to decls in the DWARF AST,
// so the ParmVarDecl::isExplicitObjectParameter fails.

// Tests that we correctly deduce the CV-quals and storage
// class of explicit object member functions.
Expand All @@ -8,10 +13,10 @@
//
// CHECK: (lldb) type lookup Foo
// CHECK-NEXT: struct Foo {
// CHECK-NEXT: void Method(Foo);
// CHECK-NEXT: void cMethod(const Foo &) const;
// CHECK-NEXT: void vMethod(volatile Foo &) volatile;
// CHECK-NEXT: void cvMethod(const volatile Foo &) const volatile;
// CHECK-NEXT: void Method(this Foo);
// CHECK-NEXT: void cMethod(this const Foo &) const;
// CHECK-NEXT: void vMethod(this volatile Foo &) volatile;
// CHECK-NEXT: void cvMethod(this const volatile Foo &) const volatile;
// CHECK-NEXT: }

struct Foo {
Expand Down
194 changes: 194 additions & 0 deletions lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1423,3 +1423,197 @@ TEST_F(DWARFASTParserClangTests, TestParseSubroutine_ParameterCreation) {
EXPECT_EQ(func->getParamDecl(1)->getDeclContext(), func);
EXPECT_EQ(func->getParamDecl(1)->getName(), "namedParam");
}

TEST_F(DWARFASTParserClangTests, TestObjectPointer_IndexEncoding) {
// This tests the behaviour of DWARFASTParserClang
// for DW_TAG_subprogram definitions which have a DW_AT_object_pointer
// that encodes a constant index (instead of a DIE reference).

const char *yamldata = R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_AARCH64
DWARF:
debug_str:
- Context
- func
- this
- self
- arg
debug_abbrev:
- ID: 0
Table:
- Code: 0x1
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_language
Form: DW_FORM_data2
- Code: 0x2
Tag: DW_TAG_structure_type
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Code: 0x3
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Attribute: DW_AT_declaration
Form: DW_FORM_flag_present
- Attribute: DW_AT_object_pointer
Form: DW_FORM_implicit_const
Value: 1
- Attribute: DW_AT_external
Form: DW_FORM_flag_present
- Code: 0x4
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Attribute: DW_AT_declaration
Form: DW_FORM_flag_present
- Attribute: DW_AT_object_pointer
Form: DW_FORM_implicit_const
Value: 0
- Attribute: DW_AT_external
Form: DW_FORM_flag_present

- Code: 0x5
Tag: DW_TAG_formal_parameter
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp

- Code: 0x6
Tag: DW_TAG_formal_parameter
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Attribute: DW_AT_artificial
Form: DW_FORM_flag_present

debug_info:
- Version: 5
UnitType: DW_UT_compile
AddrSize: 8
Entries:

# DW_TAG_compile_unit
# DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus)

- AbbrCode: 0x1
Values:
- Value: 0x04

# DW_TAG_structure_type
# DW_AT_name [DW_FORM_strp] ("Context")

- AbbrCode: 0x2
Values:
- Value: 0x0

# DW_TAG_subprogram
# DW_AT_name [DW_FORM_strp] ("func")
# DW_AT_object_pointer [DW_FORM_implicit_const] (1)
- AbbrCode: 0x3
Values:
- Value: 0x8
- Value: 0x1
- Value: 0x1
- Value: 0x1

# DW_TAG_formal_parameter
# DW_AT_name [DW_FORM_strp] ("arg")
- AbbrCode: 0x5
Values:
- Value: 0x17

# DW_TAG_formal_parameter
# DW_AT_name [DW_FORM_strp] ("self")
# DW_AT_artificial
- AbbrCode: 0x6
Values:
- Value: 0x12
- Value: 0x1

- AbbrCode: 0x0

# DW_TAG_subprogram
# DW_AT_object_pointer [DW_FORM_implicit_const] (0)
# DW_AT_name [DW_FORM_strp] ("func")
- AbbrCode: 0x4
Values:
- Value: 0x8
- Value: 0x1
- Value: 0x1
- Value: 0x1

# DW_TAG_formal_parameter
# DW_AT_name [DW_FORM_strp] ("this")
# DW_AT_artificial
- AbbrCode: 0x6
Values:
- Value: 0xd
- Value: 0x1

# DW_TAG_formal_parameter
# DW_AT_name [DW_FORM_strp] ("arg")
- AbbrCode: 0x5
Values:
- Value: 0x17

- AbbrCode: 0x0
- AbbrCode: 0x0
...
)";

YAMLModuleTester t(yamldata);

DWARFUnit *unit = t.GetDwarfUnit();
ASSERT_NE(unit, nullptr);
const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
DWARFDIE cu_die(unit, cu_entry);

auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
auto &ast_ctx = *holder->GetAST();
DWARFASTParserClangStub ast_parser(ast_ctx);

auto context_die = cu_die.GetFirstChild();
ASSERT_TRUE(context_die.IsValid());
ASSERT_EQ(context_die.Tag(), DW_TAG_structure_type);

auto sub1 = context_die.GetFirstChild();
ASSERT_TRUE(sub1.IsValid());
ASSERT_EQ(sub1.Tag(), DW_TAG_subprogram);

auto sub2 = sub1.GetSibling();
ASSERT_TRUE(sub2.IsValid());
ASSERT_EQ(sub2.Tag(), DW_TAG_subprogram);

// Object parameter is at constant index 1
{
auto param_die = sub1.GetFirstChild().GetSibling();
ASSERT_TRUE(param_die.IsValid());

EXPECT_EQ(param_die, ast_parser.GetObjectParameter(sub1, context_die));
}

// Object parameter is at constant index 0
{
auto param_die = sub2.GetFirstChild();
ASSERT_TRUE(param_die.IsValid());

EXPECT_EQ(param_die, ast_parser.GetObjectParameter(sub2, context_die));
}
}
Loading