Skip to content

Commit

Permalink
[AArch64][PAC][lldb][Dwarf] Support `[[clang::ptrauth_vtable_pointer(…
Browse files Browse the repository at this point in the history
…...)]]` attribute

- Encode the attribute in Dwarf as a `DW_TAG_LLVM_ptrauth_type` entry
  corresponding to vtable pointer. The attribute supports "default"
  values for address and extra discrimination, but in Dwarf we emit
  actual values due to `DW_AT_LLVM_ptrauth_*` attributes limitations. See
  lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp for details.

- When parsing dynamic class and structure types from Dwarf, check the
  vtable pointer type against `DW_TAG_LLVM_ptrauth_type`. If true, apply
  the `clang::ptrauth_vtable_pointer` attribute with corresponding
  parameters to the class record decl. "Default" address and extra
  discrimination values are not restored, the actual ones are, but the
  result is effectively the same since the signing schema must be
  preserved unchanged.
  • Loading branch information
kovdan01 committed Feb 12, 2024
1 parent 1cd3542 commit 08567db
Show file tree
Hide file tree
Showing 3 changed files with 358 additions and 0 deletions.
41 changes: 41 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2432,6 +2432,47 @@ void CGDebugInfo::CollectVTableInfo(const CXXRecordDecl *RD, llvm::DIFile *Unit,
if (!VPtrTy)
VPtrTy = getOrCreateVTablePtrType(Unit);

if (auto *VptrAuthAttr = RD->getAttr<VTablePointerAuthenticationAttr>()) {
unsigned TypedDiscriminator =
CGM.getContext().getPointerAuthVTablePointerDiscriminator(RD);
auto &LangOpts = CGM.getContext().getLangOpts();

unsigned Key = VptrAuthAttr->getKey();

bool HasAddressDiscrimination =
LangOpts.PointerAuthVTPtrAddressDiscrimination;
if (VptrAuthAttr->getAddressDiscrimination() !=
VTablePointerAuthenticationAttr::DefaultAddressDiscrimination) {
HasAddressDiscrimination =
(VptrAuthAttr->getAddressDiscrimination() ==
VTablePointerAuthenticationAttr::AddressDiscrimination);
}

unsigned ExtraDiscriminator = 0;

switch (VptrAuthAttr->getExtraDiscrimination()) {
case VTablePointerAuthenticationAttr::DefaultExtraDiscrimination:
if (LangOpts.PointerAuthVTPtrTypeDiscrimination)
ExtraDiscriminator = TypedDiscriminator;
break;
case VTablePointerAuthenticationAttr::TypeDiscrimination:
ExtraDiscriminator = TypedDiscriminator;
break;
case VTablePointerAuthenticationAttr::CustomDiscrimination:
ExtraDiscriminator = VptrAuthAttr->getCustomDiscriminationValue();
break;
case VTablePointerAuthenticationAttr::NoExtraDiscrimination:
break;
}

// See CodeGenModule::computeVTPointerAuthentication:
// isIsaPointer and authenticatesNullValues are always false.
VPtrTy = DBuilder.createPtrAuthQualifiedType(
VPtrTy, Key, HasAddressDiscrimination, ExtraDiscriminator,
/* isIsaPointer */ false,
/* authenticatesNullValues */ false);
}

unsigned Size = CGM.getContext().getTypeSize(CGM.getContext().VoidPtrTy);
llvm::DIType *VPtrMember =
DBuilder.createMemberType(Unit, getVTableName(RD), Unit, 0, Size, 0, 0,
Expand Down
42 changes: 42 additions & 0 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1842,6 +1842,48 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc,
attrs.name.GetCString(), tag_decl_kind, attrs.class_language,
&metadata, attrs.exports_symbols);
}

if (metadata.GetIsDynamicCXXType()) {
clang::RecordDecl *record_decl = m_ast.GetAsRecordDecl(clang_type);
DWARFDIE vptr_type_die =
die.GetFirstChild().GetAttributeValueAsReferenceDIE(DW_AT_type);
if (vptr_type_die.Tag() == DW_TAG_LLVM_ptrauth_type) {
unsigned key = vptr_type_die.GetAttributeValueAsUnsigned(
DW_AT_LLVM_ptrauth_key, -1);
unsigned discriminator = vptr_type_die.GetAttributeValueAsUnsigned(
DW_AT_LLVM_ptrauth_extra_discriminator, -1);
unsigned has_addr_discr = vptr_type_die.GetAttributeValueAsUnsigned(
DW_AT_LLVM_ptrauth_address_discriminated, -1);

auto error_missing = [&vptr_type_die](const dw_attr_t attr) {
vptr_type_die.GetDWARF()->GetObjectFile()->GetModule()->ReportError(
"[{0:x16}]: missing attribute {1:x4} ({2}) required for signed "
"vtable pointer",
vptr_type_die.GetOffset(), attr, DW_AT_value_to_name(attr));
};

if (key == unsigned(-1))
error_missing(DW_AT_LLVM_ptrauth_key);
if (discriminator == unsigned(-1))
error_missing(DW_AT_LLVM_ptrauth_extra_discriminator);
if (has_addr_discr == unsigned(-1))
error_missing(DW_AT_LLVM_ptrauth_extra_discriminator);

record_decl->addAttr(
clang::VTablePointerAuthenticationAttr::CreateImplicit(
m_ast.getASTContext(),
key == 2
? clang::VTablePointerAuthenticationAttr::ProcessDependent
: clang::VTablePointerAuthenticationAttr::
ProcessIndependent,
has_addr_discr ? clang::VTablePointerAuthenticationAttr::
AddressDiscrimination
: clang::VTablePointerAuthenticationAttr::
NoAddressDiscrimination,
clang::VTablePointerAuthenticationAttr::CustomDiscrimination,
discriminator));
}
}
}

// Store a forward declaration to this class type in case any
Expand Down
275 changes: 275 additions & 0 deletions lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,281 @@ TEST_F(DWARFASTParserClangTests, TestPtrAuthParsing) {
ASSERT_EQ(type_as_string, "void (*__ptrauth(0,0,42))(...)");
}

TEST_F(DWARFASTParserClangTests, TestVTablePtrAuthParsing) {
// Tests parsing dynamic structure types with explicit vtable pointer
// authentication

// This is Dwarf for the following C++ code:
// ```
// struct [[clang::ptrauth_vtable_pointer(process_dependent,
// address_discrimination,
// custom_discrimination, 42)]] A {
// virtual void foo() {}
// };
// A a;
// ```

const char *yamldata = R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_AARCH64
DWARF:
debug_str:
- a
- A
- _vptr$A
- foo
- __vtbl_ptr_type
- int
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_variable
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Attribute: DW_AT_type
Form: DW_FORM_ref4
- Attribute: DW_AT_external
Form: DW_FORM_flag_present
- Code: 0x3
Tag: DW_TAG_structure_type
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_containing_type
Form: DW_FORM_ref4
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Code: 0x4
Tag: DW_TAG_member
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Attribute: DW_AT_type
Form: DW_FORM_ref4
- Attribute: DW_AT_artificial
Form: DW_FORM_flag_present
- Code: 0x5
Tag: DW_TAG_subprogram
Children: DW_CHILDREN_yes
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Attribute: DW_AT_virtuality
Form: DW_FORM_data1
- Attribute: DW_AT_containing_type
Form: DW_FORM_ref4
- Code: 0x6
Tag: DW_TAG_formal_parameter
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_type
Form: DW_FORM_ref4
- Attribute: DW_AT_artificial
Form: DW_FORM_flag_present
- Code: 0x7
Tag: DW_TAG_LLVM_ptrauth_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_type
Form: DW_FORM_ref4
- Attribute: DW_AT_LLVM_ptrauth_key
Form: DW_FORM_data1
- Attribute: DW_AT_LLVM_ptrauth_extra_discriminator
Form: DW_FORM_data2
- Attribute: DW_AT_LLVM_ptrauth_address_discriminated
Form: DW_FORM_flag_present
- Code: 0x8
Tag: DW_TAG_pointer_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_type
Form: DW_FORM_ref4
- Code: 0x9
Tag: DW_TAG_pointer_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_type
Form: DW_FORM_ref4
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Code: 0xA
Tag: DW_TAG_subroutine_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_type
Form: DW_FORM_ref4
- Code: 0xB
Tag: DW_TAG_base_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Attribute: DW_AT_encoding
Form: DW_FORM_data1
- Attribute: DW_AT_byte_size
Form: DW_FORM_data1
- Code: 0xC
Tag: DW_TAG_pointer_type
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_type
Form: DW_FORM_ref4
debug_info:
- Version: 5
UnitType: DW_UT_compile
AddrSize: 8
Entries:
# 0x0c: DW_TAG_compile_unit
# DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus_11)
- AbbrCode: 0x1
Values:
- Value: 0x1A
# 0x0f: DW_TAG_variable
# DW_AT_name [DW_FORM_strp] (\"a\")
# DW_AT_type [DW_FORM_ref4] (0x00000018 \"A\")
# DW_AT_external [DW_FORM_flag_present] (true)
- AbbrCode: 0x2
Values:
- Value: 0x00
- Value: 0x18
# 0x18: DW_TAG_structure_type
# DW_AT_containing_type [DW_FORM_ref4] (0x00000018 \"A\")
# DW_AT_name [DW_FORM_strp] (\"A\")
- AbbrCode: 0x3
Values:
- Value: 0x18
- Value: 0x02
# 0x21: DW_TAG_member
# DW_AT_name [DW_FORM_strp] (\"_vptr$A\")
# DW_AT_type [DW_FORM_ref4] (0x0000002f)
# DW_AT_artificial [DW_FORM_flag_present] (true)
- AbbrCode: 0x4
Values:
- Value: 0x04
- Value: 0x3B
# 0x2a: DW_TAG_subprogram
# DW_AT_name [DW_FORM_strp] (\"foo\")
# DW_AT_virtuality [DW_FORM_data1] (DW_VIRTUALITY_virtual)
# DW_AT_containing_type [DW_FORM_ref4] (0x00000018 \"A\")
- AbbrCode: 0x5
Values:
- Value: 0x0C
- Value: 0x01
- Value: 0x18
# 0x34: DW_TAG_formal_parameter
# DW_AT_type [DW_FORM_ref4] (0x0000005d \"A *\")
# DW_AT_artificial [DW_FORM_flag_present] (true)
- AbbrCode: 0x6
Values:
- Value: 0x5D
- AbbrCode: 0x0 # end of child tags of 0x2a
- AbbrCode: 0x0 # end of child tags of 0x18
# 0x3b: DW_TAG_LLVM_ptrauth_type
# DW_AT_type [DW_FORM_ref4] (0x00000043 \"int (**)()\")
# DW_AT_LLVM_ptrauth_key [DW_FORM_data1] (0x02)
# DW_AT_LLVM_ptrauth_extra_discriminator [DW_FORM_data2] (0x002a)
# DW_AT_LLVM_ptrauth_address_discriminated [DW_FORM_flag_present] (true)
- AbbrCode: 0x7
Values:
- Value: 0x43
- Value: 0x02
- Value: 0x2A
# 0x43: DW_TAG_pointer_type
# DW_AT_type [DW_FORM_ref4] (0x00000048 \"int (*)()\")
- AbbrCode: 0x8
Values:
- Value: 0x48
# 0x48: DW_TAG_pointer_type
# DW_AT_type [DW_FORM_ref4] (0x00000051 \"int ()\")
# DW_AT_name [DW_FORM_strp] (\"__vtbl_ptr_type\")
- AbbrCode: 0x9
Values:
- Value: 0x51
- Value: 0x10
# 0x51: DW_TAG_subroutine_type
# DW_AT_type [DW_FORM_ref4] (0x00000056 \"int\")
- AbbrCode: 0xA
Values:
- Value: 0x56
# 0x56: DW_TAG_base_type
# DW_AT_name [DW_FORM_strp] (\"int\")
# DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed)
# DW_AT_byte_size [DW_FORM_data1] (0x04)
- AbbrCode: 0xB
Values:
- Value: 0x20
- Value: 0x05
- Value: 0x04
# 0x5d: DW_TAG_pointer_type
# DW_AT_type [DW_FORM_ref4] (0x00000018 \"A\")
- AbbrCode: 0xC
Values:
- Value: 0x18
- AbbrCode: 0x0 # end of child tags of 0x0c
...
)";
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);
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);

DWARFDIE struct_object = cu_die.GetFirstChild();
ASSERT_EQ(struct_object.Tag(), DW_TAG_variable);
DWARFDIE structure_type =
struct_object.GetAttributeValueAsReferenceDIE(DW_AT_type);
ASSERT_EQ(structure_type.Tag(), DW_TAG_structure_type);

SymbolContext sc;
bool new_type = false;
lldb::TypeSP type =
ast_parser.ParseTypeFromDWARF(sc, structure_type, &new_type);
clang::RecordDecl *record_decl =
TypeSystemClang::GetAsRecordDecl(type->GetForwardCompilerType());
auto *attr = record_decl->getAttr<clang::VTablePointerAuthenticationAttr>();
ASSERT_NE(attr, nullptr);
ASSERT_EQ(attr->getKey(),
clang::VTablePointerAuthenticationAttr::ProcessDependent);
ASSERT_EQ(attr->getAddressDiscrimination(),
clang::VTablePointerAuthenticationAttr::AddressDiscrimination);
ASSERT_EQ(attr->getExtraDiscrimination(),
clang::VTablePointerAuthenticationAttr::CustomDiscrimination);
ASSERT_EQ(attr->getCustomDiscriminationValue(), 42);
}

struct ExtractIntFromFormValueTest : public testing::Test {
SubsystemRAII<FileSystem, HostInfo> subsystems;
clang_utils::TypeSystemClangHolder holder;
Expand Down

0 comments on commit 08567db

Please sign in to comment.