Skip to content

Commit

Permalink
[AArch64][PAC][lldb][Dwarf] Support [[clang::ptrauth_struct(...)]]
Browse files Browse the repository at this point in the history
…attribute
  • Loading branch information
kovdan01 committed Feb 15, 2024
1 parent a567cae commit ee59834
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 1 deletion.
60 changes: 59 additions & 1 deletion clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2304,6 +2304,50 @@ llvm::DINodeArray CGDebugInfo::CollectBTFDeclTagAnnotations(const Decl *D) {
return DBuilder.getOrCreateArray(Annotations);
}

llvm::DINodeArray
CGDebugInfo::CollectPtrAuthStructAnnotations(const RecordDecl *RD) {
if (!RD->hasAttr<PointerAuthStructAttr>())
return nullptr;

llvm::LLVMContext &Context = CGM.getLLVMContext();

auto [IsKeyValIndependent, Key] =
CGM.getContext().getPointerAuthStructKey(RD);
auto [IsDiscValIndependent, Disc] =
CGM.getContext().getPointerAuthStructDisc(RD);

// TODO: handle value-dependent keys and discriminators. What does this even
// mean? Using template integer parameters as key/disc or expressions like
// `__builtin_ptrauth_struct_key(...)` result in specific constants, and does
// not make key/disc value dependent.
if (!IsKeyValIndependent || !IsDiscValIndependent)
return nullptr;

// TODO: 2-bit key and 16-bit disc results in negative values of i2 and i16
// types in IR for unsigned integers using all the given bit-width (for ex.,
// key 3 is i8 -1, disc 65534 is i16 -2). Do we want to use wider types to
// prevent negation?
auto *KeyMD = llvm::ConstantAsMetadata::get(
llvm::ConstantInt::get(Context, llvm::APInt(2, Key, false)));
auto *DiscMD = llvm::ConstantAsMetadata::get(
llvm::ConstantInt::get(Context, llvm::APInt(16, Disc, false)));

llvm::Metadata *Annotations[2];
llvm::Metadata *OpsKey[2] = {
llvm::MDString::get(Context, StringRef("ptrauth_struct_key")), KeyMD};
Annotations[0] = llvm::MDNode::get(CGM.getLLVMContext(), OpsKey);
llvm::Metadata *OpsDisc[2] = {
llvm::MDString::get(Context, StringRef("ptrauth_struct_disc")), DiscMD};
Annotations[1] = llvm::MDNode::get(CGM.getLLVMContext(), OpsDisc);

// TODO: is it OK to return DINodeArray instead of MDNodeArray from
// CollectPtrAuthStructAnnotations and CollectBTFDeclTagAnnotations? The
// elements are not actually DINodes, and trying to iterate over the returned
// DINodeArray results in an assertion failure. This does not seem to happen
// though - it looks like that there are no places where such loops are used.
return DBuilder.getOrCreateArray(Annotations);
}

llvm::DIType *CGDebugInfo::getOrCreateVTablePtrType(llvm::DIFile *Unit) {
if (VTablePtrType)
return VTablePtrType;
Expand Down Expand Up @@ -3790,7 +3834,21 @@ llvm::DICompositeType *CGDebugInfo::CreateLimitedType(const RecordType *Ty) {
dyn_cast<CXXRecordDecl>(CXXRD->getDeclContext()));
}

llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(D);
llvm::DINodeArray AnnotationsBTFTag = CollectBTFDeclTagAnnotations(D);
llvm::DINodeArray AnnotationsPAuthStruct = CollectPtrAuthStructAnnotations(D);

llvm::SmallVector<llvm::Metadata *, 4> AnnotationsVec;
if (AnnotationsBTFTag.get() != nullptr)
for (const llvm::MDOperand &Op : AnnotationsBTFTag.get()->operands())
AnnotationsVec.push_back(Op.get());
if (AnnotationsPAuthStruct.get() != nullptr)
for (const llvm::MDOperand &Op : AnnotationsPAuthStruct.get()->operands())
AnnotationsVec.push_back(Op.get());

llvm::DINodeArray Annotations =
AnnotationsVec.empty() ? nullptr
: DBuilder.getOrCreateArray(AnnotationsVec);

llvm::DICompositeType *RealDecl = DBuilder.createReplaceableCompositeType(
getTagForRecord(RD), RDName, RDContext, DefUnit, Line, 0, Size, Align,
Flags, Identifier, Annotations);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,9 @@ class CGDebugInfo {
/// A helper function to collect debug info for btf_decl_tag annotations.
llvm::DINodeArray CollectBTFDeclTagAnnotations(const Decl *D);

/// A helper function to collect debug info for ptrauth_struct attribute.
llvm::DINodeArray CollectPtrAuthStructAnnotations(const RecordDecl *RD);

llvm::DIType *createFieldType(StringRef name, QualType type,
SourceLocation loc, AccessSpecifier AS,
uint64_t offsetInBits, uint32_t AlignInBits,
Expand Down
33 changes: 33 additions & 0 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1884,6 +1884,39 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc,
discriminator));
}
}

// TODO: check that each annotation does not appear more than once
std::optional<uint64_t> pauth_key, pauth_disc;
for (DWARFDIE die_child = die.GetFirstChild(); die_child;
die_child = die_child.GetSibling()) {
if (die_child.Tag() != DW_TAG_LLVM_annotation)
continue;
if (die_child.GetAttributeValueAsString(DW_AT_name, nullptr) ==
llvm::StringRef("ptrauth_struct_key"))
pauth_key =
die_child.GetAttributeValueAsOptionalUnsigned(DW_AT_const_value);
else if (die_child.GetAttributeValueAsString(DW_AT_name, nullptr) ==
llvm::StringRef("ptrauth_struct_disc"))
pauth_disc =
die_child.GetAttributeValueAsOptionalUnsigned(DW_AT_const_value);
}

// TODO: do we need some handling for one std::nullopt out of two?
if (pauth_key != std::nullopt && pauth_disc != std::nullopt) {
clang::ASTContext &ctx = m_ast.getASTContext();
llvm::APInt key_int = llvm::APInt(ctx.getTypeSize(ctx.UnsignedIntTy),
pauth_key.value(), false);
auto *key = clang::IntegerLiteral::Create(ctx, key_int, ctx.UnsignedIntTy,
clang::SourceLocation());
llvm::APInt disc_int = llvm::APInt(ctx.getTypeSize(ctx.UnsignedIntTy),
pauth_disc.value(), false);
auto *disc = clang::IntegerLiteral::Create(
ctx, disc_int, ctx.UnsignedIntTy, clang::SourceLocation());

clang::RecordDecl *record_decl = m_ast.GetAsRecordDecl(clang_type);
record_decl->addAttr(
clang::PointerAuthStructAttr::CreateImplicit(ctx, key, disc));
}
}

// Store a forward declaration to this class type in case any
Expand Down
137 changes: 137 additions & 0 deletions lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,143 @@ TEST_F(DWARFASTParserClangTests, TestVTablePtrAuthParsing) {
ASSERT_EQ(attr->getCustomDiscriminationValue(), 42);
}

TEST_F(DWARFASTParserClangTests, TestPtrAuthStructAttr) {
// Tests parsing types with ptrauth_struct attribute
// authentication

// This is Dwarf for the following C code:
// ```
// struct [[clang::ptrauth_struct(2, 42)]] A {};
// struct A a;
// ```

const char *yamldata = R"(
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_AARCH64
DWARF:
debug_str:
- a
- A
- ptrauth_struct_key
- ptrauth_struct_disc
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_name
Form: DW_FORM_strp
- Code: 0x4
Tag: DW_TAG_LLVM_annotation
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- Attribute: DW_AT_const_value
Form: DW_FORM_udata
debug_info:
- Version: 5
UnitType: DW_UT_compile
AddrSize: 8
Entries:
# 0x0c: DW_TAG_compile_unit
# DW_AT_language [DW_FORM_data2] (DW_LANG_C99)
- AbbrCode: 0x1
Values:
- Value: 0x0c
# 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_name [DW_FORM_strp] (\"A\")
- AbbrCode: 0x3
Values:
- Value: 0x02
# 0x1d: DW_TAG_LLVM_annotation
# DW_AT_name [DW_FORM_strp] (\"ptrauth_struct_key\")
# DW_AT_const_value [DW_FORM_udata] (2)
- AbbrCode: 0x4
Values:
- Value: 0x04
- Value: 0x02
# 0x23: DW_TAG_LLVM_annotation
# DW_AT_name [DW_FORM_strp] (\"ptrauth_struct_disc\")
# DW_AT_const_value [DW_FORM_udata] (42)
- AbbrCode: 0x4
Values:
- Value: 0x17
- Value: 0x2a
- AbbrCode: 0x0 # end of child tags of 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 [is_key_val_independent, key] =
ast_ctx.getASTContext().getPointerAuthStructKey(record_decl);
auto [is_disc_val_independent, disc] =
ast_ctx.getASTContext().getPointerAuthStructDisc(record_decl);
ASSERT_TRUE(is_key_val_independent);
ASSERT_TRUE(is_disc_val_independent);
ASSERT_EQ(key, 2);
ASSERT_EQ(disc, 42);
}

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

0 comments on commit ee59834

Please sign in to comment.