From fd2517ec6367f736834acef69b7d490803d2b479 Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Thu, 1 Feb 2024 03:12:38 +0300 Subject: [PATCH] [AArch64][PAC][lldb][Dwarf] Support `[[clang::ptrauth_vtable_pointer(...)]]` 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. --- clang/lib/CodeGen/CGDebugInfo.cpp | 41 +++ .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 42 +++ .../DWARF/DWARFASTParserClangTests.cpp | 275 ++++++++++++++++++ 3 files changed, 358 insertions(+) diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 44757b8ebb05..d030167cf55f 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -2432,6 +2432,47 @@ void CGDebugInfo::CollectVTableInfo(const CXXRecordDecl *RD, llvm::DIFile *Unit, if (!VPtrTy) VPtrTy = getOrCreateVTablePtrType(Unit); + if (auto *VptrAuthAttr = RD->getAttr()) { + 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, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 21fdeff1a27e..c5b1abff8eb1 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -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 diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp index 5d9d386430d4..ec0f57276cf3 100644 --- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp +++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp @@ -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("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(); + 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 subsystems; clang_utils::TypeSystemClangHolder holder;