Files
clang-p2996/lldb/unittests/SymbolFile/DWARF/DWARFDIETest.cpp
Pavel Labath dd5d730072 [lldb] Better matching of types in anonymous namespaces (#102111)
This patch extends TypeQuery matching to support anonymous namespaces. A
new flag is added to control the behavior. In the "strict" mode, the
query must match the type exactly -- all anonymous namespaces included.
The dynamic type resolver in the itanium abi (the motivating use case
for this) uses this flag, as it queries using the name from the
demangles, which includes anonymous namespaces.

This ensures we don't confuse a type with a same-named type in an
anonymous namespace. However, this does *not* ensure we don't confuse
two types in anonymous namespacs (in different CUs). To resolve this, we
would need to use a completely different lookup algorithm, which
probably also requires a DWARF extension.

In the "lax" mode (the default), the anonymous namespaces in the query
are optional, and this allows one search for the type using the usual
language rules (`::A` matches `::(anonymous namespace)::A`).

This patch also changes the type context computation algorithm in
DWARFDIE, so that it includes anonymous namespace information. This
causes a slight change in behavior: the algorithm previously stopped
computing the context after encountering an anonymous namespace, which
caused the outer namespaces to be ignored. This meant that a type like
`NS::(anonymous namespace)::A` would be (incorrectly) recognized as
`::A`). This can cause code depending on the old behavior to misbehave.
The fix is to specify all the enclosing namespaces in the query, or use
a non-exact match.
2024-09-02 08:34:14 +02:00

397 lines
14 KiB
C++

//===-- DWARFDIETest.cpp ----------------------------------------------=---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Plugins/SymbolFile/DWARF/DWARFDIE.h"
#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
#include "TestingSupport/Symbol/YAMLModuleTester.h"
#include "lldb/Core/dwarf.h"
#include "lldb/Symbol/Type.h"
#include "lldb/lldb-private-enumerations.h"
#include "llvm/ADT/STLExtras.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::plugin::dwarf;
using namespace lldb_private::dwarf;
TEST(DWARFDIETest, ChildIteration) {
// Tests DWARFDIE::child_iterator.
const char *yamldata = R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_386
DWARF:
debug_abbrev:
- Table:
- Code: 0x00000001
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_language
Form: DW_FORM_data2
- Code: 0x00000002
Tag: DW_TAG_base_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_encoding
Form: DW_FORM_data1
- Attribute: DW_AT_byte_size
Form: DW_FORM_data1
debug_info:
- Version: 4
AddrSize: 8
Entries:
- AbbrCode: 0x00000001
Values:
- Value: 0x000000000000000C
- AbbrCode: 0x00000002
Values:
- Value: 0x0000000000000007 # DW_ATE_unsigned
- Value: 0x0000000000000004
- AbbrCode: 0x00000002
Values:
- Value: 0x0000000000000007 # DW_ATE_unsigned
- Value: 0x0000000000000008
- AbbrCode: 0x00000002
Values:
- Value: 0x0000000000000005 # DW_ATE_signed
- Value: 0x0000000000000008
- AbbrCode: 0x00000000
)";
YAMLModuleTester t(yamldata);
ASSERT_TRUE((bool)t.GetDwarfUnit());
DWARFUnit *unit = t.GetDwarfUnit();
const DWARFDebugInfoEntry *die_first = unit->DIE().GetDIE();
// Create a DWARFDIE that has three DW_TAG_base_type children.
DWARFDIE top_die(unit, die_first);
// Create the iterator range that has the three tags as elements.
llvm::iterator_range<DWARFDIE::child_iterator> children = top_die.children();
// Compare begin() to the first child DIE.
DWARFDIE::child_iterator child_iter = children.begin();
ASSERT_NE(child_iter, children.end());
const DWARFDebugInfoEntry *die_child0 = die_first->GetFirstChild();
EXPECT_EQ((*child_iter).GetDIE(), die_child0);
// Step to the second child DIE.
++child_iter;
ASSERT_NE(child_iter, children.end());
const DWARFDebugInfoEntry *die_child1 = die_child0->GetSibling();
EXPECT_EQ((*child_iter).GetDIE(), die_child1);
// Step to the third child DIE.
++child_iter;
ASSERT_NE(child_iter, children.end());
const DWARFDebugInfoEntry *die_child2 = die_child1->GetSibling();
EXPECT_EQ((*child_iter).GetDIE(), die_child2);
// Step to the end of the range.
++child_iter;
EXPECT_EQ(child_iter, children.end());
// Take one of the DW_TAG_base_type DIEs (which has no children) and make
// sure the children range is now empty.
DWARFDIE no_children_die(unit, die_child0);
EXPECT_TRUE(no_children_die.children().empty());
}
TEST(DWARFDIETest, PeekName) {
const char *yamldata = R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_386
DWARF:
debug_str:
- 'NameType1'
- 'NameType2'
debug_abbrev:
- Table:
- Code: 0x00000001
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_language
Form: DW_FORM_data2
- Code: 0x00000002
Tag: DW_TAG_base_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Code: 0x00000003
Tag: DW_TAG_base_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_abstract_origin
Form: DW_FORM_ref1
- Code: 0x00000004
Tag: DW_TAG_base_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_specification
Form: DW_FORM_ref1
debug_info:
- Version: 4
AddrSize: 8
Entries:
- AbbrCode: 0x00000001
Values:
- Value: 0x000000000000000C
- AbbrCode: 0x00000002
Values:
- Value: 0x0000000000000000 # Name = NameType1
- AbbrCode: 0x00000002
Values:
- Value: 0x000000000000000a # Name = NameType2
- AbbrCode: 0x00000003
Values:
- Value: 0x000000000000000e # Ref abstract origin to NameType1 DIE.
- AbbrCode: 0x00000004
Values:
- Value: 0x0000000000000013 # Ref specification to NameType2 DIE.
- AbbrCode: 0x00000000
)";
YAMLModuleTester t(yamldata);
auto *symbol_file =
llvm::cast<SymbolFileDWARF>(t.GetModule()->GetSymbolFile());
DWARFUnit *unit = symbol_file->DebugInfo().GetUnitAtIndex(0);
dw_offset_t first_die_offset = 11;
EXPECT_EQ(unit->PeekDIEName(first_die_offset), "");
dw_offset_t second_die_offset = 14;
EXPECT_EQ(unit->PeekDIEName(second_die_offset), "NameType1");
dw_offset_t third_die_offset = 19;
EXPECT_EQ(unit->PeekDIEName(third_die_offset), "NameType2");
dw_offset_t fourth_die_offset = 24;
EXPECT_EQ(unit->PeekDIEName(fourth_die_offset), "NameType1");
dw_offset_t fifth_die_offset = 26;
EXPECT_EQ(unit->PeekDIEName(fifth_die_offset), "NameType2");
}
TEST(DWARFDIETest, GetContext) {
const char *yamldata = R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_386
DWARF:
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_namespace
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_string
- Code: 0x3
Tag: DW_TAG_structure_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_string
- Code: 0x4
Tag: DW_TAG_namespace
Children: DW_CHILDREN_yes
debug_info:
- Version: 4
AddrSize: 8
Entries:
- AbbrCode: 0x1
Values:
- Value: 0x000000000000000C
- AbbrCode: 0x2
Values:
- CStr: NAMESPACE
- AbbrCode: 0x3
Values:
- CStr: STRUCT
- AbbrCode: 0x4
- AbbrCode: 0x3
Values:
- CStr: STRUCT
- AbbrCode: 0x0
- AbbrCode: 0x0
- AbbrCode: 0x0
)";
YAMLModuleTester t(yamldata);
auto *symbol_file =
llvm::cast<SymbolFileDWARF>(t.GetModule()->GetSymbolFile());
DWARFUnit *unit = symbol_file->DebugInfo().GetUnitAtIndex(0);
ASSERT_TRUE(unit);
auto make_namespace = [](const char *name) {
return CompilerContext(CompilerContextKind::Namespace, ConstString(name));
};
auto make_struct = [](const char *name) {
return CompilerContext(CompilerContextKind::ClassOrStruct,
ConstString(name));
};
DWARFDIE struct_die = unit->DIE().GetFirstChild().GetFirstChild();
ASSERT_TRUE(struct_die);
DWARFDIE anon_struct_die = struct_die.GetSibling().GetFirstChild();
ASSERT_TRUE(anon_struct_die);
EXPECT_THAT(
struct_die.GetDeclContext(),
testing::ElementsAre(make_namespace("NAMESPACE"), make_struct("STRUCT")));
EXPECT_THAT(
struct_die.GetTypeLookupContext(),
testing::ElementsAre(make_namespace("NAMESPACE"), make_struct("STRUCT")));
EXPECT_THAT(struct_die.GetDWARFDeclContext(),
DWARFDeclContext({{DW_TAG_structure_type, "STRUCT"},
{DW_TAG_namespace, "NAMESPACE"}}));
EXPECT_THAT(anon_struct_die.GetDeclContext(),
testing::ElementsAre(make_namespace("NAMESPACE"),
make_namespace(nullptr),
make_struct("STRUCT")));
EXPECT_THAT(anon_struct_die.GetTypeLookupContext(),
testing::ElementsAre(make_namespace("NAMESPACE"),
make_namespace(nullptr),
make_struct("STRUCT")));
EXPECT_THAT(anon_struct_die.GetDWARFDeclContext(),
DWARFDeclContext({{DW_TAG_structure_type, "STRUCT"},
{DW_TAG_namespace, nullptr},
{DW_TAG_namespace, "NAMESPACE"}}));
}
TEST(DWARFDIETest, GetContextInFunction) {
// Make sure we get the right context fo each "struct_t" type. The first
// should be "a::struct_t" and the one defined in the "foo" function should be
// "struct_t". Previous DWARFDIE::GetTypeLookupContext() function calls would
// have the "struct_t" in "foo" be "a::struct_t" because it would traverse the
// entire die parent tree and ignore DW_TAG_subprogram and keep traversing the
// parents.
//
// 0x0000000b: DW_TAG_compile_unit
// 0x0000000c: DW_TAG_namespace
// DW_AT_name("a")
// 0x0000000f: DW_TAG_structure_type
// DW_AT_name("struct_t")
// 0x00000019: DW_TAG_subprogram
// DW_AT_name("foo")
// 0x0000001e: DW_TAG_structure_type
// DW_AT_name("struct_t")
// 0x00000028: NULL
// 0x00000029: NULL
// 0x0000002a: NULL
const char *yamldata = R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_386
DWARF:
debug_str:
- ''
debug_abbrev:
- ID: 0
Table:
- Code: 0x1
Tag: DW_TAG_compile_unit
Children: DW_CHILDREN_yes
- Code: 0x2
Tag: DW_TAG_namespace
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_string
- Code: 0x3
Tag: DW_TAG_structure_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_string
- Code: 0x4
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_string
debug_info:
- Length: 0x27
Version: 4
AbbrevTableID: 0
AbbrOffset: 0x0
AddrSize: 8
Entries:
- AbbrCode: 0x1
- AbbrCode: 0x2
Values:
- Value: 0xDEADBEEFDEADBEEF
CStr: a
- AbbrCode: 0x3
Values:
- Value: 0xDEADBEEFDEADBEEF
CStr: struct_t
- AbbrCode: 0x4
Values:
- Value: 0xDEADBEEFDEADBEEF
CStr: foo
- AbbrCode: 0x3
Values:
- Value: 0xDEADBEEFDEADBEEF
CStr: struct_t
- AbbrCode: 0x0
- AbbrCode: 0x0
- AbbrCode: 0x0)";
YAMLModuleTester t(yamldata);
auto *symbol_file =
llvm::cast<SymbolFileDWARF>(t.GetModule()->GetSymbolFile());
DWARFUnit *unit = symbol_file->DebugInfo().GetUnitAtIndex(0);
ASSERT_TRUE(unit);
auto make_namespace = [](llvm::StringRef name) {
return CompilerContext(CompilerContextKind::Namespace, ConstString(name));
};
auto make_struct = [](llvm::StringRef name) {
return CompilerContext(CompilerContextKind::ClassOrStruct,
ConstString(name));
};
// Grab the "a::struct_t" type from the "a" namespace
DWARFDIE a_struct_die = unit->DIE().GetFirstChild().GetFirstChild();
ASSERT_TRUE(a_struct_die);
EXPECT_THAT(
a_struct_die.GetDeclContext(),
testing::ElementsAre(make_namespace("a"), make_struct("struct_t")));
// Grab the "struct_t" defined in the "foo" function.
DWARFDIE foo_struct_die =
unit->DIE().GetFirstChild().GetFirstChild().GetSibling().GetFirstChild();
EXPECT_THAT(foo_struct_die.GetTypeLookupContext(),
testing::ElementsAre(make_struct("struct_t")));
}