Skip to content

Commit ee59834

Browse files
committed
[AArch64][PAC][lldb][Dwarf] Support [[clang::ptrauth_struct(...)]] attribute
1 parent a567cae commit ee59834

File tree

4 files changed

+232
-1
lines changed

4 files changed

+232
-1
lines changed

clang/lib/CodeGen/CGDebugInfo.cpp

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2304,6 +2304,50 @@ llvm::DINodeArray CGDebugInfo::CollectBTFDeclTagAnnotations(const Decl *D) {
23042304
return DBuilder.getOrCreateArray(Annotations);
23052305
}
23062306

2307+
llvm::DINodeArray
2308+
CGDebugInfo::CollectPtrAuthStructAnnotations(const RecordDecl *RD) {
2309+
if (!RD->hasAttr<PointerAuthStructAttr>())
2310+
return nullptr;
2311+
2312+
llvm::LLVMContext &Context = CGM.getLLVMContext();
2313+
2314+
auto [IsKeyValIndependent, Key] =
2315+
CGM.getContext().getPointerAuthStructKey(RD);
2316+
auto [IsDiscValIndependent, Disc] =
2317+
CGM.getContext().getPointerAuthStructDisc(RD);
2318+
2319+
// TODO: handle value-dependent keys and discriminators. What does this even
2320+
// mean? Using template integer parameters as key/disc or expressions like
2321+
// `__builtin_ptrauth_struct_key(...)` result in specific constants, and does
2322+
// not make key/disc value dependent.
2323+
if (!IsKeyValIndependent || !IsDiscValIndependent)
2324+
return nullptr;
2325+
2326+
// TODO: 2-bit key and 16-bit disc results in negative values of i2 and i16
2327+
// types in IR for unsigned integers using all the given bit-width (for ex.,
2328+
// key 3 is i8 -1, disc 65534 is i16 -2). Do we want to use wider types to
2329+
// prevent negation?
2330+
auto *KeyMD = llvm::ConstantAsMetadata::get(
2331+
llvm::ConstantInt::get(Context, llvm::APInt(2, Key, false)));
2332+
auto *DiscMD = llvm::ConstantAsMetadata::get(
2333+
llvm::ConstantInt::get(Context, llvm::APInt(16, Disc, false)));
2334+
2335+
llvm::Metadata *Annotations[2];
2336+
llvm::Metadata *OpsKey[2] = {
2337+
llvm::MDString::get(Context, StringRef("ptrauth_struct_key")), KeyMD};
2338+
Annotations[0] = llvm::MDNode::get(CGM.getLLVMContext(), OpsKey);
2339+
llvm::Metadata *OpsDisc[2] = {
2340+
llvm::MDString::get(Context, StringRef("ptrauth_struct_disc")), DiscMD};
2341+
Annotations[1] = llvm::MDNode::get(CGM.getLLVMContext(), OpsDisc);
2342+
2343+
// TODO: is it OK to return DINodeArray instead of MDNodeArray from
2344+
// CollectPtrAuthStructAnnotations and CollectBTFDeclTagAnnotations? The
2345+
// elements are not actually DINodes, and trying to iterate over the returned
2346+
// DINodeArray results in an assertion failure. This does not seem to happen
2347+
// though - it looks like that there are no places where such loops are used.
2348+
return DBuilder.getOrCreateArray(Annotations);
2349+
}
2350+
23072351
llvm::DIType *CGDebugInfo::getOrCreateVTablePtrType(llvm::DIFile *Unit) {
23082352
if (VTablePtrType)
23092353
return VTablePtrType;
@@ -3790,7 +3834,21 @@ llvm::DICompositeType *CGDebugInfo::CreateLimitedType(const RecordType *Ty) {
37903834
dyn_cast<CXXRecordDecl>(CXXRD->getDeclContext()));
37913835
}
37923836

3793-
llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(D);
3837+
llvm::DINodeArray AnnotationsBTFTag = CollectBTFDeclTagAnnotations(D);
3838+
llvm::DINodeArray AnnotationsPAuthStruct = CollectPtrAuthStructAnnotations(D);
3839+
3840+
llvm::SmallVector<llvm::Metadata *, 4> AnnotationsVec;
3841+
if (AnnotationsBTFTag.get() != nullptr)
3842+
for (const llvm::MDOperand &Op : AnnotationsBTFTag.get()->operands())
3843+
AnnotationsVec.push_back(Op.get());
3844+
if (AnnotationsPAuthStruct.get() != nullptr)
3845+
for (const llvm::MDOperand &Op : AnnotationsPAuthStruct.get()->operands())
3846+
AnnotationsVec.push_back(Op.get());
3847+
3848+
llvm::DINodeArray Annotations =
3849+
AnnotationsVec.empty() ? nullptr
3850+
: DBuilder.getOrCreateArray(AnnotationsVec);
3851+
37943852
llvm::DICompositeType *RealDecl = DBuilder.createReplaceableCompositeType(
37953853
getTagForRecord(RD), RDName, RDContext, DefUnit, Line, 0, Size, Align,
37963854
Flags, Identifier, Annotations);

clang/lib/CodeGen/CGDebugInfo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@ class CGDebugInfo {
316316
/// A helper function to collect debug info for btf_decl_tag annotations.
317317
llvm::DINodeArray CollectBTFDeclTagAnnotations(const Decl *D);
318318

319+
/// A helper function to collect debug info for ptrauth_struct attribute.
320+
llvm::DINodeArray CollectPtrAuthStructAnnotations(const RecordDecl *RD);
321+
319322
llvm::DIType *createFieldType(StringRef name, QualType type,
320323
SourceLocation loc, AccessSpecifier AS,
321324
uint64_t offsetInBits, uint32_t AlignInBits,

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1884,6 +1884,39 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc,
18841884
discriminator));
18851885
}
18861886
}
1887+
1888+
// TODO: check that each annotation does not appear more than once
1889+
std::optional<uint64_t> pauth_key, pauth_disc;
1890+
for (DWARFDIE die_child = die.GetFirstChild(); die_child;
1891+
die_child = die_child.GetSibling()) {
1892+
if (die_child.Tag() != DW_TAG_LLVM_annotation)
1893+
continue;
1894+
if (die_child.GetAttributeValueAsString(DW_AT_name, nullptr) ==
1895+
llvm::StringRef("ptrauth_struct_key"))
1896+
pauth_key =
1897+
die_child.GetAttributeValueAsOptionalUnsigned(DW_AT_const_value);
1898+
else if (die_child.GetAttributeValueAsString(DW_AT_name, nullptr) ==
1899+
llvm::StringRef("ptrauth_struct_disc"))
1900+
pauth_disc =
1901+
die_child.GetAttributeValueAsOptionalUnsigned(DW_AT_const_value);
1902+
}
1903+
1904+
// TODO: do we need some handling for one std::nullopt out of two?
1905+
if (pauth_key != std::nullopt && pauth_disc != std::nullopt) {
1906+
clang::ASTContext &ctx = m_ast.getASTContext();
1907+
llvm::APInt key_int = llvm::APInt(ctx.getTypeSize(ctx.UnsignedIntTy),
1908+
pauth_key.value(), false);
1909+
auto *key = clang::IntegerLiteral::Create(ctx, key_int, ctx.UnsignedIntTy,
1910+
clang::SourceLocation());
1911+
llvm::APInt disc_int = llvm::APInt(ctx.getTypeSize(ctx.UnsignedIntTy),
1912+
pauth_disc.value(), false);
1913+
auto *disc = clang::IntegerLiteral::Create(
1914+
ctx, disc_int, ctx.UnsignedIntTy, clang::SourceLocation());
1915+
1916+
clang::RecordDecl *record_decl = m_ast.GetAsRecordDecl(clang_type);
1917+
record_decl->addAttr(
1918+
clang::PointerAuthStructAttr::CreateImplicit(ctx, key, disc));
1919+
}
18871920
}
18881921

18891922
// Store a forward declaration to this class type in case any

lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,143 @@ TEST_F(DWARFASTParserClangTests, TestVTablePtrAuthParsing) {
695695
ASSERT_EQ(attr->getCustomDiscriminationValue(), 42);
696696
}
697697

698+
TEST_F(DWARFASTParserClangTests, TestPtrAuthStructAttr) {
699+
// Tests parsing types with ptrauth_struct attribute
700+
// authentication
701+
702+
// This is Dwarf for the following C code:
703+
// ```
704+
// struct [[clang::ptrauth_struct(2, 42)]] A {};
705+
// struct A a;
706+
// ```
707+
708+
const char *yamldata = R"(
709+
--- !ELF
710+
FileHeader:
711+
Class: ELFCLASS64
712+
Data: ELFDATA2LSB
713+
Type: ET_EXEC
714+
Machine: EM_AARCH64
715+
DWARF:
716+
debug_str:
717+
- a
718+
- A
719+
- ptrauth_struct_key
720+
- ptrauth_struct_disc
721+
debug_abbrev:
722+
- ID: 0
723+
Table:
724+
- Code: 0x1
725+
Tag: DW_TAG_compile_unit
726+
Children: DW_CHILDREN_yes
727+
Attributes:
728+
- Attribute: DW_AT_language
729+
Form: DW_FORM_data2
730+
- Code: 0x2
731+
Tag: DW_TAG_variable
732+
Children: DW_CHILDREN_no
733+
Attributes:
734+
- Attribute: DW_AT_name
735+
Form: DW_FORM_strp
736+
- Attribute: DW_AT_type
737+
Form: DW_FORM_ref4
738+
- Attribute: DW_AT_external
739+
Form: DW_FORM_flag_present
740+
- Code: 0x3
741+
Tag: DW_TAG_structure_type
742+
Children: DW_CHILDREN_yes
743+
Attributes:
744+
- Attribute: DW_AT_name
745+
Form: DW_FORM_strp
746+
- Code: 0x4
747+
Tag: DW_TAG_LLVM_annotation
748+
Children: DW_CHILDREN_no
749+
Attributes:
750+
- Attribute: DW_AT_name
751+
Form: DW_FORM_strp
752+
- Attribute: DW_AT_const_value
753+
Form: DW_FORM_udata
754+
755+
debug_info:
756+
- Version: 5
757+
UnitType: DW_UT_compile
758+
AddrSize: 8
759+
Entries:
760+
# 0x0c: DW_TAG_compile_unit
761+
# DW_AT_language [DW_FORM_data2] (DW_LANG_C99)
762+
- AbbrCode: 0x1
763+
Values:
764+
- Value: 0x0c
765+
766+
# 0x0f: DW_TAG_variable
767+
# DW_AT_name [DW_FORM_strp] (\"a\")
768+
# DW_AT_type [DW_FORM_ref4] (0x00000018 \"A\")
769+
# DW_AT_external [DW_FORM_flag_present] (true)
770+
- AbbrCode: 0x2
771+
Values:
772+
- Value: 0x00
773+
- Value: 0x18
774+
775+
# 0x18: DW_TAG_structure_type
776+
# DW_AT_name [DW_FORM_strp] (\"A\")
777+
- AbbrCode: 0x3
778+
Values:
779+
- Value: 0x02
780+
781+
# 0x1d: DW_TAG_LLVM_annotation
782+
# DW_AT_name [DW_FORM_strp] (\"ptrauth_struct_key\")
783+
# DW_AT_const_value [DW_FORM_udata] (2)
784+
- AbbrCode: 0x4
785+
Values:
786+
- Value: 0x04
787+
- Value: 0x02
788+
789+
# 0x23: DW_TAG_LLVM_annotation
790+
# DW_AT_name [DW_FORM_strp] (\"ptrauth_struct_disc\")
791+
# DW_AT_const_value [DW_FORM_udata] (42)
792+
- AbbrCode: 0x4
793+
Values:
794+
- Value: 0x17
795+
- Value: 0x2a
796+
797+
- AbbrCode: 0x0 # end of child tags of 0x18
798+
- AbbrCode: 0x0 # end of child tags of 0x0c
799+
...
800+
)";
801+
YAMLModuleTester t(yamldata);
802+
803+
DWARFUnit *unit = t.GetDwarfUnit();
804+
ASSERT_NE(unit, nullptr);
805+
const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
806+
ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
807+
DWARFDIE cu_die(unit, cu_entry);
808+
809+
auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
810+
auto &ast_ctx = *holder->GetAST();
811+
DWARFASTParserClangStub ast_parser(ast_ctx);
812+
813+
DWARFDIE struct_object = cu_die.GetFirstChild();
814+
ASSERT_EQ(struct_object.Tag(), DW_TAG_variable);
815+
DWARFDIE structure_type =
816+
struct_object.GetAttributeValueAsReferenceDIE(DW_AT_type);
817+
ASSERT_EQ(structure_type.Tag(), DW_TAG_structure_type);
818+
819+
SymbolContext sc;
820+
bool new_type = false;
821+
lldb::TypeSP type =
822+
ast_parser.ParseTypeFromDWARF(sc, structure_type, &new_type);
823+
clang::RecordDecl *record_decl =
824+
TypeSystemClang::GetAsRecordDecl(type->GetForwardCompilerType());
825+
auto [is_key_val_independent, key] =
826+
ast_ctx.getASTContext().getPointerAuthStructKey(record_decl);
827+
auto [is_disc_val_independent, disc] =
828+
ast_ctx.getASTContext().getPointerAuthStructDisc(record_decl);
829+
ASSERT_TRUE(is_key_val_independent);
830+
ASSERT_TRUE(is_disc_val_independent);
831+
ASSERT_EQ(key, 2);
832+
ASSERT_EQ(disc, 42);
833+
}
834+
698835
struct ExtractIntFromFormValueTest : public testing::Test {
699836
SubsystemRAII<FileSystem, HostInfo> subsystems;
700837
clang_utils::TypeSystemClangHolder holder;

0 commit comments

Comments
 (0)