libdrgn: dwarf_info: fix resolving incomplete type in wrong scope

find_namespace_containing_die() only looks for DW_TAG_namespace DIEs
containing the target DIE, but it also needs to look fo nested
classes/structs/unions. Consider the following program:

  namespace ns {
    class Bar { ... };
    class Foo {
      class Bar { ... };
      ...
    };
  };

If we encounter a declaration DIE for ns::Foo::Bar, we'll end up looking
for the definition directly in ns and finding ns::Bar instead, which is
a completely different type.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2023-08-11 17:08:16 -07:00
parent 6840f10b03
commit bc8514512f
2 changed files with 122 additions and 3 deletions

View File

@ -4830,16 +4830,21 @@ find_namespace_containing_die(struct drgn_debug_info *dbinfo,
return err;
for (size_t i = 0; i < num_ancestors; i++) {
if (dwarf_tag(&ancestors[i]) != DW_TAG_namespace)
switch (dwarf_tag(&ancestors[i])) {
#define X(name) case DW_TAG_##name: break;
DRGN_DWARF_INDEX_NAMESPACE_TAGS
#undef X
default:
continue;
}
Dwarf_Attribute attr_mem, *attr;
if (!(attr = dwarf_attr_integrate(&ancestors[i], DW_AT_name,
&attr_mem)))
continue;
const char *name = dwarf_formstring(attr);
if (!name) {
err = drgn_error_create(DRGN_ERROR_OTHER,
"DW_TAG_namespace has invalid DW_AT_name");
err = drgn_error_libdw();
goto out;
}

View File

@ -1013,6 +1013,120 @@ class TestTypes(TestCase):
),
)
def test_incomplete_to_complete_nested(self):
prog = dwarf_program(
wrap_test_type_dies(
DwarfDie(
DW_TAG.pointer_type,
(
DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8),
DwarfAttrib(DW_AT.type, DW_FORM.ref4, "incomplete_class_die"),
),
),
DwarfDie(
DW_TAG.class_type,
(
DwarfAttrib(DW_AT.name, DW_FORM.string, "Foo"),
DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 0),
),
(
DwarfLabel("incomplete_class_die"),
DwarfDie(
DW_TAG.class_type,
(
DwarfAttrib(DW_AT.name, DW_FORM.string, "Bar"),
DwarfAttrib(
DW_AT.declaration, DW_FORM.flag_present, True
),
),
),
DwarfDie(
DW_TAG.class_type,
(
DwarfAttrib(DW_AT.name, DW_FORM.string, "Bar"),
DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 0),
),
),
),
),
DwarfDie(
DW_TAG.class_type,
(
DwarfAttrib(DW_AT.name, DW_FORM.string, "Bar"),
DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 1),
),
),
DwarfDie(
DW_TAG.subprogram,
(DwarfAttrib(DW_AT.name, DW_FORM.string, "main"),),
),
),
lang=DW_LANG.C_plus_plus,
)
self.assertIdentical(
prog.type("TEST").type.type,
prog.class_type("Bar", 0, ()),
)
def test_incomplete_to_complete_nested_specification(self):
prog = dwarf_program(
wrap_test_type_dies(
DwarfDie(
DW_TAG.pointer_type,
(
DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8),
DwarfAttrib(DW_AT.type, DW_FORM.ref4, "incomplete_class_die"),
),
),
DwarfDie(
DW_TAG.class_type,
(
DwarfAttrib(DW_AT.name, DW_FORM.string, "Foo"),
DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 0),
),
(
DwarfLabel("incomplete_class_die"),
DwarfDie(
DW_TAG.class_type,
(
DwarfAttrib(DW_AT.name, DW_FORM.string, "Bar"),
DwarfAttrib(
DW_AT.declaration, DW_FORM.flag_present, True
),
),
),
DwarfDie(
DW_TAG.class_type,
(
DwarfAttrib(
DW_AT.specification,
DW_FORM.ref4,
"incomplete_class_die",
),
DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 0),
),
),
),
),
DwarfDie(
DW_TAG.class_type,
(
DwarfAttrib(DW_AT.name, DW_FORM.string, "Bar"),
DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 1),
),
),
DwarfDie(
DW_TAG.subprogram,
(DwarfAttrib(DW_AT.name, DW_FORM.string, "main"),),
),
),
lang=DW_LANG.C_plus_plus,
)
self.assertIdentical(
prog.type("TEST").type.type,
prog.class_type("Bar", 0, ()),
)
def test_filename(self):
dies = (
DwarfDie(