Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AArch64][PAC][lldb] Support PAuth features in user expressions #68

Open
wants to merge 9 commits into
base: elf-pauth
Choose a base branch
from
101 changes: 100 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 @@ -2432,6 +2476,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 Expand Up @@ -3749,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
18 changes: 18 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/IR/AttributeMask.h"
#include "llvm/IR/CallingConv.h"
Expand Down Expand Up @@ -1075,6 +1076,23 @@ void CodeGenModule::Release() {
if (!LangOpts.isSignReturnAddressWithAKey())
getModule().addModuleFlag(llvm::Module::Min,
"sign-return-address-with-bkey", 1);

if (getTriple().isOSLinux() && getTriple().isOSBinFormatELF()) {
uint64_t PAuthABIVersion =
(LangOpts.PointerAuthCalls << 0) |
(LangOpts.PointerAuthReturns << 1) |
(LangOpts.PointerAuthVTPtrAddressDiscrimination << 2) |
(LangOpts.PointerAuthVTPtrTypeDiscrimination << 3) |
(LangOpts.PointerAuthInitFini << 4);
if (PAuthABIVersion != 0) {
getModule().addModuleFlag(
llvm::Module::Error, "aarch64-elf-pauthabi-platform",
llvm::ELF::GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX);
getModule().addModuleFlag(llvm::Module::Error,
"aarch64-elf-pauthabi-version",
PAuthABIVersion);
}
}
}

if (!CodeGenOpts.MemoryProfileOutput.empty()) {
Expand Down
28 changes: 28 additions & 0 deletions clang/test/CodeGen/aarch64-elf-pauthabi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -mbranch-protection=pauthabi %s | FileCheck %s --check-prefix=ALL
// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-returns %s | FileCheck %s --check-prefix=RET
// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls %s | FileCheck %s --check-prefix=CALL
// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls -fptrauth-vtable-pointer-address-discrimination %s | FileCheck %s --check-prefix=VPTRADDR
// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination %s | FileCheck %s --check-prefix=VPTRTYPE
// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls -fptrauth-init-fini %s | FileCheck %s --check-prefix=INITFINI

// REQUIRES: aarch64-registered-target

// ALL: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2}
// ALL: !{i32 1, !"aarch64-elf-pauthabi-version", i32 31}

// RET: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2}
// RET: !{i32 1, !"aarch64-elf-pauthabi-version", i32 2}

// CALL: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2}
// CALL: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1}

// VPTRADDR: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2}
// VPTRADDR: !{i32 1, !"aarch64-elf-pauthabi-version", i32 5}

// VPTRTYPE: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2}
// VPTRTYPE: !{i32 1, !"aarch64-elf-pauthabi-version", i32 9}

// INITFINI: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2}
// INITFINI: !{i32 1, !"aarch64-elf-pauthabi-version", i32 17}

void foo() {}
32 changes: 32 additions & 0 deletions clang/test/CodeGenCXX/ptrauth-struct-attr-annotations.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %clang_cc1 -triple aarch64 -emit-llvm -debug-info-kind=limited -o - %s | FileCheck %s

#include <ptrauth.h>

struct __attribute__((ptrauth_struct(1, 42))) S0 {};

template <int k>
class [[clang::ptrauth_struct(k, 65534)]] S1 {};

union __attribute__((ptrauth_struct(__builtin_ptrauth_struct_key(S0) + 1, __builtin_ptrauth_struct_disc(S0) + 1))) S2 {};

S0 s0;
S1<3> s1;
S2 s2;

// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "S1<3>",
// CHECK-SAME: annotations: ![[A1:.*]])
// CHECK: ![[A1]] = !{![[A1K:.*]], ![[A1D:.*]]}
// CHECK: ![[A1K]] = !{!"ptrauth_struct_key", i2 -1}
// CHECK: ![[A1D]] = !{!"ptrauth_struct_disc", i16 -2}

// CHECK: !DICompositeType(tag: DW_TAG_union_type, name: "S2",
// CHECK-SAME: annotations: ![[A2:.*]])
// CHECK: ![[A2]] = !{![[A2K:.*]], ![[A2D:.*]]}
// CHECK: ![[A2K]] = !{!"ptrauth_struct_key", i2 -2}
// CHECK: ![[A2D]] = !{!"ptrauth_struct_disc", i16 43}

// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "S0",
// CHECK-SAME: annotations: ![[A0:.*]])
// CHECK: ![[A0]] = !{![[A0K:.*]], ![[A0D:.*]]}
// CHECK: ![[A0K]] = !{!"ptrauth_struct_key", i2 1}
// CHECK: ![[A0D]] = !{!"ptrauth_struct_disc", i16 42}
3 changes: 2 additions & 1 deletion lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/PrettyStackTrace.h"
#include <array>
#include <atomic>
#include <memory>
#include <optional>
Expand Down Expand Up @@ -492,7 +493,7 @@ struct Ctx {

llvm::raw_fd_ostream openAuxiliaryFile(llvm::StringRef, std::error_code &);

SmallVector<uint8_t, 0> aarch64PauthAbiTag;
std::optional<std::array<uint8_t, 16>> aarch64PauthAbiTag;
};

LLVM_LIBRARY_VISIBILITY extern Ctx ctx;
Expand Down
20 changes: 10 additions & 10 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2662,17 +2662,17 @@ static void getAarch64PauthInfo() {

auto NonEmptyIt = std::find_if(
ctx.objectFiles.begin(), ctx.objectFiles.end(),
[](const ELFFileBase *f) { return !f->aarch64PauthAbiTag.empty(); });
[](const ELFFileBase *f) { return f->aarch64PauthAbiTag.has_value(); });
if (NonEmptyIt == ctx.objectFiles.end())
return;

ctx.aarch64PauthAbiTag = (*NonEmptyIt)->aarch64PauthAbiTag;
StringRef F1 = (*NonEmptyIt)->getName();
for (ELFFileBase *F : ArrayRef(ctx.objectFiles)) {
StringRef F2 = F->getName();
const SmallVector<uint8_t, 0> &D1 = ctx.aarch64PauthAbiTag;
const SmallVector<uint8_t, 0> &D2 = F->aarch64PauthAbiTag;
if (D1.empty() != D2.empty()) {
std::optional<std::array<uint8_t, 16>> D1 = ctx.aarch64PauthAbiTag;
std::optional<std::array<uint8_t, 16>> D2 = F->aarch64PauthAbiTag;
if (D1.has_value() != D2.has_value()) {
auto Helper = [](StringRef Report, const Twine &Msg) {
if (Report == "warning")
warn(Msg);
Expand All @@ -2681,19 +2681,19 @@ static void getAarch64PauthInfo() {
};

Helper(config->zPauthReport,
(D1.empty() ? F1.str() : F2.str()) +
(D1.has_value() ? F2.str() : F1.str()) +
" has no AArch64 PAuth compatibility info while " +
(D1.empty() ? F2.str() : F1.str()) +
(D1.has_value() ? F1.str() : F2.str()) +
" has one; either all or no input files must have it");
}

if (!D1.empty() && !D2.empty() &&
!std::equal(D1.begin(), D1.end(), D2.begin(), D2.end()))
if (D1.has_value() && D2.has_value() &&
!std::equal(D1->begin(), D1->end(), D2->begin(), D2->end()))
errorOrWarn(
"incompatible values of AArch64 PAuth compatibility info found"
"\n" +
F1 + ": 0x" + toHex(ArrayRef(D1.data(), D1.size())) + "\n" + F2 +
": 0x" + toHex(ArrayRef(D2.data(), D2.size())));
F1 + ": 0x" + toHex(ArrayRef(D1->data(), D1->size())) + "\n" + F2 +
": 0x" + toHex(ArrayRef(D2->data(), D2->size())));
}
}

Expand Down
53 changes: 41 additions & 12 deletions lld/ELF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/raw_ostream.h"

#include <tuple>

using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
Expand Down Expand Up @@ -878,11 +880,14 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
// of zero or more type-length-value fields. We want to find a field of a
// certain type. It seems a bit too much to just store a 32-bit value, perhaps
// the ABI is unnecessarily complicated.
template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
template <class ELFT>
static std::pair<uint32_t, std::optional<std::array<uint8_t, 16>>>
readGnuProperty(const InputSection &sec) {
using Elf_Nhdr = typename ELFT::Nhdr;
using Elf_Note = typename ELFT::Note;

uint32_t featuresSet = 0;
std::optional<std::array<uint8_t, 16>> aarch64PauthAbiTag;
ArrayRef<uint8_t> data = sec.content();
auto reportFatal = [&](const uint8_t *place, const char *msg) {
fatal(toString(sec.file) + ":(" + sec.name + "+0x" +
Expand Down Expand Up @@ -924,6 +929,19 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
if (size < 4)
reportFatal(place, "FEATURE_1_AND entry is too short");
featuresSet |= read32<ELFT::TargetEndianness>(desc.data());
} else if (config->emachine == EM_AARCH64 &&
type == GNU_PROPERTY_AARCH64_FEATURE_PAUTH) {
if (aarch64PauthAbiTag != std::nullopt)
reportFatal(data.data(),
"multiple GNU_PROPERTY_AARCH64_FEATURE_PAUTH properties "
"are not allowed");
if (size != 16)
reportFatal(
data.data(),
"size of GNU_PROPERTY_AARCH64_FEATURE_PAUTH property must be 16");
aarch64PauthAbiTag = std::array<uint8_t, 16>{};
memcpy(aarch64PauthAbiTag->data(), desc.data(),
aarch64PauthAbiTag->size());
}

// Padding is present in the note descriptor, if necessary.
Expand All @@ -934,7 +952,7 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
data = data.slice(nhdr->getSize(sec.addralign));
}

return featuresSet;
return {featuresSet, aarch64PauthAbiTag};
}

// Extract compatibility info for aarch64 pointer authentication from the
Expand Down Expand Up @@ -966,13 +984,15 @@ static void readAArch64PauthAbiTag(const InputSection &sec, ObjFile<ELFT> &f) {
" (ARM expected)");

ArrayRef<uint8_t> desc = note.getDesc(sec.addralign);
if (desc.size() < 16) {
reportError("too short AArch64 PAuth compatibility info "
"(at least 16 bytes expected)");
if (desc.size() != 16) {
reportError("invalid AArch64 PAuth compatibility info length "
"(exactly 16 bytes expected)");
return;
}

f.aarch64PauthAbiTag = SmallVector<uint8_t, 0>(iterator_range(desc));
f.aarch64PauthAbiTag = std::array<uint8_t, 16>{};
memcpy(f.aarch64PauthAbiTag->data(), desc.data(),
f.aarch64PauthAbiTag->size());
}

template <class ELFT>
Expand Down Expand Up @@ -1029,15 +1049,24 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
// .note.gnu.property containing a single AND'ed bitmap, we discard an input
// file's .note.gnu.property section.
if (name == ".note.gnu.property") {
this->andFeatures = readAndFeatures<ELFT>(InputSection(*this, sec, name));
std::tie(this->andFeatures, this->aarch64PauthAbiTag) =
readGnuProperty<ELFT>(InputSection(*this, sec, name));
return &InputSection::discarded;
}

if (config->emachine == EM_AARCH64 &&
name == ".note.AARCH64-PAUTH-ABI-tag") {
readAArch64PauthAbiTag<ELFT>(InputSection(*this, sec, name), *this);
return &InputSection::discarded;
}
// TODO: alternative PAuth ELF marking way
// To be implemented after the following PRs are merged:
// - https://github.com/ARM-software/abi-aa/pull/240
// - https://github.com/llvm/llvm-project/pull/72714
//
// if (config->emachine == EM_AARCH64 &&
// name == ".note.AARCH64-PAUTH-ABI-tag") {
// if (this->aarch64PauthAbiTag != std::nullopt)
// error("cannot mix two PAuth ABI tag ELF marking ways in one object
// file");
// readAArch64PauthAbiTag<ELFT>(InputSection(*this, sec, name), *this);
// return &InputSection::discarded;
// }

// Split stacks is a feature to support a discontiguous stack,
// commonly used in the programming language Go. For the details,
Expand Down
Loading